• Stars
    star
    347
  • Rank 117,780 (Top 3 %)
  • Language
    Ruby
  • License
    MIT License
  • Created almost 12 years ago
  • Updated almost 4 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A set of Rack middleware and cache helpers that implement various caching strategies.

Garner

Gem Version Build Status Dependency Status Code Climate Coverage Status

Garner is a cache layer for Ruby and Rack applications, supporting model and instance binding and hierarchical invalidation. To "garner" means to gather data from various sources and to make it readily available in one place, kind of like a cache!

If you're not familiar with HTTP caching, ETags and If-Modified-Since, watch us introduce Garner in From Zero to API Cache in 10 Minutes at GoRuCo 2012.

Upgrading

The current stable release line of Garner is 0.5.x, and contains many breaking changes from the previous 0.3.x series. For a summary of important changes, see UPGRADING.

Usage

Application Logic Caching

Add Garner to your Gemfile with gem "garner" and run bundle install. Next, include the appropriate mixin in your app:

  • For plain-old Ruby apps, include Garner::Cache::Context.
  • For Rack apps, first require "garner/mixins/rack", then include Garner::Mixins::Rack. (This provides saner defaults for injecting request parameters into the cache context key. More on cache context keys later.)

Now, to use Garner's cache, invoke garner with a logic block from within your application. The result of the block will be computed once, and then stored in the cache.

get "/system/counts/all" do
  # Compute once and cache for subsequent reads
  garner do
    {
      "orders_count" => Order.count,
      "users_count"  => User.count
    }
  end
end

The cached value can be bound to a particular model instance. For example, if a user has an address that may or may not change when the user is saved, you will want the cached address to be invalidated every time the user record is modified.

get "/me/address" do
  # Invalidate when current_user is modified
  garner.bind(current_user) do
    current_user.address
  end
end

ORM Integrations

Mongoid

To use Mongoid 3, 4 or 5 documents and classes for Garner bindings, use Garner::Mixins::Mongoid::Document. You can set it up in an initializer:

require "garner/mixins/mongoid"

module Mongoid
  module Document
    include Garner::Mixins::Mongoid::Document
  end
end

This enables binding to Mongoid classes as well as instances. For example:

get "/system/counts/orders" do
  # Invalidate when any order is created, updated or deleted
  garner.bind(Order) do
    {
      "orders_count" => Order.count,
    }
  end
end

What if you want to bind a cache result to a persisted object that hasn't been retrieved yet? Consider the example of caching a particular order without a database query:

get "/order/:id" do
  # Invalidate when Order.find(params[:id]) is modified
  garner.bind(Order.identify(params[:id])) do
    Order.find(params[:id])
  end
end

In the above example, the Order.identify call will not result in a database query. Instead, it just communicates to Garner's cache sweeper that whenever the order with identity params[:id] is updated, this cache result should be invalidated. The identify method is provided by the Mongoid mixin. To use it, you should configure Garner.config.mongoid_identity_fields, e.g.:

Garner.configure do |config|
  config.mongoid_identity_fields = [:_id, :_slugs]
end

These may be scalar or array fields. Only uniquely-constrained fields should be used here; otherwise you risk caching the same result for two different blocks.

The Mongoid mixin also provides helper methods for cached find operations. The following code will fetch an order once (via find) from the database, and then fetch it from the cache on subsequent requests. The cache will be invalidated whenever the underlying Order changes in the database.

order = Order.garnered_find(3)

Explicit invalidation should be unnecessary, since callbacks are declared to invalidate the cache whenever a Mongoid object is created, updated or destroyed, but for special cases, invalidate_garner_caches may be called on a Mongoid object or class:

Order.invalidate_garner_caches
Order.find(3).invalidate_garner_caches

ActiveRecord

Garner provides rudimentary support for ActiveRecord. To use ActiveRecord models for Garner bindings, use Garner::Mixins::ActiveRecord::Base. You can set it up in an initializer:

require "garner/mixins/active_record"

module ActiveRecord
  class Base
    include Garner::Mixins::ActiveRecord::Base
  end
end

Cache Options

You can pass additional options directly to the cache implementation:

get "/latest_order" do
  # Expire the latest order every 15 minutes
  garner.options(expires_in: 15.minutes) do
    Order.latest
  end
end

Under The Hood: Bindings

As we've seen, a cache result can be bound to a model instance (e.g., current_user) or a virtual instance reference (Order.identify(params[:id])). In some cases, we may want to compose bindings:

get "/system/counts/all" do
  # Invalidate when any order or user is modified
  garner.bind(Order).bind(User) do
    {
      "orders_count" => Order.count,
      "users_count"  => User.count
    }
  end
end

Binding keys are computed via pluggable strategies, as are the rules for invalidating caches when a binding changes. By default, Garner uses Garner::Strategies::Binding::Key::SafeCacheKey to compute binding keys: this uses cache_key if defined on an object; otherwise it always bypasses cache. Similarly, Garner uses Garner::Strategies::Binding::Invalidation::Touch as its default invalidation strategy. This will call :touch on a document if it is defined; otherwise it will take no action.

Additional binding and invalidation strategies can be written. To use them, set Garner.config.binding_key_strategy and Garner.config.binding_invalidation_strategy.

Under The Hood: Cache Context Keys

Explicit cache context keys are usually unnecessary in Garner. Given a cache binding, Garner will compute an appropriately unique cache key. Moreover, in the context of Garner::Mixins::Rack, Garner will compose the following key factors by default:

  • Garner::Strategies::Context::Key::Caller inserts the calling file and line number, allowing multiple calls from the same function to generate different results.
  • Garner::Strategies::Context::Key::RequestGet inserts the value of HTTP request's GET parameters into the cache key when :request is present in the context.
  • Garner::Strategies::Context::Key::RequestPost inserts the value of HTTP request's POST parameters into the cache key when :request is present in the context.
  • Garner::Strategies::Context::Key::RequestPath inserts the value of the HTTP request's path into the cache key when :request is present in the context.

Additional key factors may be specified explicitly using the key method. To see a specific example of this in action, let's consider the case of role-based caching. For example, an order may have a different representation for an admin versus an ordinary user:

get "/order/:id" do
  garner.bind(Order.identify(params[:id])).key({ role: current_user.role }) do
    Order.find(params[:id])
  end
end

As with bindings, context key factors may be composed by calling key() multiple times on a garner invocation. The keys will be applied in the order in which they are called.

Configuration

By default Garner will use an instance of ActiveSupport::Cache::MemoryStore in a non-Rails and Rails.cache in a Rails environment. You can configure it to use any other cache store.

Garner.configure do |config|
  config.cache = ActiveSupport::Cache::FileStore.new
end

The full list of Garner.config attributes is:

  • :global_cache_options: A hash of options to be passed on every call to Garner.config.cache, like { :expires_in => 10.minutes }. Defaults to {}
  • :context_key_strategies: An array of context key strategies, to be applied in order. Defaults to [Garner::Strategies::Context::Key::Caller]
  • :rack_context_key_strategies: Rack-specific context key strategies. Defaults to:
[
  Garner::Strategies::Context::Key::Caller,
  Garner::Strategies::Context::Key::RequestGet,
  Garner::Strategies::Context::Key::RequestPost,
  Garner::Strategies::Context::Key::RequestPath
]
  • :binding_key_strategy: Binding key strategy. Defaults to Garner::Strategies::Binding::Key::SafeCacheKey.
  • :binding_invalidation_strategy: Binding invalidation strategy. Defaults to Garner::Strategies::Binding::Invalidation::Touch.
  • :mongoid_identity_fields: Identity fields considered legal for the identity method. Defaults to [:_id].
  • :caller_root: Root path of application, to be stripped out of value strings generated by the Caller context key strategy. Defaults to Rails.root if in a Rails environment; otherwise to the nearest ancestor directory containing a Gemfile.
  • :invalidate_mongoid_root: If set to true, invalidates the _root document along with any embedded Mongoid document binding. Defaults to true.
  • :whiny_nils: If set to true, raises an exception when a nil binding is specified (i.e., garner.bind(nil)). Defaults to true.

Contributing

Fork the project. Make your feature addition or bug fix with tests. Send a pull request.

Copyright and License

MIT License, see LICENSE for details.

(c) 2012-2013 Artsy, Frank Macreery, Daniel Doubrovkine and contributors.

More Repositories

1

eigen

The Art World in Your Pocket or Your Trendy Tech Company's Tote, Artsy's mobile app.
TypeScript
3,430
star
2

eidolon

The Artsy Auction Kiosk App.
Swift
2,711
star
3

fresnel

An SSR compatible approach to CSS media query based responsive layouts for React.
TypeScript
1,098
star
4

README

๐Ÿ‘‹ - The documentation for being an Artsy Engineer
TypeScript
1,044
star
5

artsy.github.io

The Artsy Engineering Open-Source Developers Blog
SCSS
1,015
star
6

emission

โš ๏ธ Deprecated repo, moved to artsy/eigen โžก๏ธ React Native Components
TypeScript
621
star
7

force

The Artsy.net website
TypeScript
550
star
8

metaphysics

Artsy's GraphQL API
TypeScript
355
star
9

reaction

Artsy's publishing components
TypeScript
354
star
10

Emergence

TV. Shows.
Swift
353
star
11

scroll-frame

Retain your scroll position between pages using an iframe. Especially helpful for infinite scrolling views.
JavaScript
312
star
12

ezel

A boilerplate for Backbone projects that share code server/client, render server/client, and scale through modular architecture.
JavaScript
294
star
13

Swift-at-Artsy

Repo for the notes for Swift at Artsy
Swift
284
star
14

energy-legacy

LEGACY - Artsy Folio, The Partner iPhone / iPad app.
Objective-C
209
star
15

palette

Artsy's design system
TypeScript
203
star
16

react-redux-controller

Library for creating a controller layer to link React and Redux, on top of react-redux.
JavaScript
97
star
17

hokusai

Artsy's Docker / Kubernetes CLI and Workflow
Python
87
star
18

positron

Positron is Artsy Writer or the editorial tool and API for Artsy.
TypeScript
85
star
19

day-schedule-selector

A jQuery plugin to render a weekly schedule and allow selecting time slots in each day.
JavaScript
85
star
20

mobile

Mobile Team TODO
84
star
21

benv

Stub a browser environment in node.js and headlessly test your client-side code.
JavaScript
72
star
22

flare

Artsy iPhone Launch Marketing Page
CoffeeScript
63
star
23

team-navigator

An internal HR product for Artsy's team
JavaScript
62
star
24

sharify

Easily share data between Browserify modules meant to run on the server and client.
JavaScript
61
star
25

x-react-native

Conference Details for Artsy x React Native
TypeScript
49
star
26

backbone-super-sync

Isomorphic Backbone.sync adapter using superagent.
JavaScript
47
star
27

jenkins-backup-s3

A collection of scripts to backup Jenkins configuration to S3, as well as manage and restore those backups
Python
46
star
28

graphql-slack-updater

A weekly Travis task that sends our GraphQL updates to Slack
Ruby
40
star
29

meta

Artsy on Artsy.
39
star
30

Specs

The Artsy CocoaPods Specs
Ruby
37
star
31

doppler

Artsy.net developer website.
Ruby
37
star
32

detect-responsive-traits

Determine responsive traits to only server-side render markup truly needed.
TypeScript
36
star
33

gris

Gris is a framework for building hypermedia API services using Grape, Roar and ActiveRecord
Ruby
35
star
34

artsy-2013

The 2013.artsy.net static site using Node.js for some preprocessors.
CoffeeScript
33
star
35

the-art-genome-project

Gene names and definitions
JavaScript
33
star
36

elderfield

The Artsy Alexa (Echo) skill.
JavaScript
33
star
37

peril-settings

Artsy's peril settings
TypeScript
30
star
38

2014.artsy.net

CoffeeScript
25
star
39

artsy-ruby-client

Artsy API Ruby Client
Ruby
25
star
40

express-reloadable

Automatically hot-swap Express server code without the restart
JavaScript
25
star
41

Artsy-Authentication

Cocoa libraries dealing with Authentication for Artsy. Yawn, boring
Objective-C
22
star
42

dupe-report

A tool for reporting new webpack bundle duplicates to github and slack
TypeScript
21
star
43

Artsy-UILabels

This is our Artsy styled UILabel subclasses.
Objective-C
20
star
44

Artsy-OSSUIFonts

Open source variants of the Artsy fonts, wrapped as a CocoaPod
Beef
18
star
45

Mitosis

Artsy Chat Bot for Facebook Messenger
JavaScript
17
star
46

atomic-store

Atomic event store for Scala/Akka
Scala
17
star
47

momentum

Shared utilities for managing and deploying OpsWorks apps at Artsy. [DEPRECATED]
Ruby
15
star
48

orbs

CircleCI orbs used at Artsy
Shell
15
star
49

lint-changed

Lint files that have changed since master
TypeScript
14
star
50

artsy-eigen-web-association

A tiny app that serves the apple-app-site-association required for iOS Handoff related features.
JavaScript
14
star
51

javascriptures

Demo projects for javascripture sessions
JavaScript
14
star
52

estella

Make your Ruby objects searchable with Elasticsearch.
Ruby
13
star
53

echo

Eigen's floating administration lab up in the clouds
Shell
13
star
54

graphql-workshop

A workshop for understanding GraphQL by exploring Metaphysics
13
star
55

emission-nebula

Handles weekly deploys of Emission to TestFlight
Ruby
12
star
56

Artsy-UIColors

Consolidated UIColors used in the Artsy iOS Apps
Ruby
12
star
57

cohesion

Artsy's analytics schema
TypeScript
12
star
58

yeoman-generator-artsy

A Yeoman generate for Artsy CLI apps
JavaScript
11
star
59

renovate-config

Shared renovate configurations
JavaScript
11
star
60

stitch

Helps your Component and Template dependencies peacefully coexist
JavaScript
11
star
61

vscode-artsy

A VS Code extension for the Artsy Tech Stacks
11
star
62

money_helper

A simple module to assist in formatting unambiguous prices and price ranges in international currencies
Ruby
11
star
63

aprb

[Deprecated] Artsy public radio notifications in Slack.
Elixir
10
star
64

artsy-passport

Wires up the common auth handlers for Artsy's node based apps using http://passportjs.org.
JavaScript
10
star
65

next

Tools an utilites for Next.js by Artsy
JavaScript
10
star
66

eigen-artefacts

CocoaPods dependencies used in Eigen.
C++
10
star
67

Artsy-UIButtons

Artsy's UIButton subclasses
Objective-C
10
star
68

codemods

Various codemods used around Artsy
TypeScript
10
star
69

relay-workshop

Home for Artsy's Relay peer learning tutorials
TypeScript
9
star
70

studio

TypeScript
9
star
71

palette-zeplin-extension

See Artsy's palette components inside Zeplin directly
JavaScript
9
star
72

artsy.github.io-gatsby

re-write Engineering Blog
TypeScript
8
star
73

biesenbach

The Artsy Google Home application.
JavaScript
8
star
74

microgravity-deprecated

The Artsy.net mobile website:
CoffeeScript
8
star
75

palette-mobile

Artsy's Design System on Mobile
TypeScript
8
star
76

Aerodramus

An Objective-C API for interacting with artsy/echo
Objective-C
8
star
77

aprd

Artsy's Real-time Slack Notification Service (aka Artsy Public Radio)
Elixir
8
star
78

artsy-backbone-mixins

A library of Backbone mixins that DRY up some common domain logic and Artsy API rabbit holes.
JavaScript
8
star
79

energy

Artsy Folio, The Partner iPhone / iPad app.
TypeScript
7
star
80

backbone-cache-sync

Server-side Backbone.sync adapter that caches requests using Redis.
JavaScript
7
star
81

kaws

API for collections, our SEO optimized marketing landing pages
TypeScript
7
star
82

horizon

Visual representations of release pipelines
Ruby
7
star
83

APR

Artsy Private Radio
Elixir
7
star
84

guides

The Engineers Guide to the Artsy Universe
7
star
85

forque

A home for modern admin UIs; successor to Torque
TypeScript
7
star
86

hack-playdate-gallery

Lua
7
star
87

browserify-dev-middleware

Middleware to compile browserify files on request for development purpose.
JavaScript
7
star
88

artsy-xapp

Tiny lib to fetch and refresh an xapp token from Artsy
JavaScript
7
star
89

auto-config

Artsy's shared auto release config
6
star
90

volley

Datadog StatsD ingestion for client-side data
TypeScript
6
star
91

rosalind

Admin app for batch operations on genomes
JavaScript
6
star
92

bucket-assets

Uploads a folder of static assets to an s3 bucket with convenient defaults.
JavaScript
6
star
93

extraction

UI components shared between Eigen and Emission.
Objective-C
6
star
94

to-title-case

Capitalizes your titles as per The Chicago Manual of Style
TypeScript
6
star
95

miasma

Smoke tests for Artsy, and possible successor to Vapor
JavaScript
6
star
96

update-repo

JS library for updating other repositories
TypeScript
6
star
97

cli

TypeScript
6
star
98

convection

Rails application for consignments
Ruby
5
star
99

performance-monitor

TypeScript
5
star
100

graphql-page_cursors

Add page cursors to your Rails GQL schema
Ruby
5
star