garner 0.4.5 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +35 -0
- data/.travis.yml +13 -0
- data/CHANGELOG.md +130 -0
- data/CONTRIBUTING.md +118 -0
- data/Gemfile +3 -0
- data/README.md +1 -0
- data/Rakefile +39 -0
- data/UPGRADING.md +118 -0
- data/garner.gemspec +44 -0
- data/lib/garner.rb +21 -21
- data/lib/garner/cache.rb +13 -6
- data/lib/garner/cache/binding.rb +6 -14
- data/lib/garner/cache/context.rb +11 -12
- data/lib/garner/cache/identity.rb +1 -1
- data/lib/garner/config.rb +12 -7
- data/lib/garner/mixins/active_record.rb +3 -3
- data/lib/garner/mixins/active_record/base.rb +2 -2
- data/lib/garner/mixins/mongoid.rb +4 -4
- data/lib/garner/mixins/mongoid/document.rb +8 -12
- data/lib/garner/mixins/mongoid/identity.rb +5 -6
- data/lib/garner/mixins/rack.rb +1 -2
- data/lib/garner/strategies/binding/invalidation/base.rb +2 -4
- data/lib/garner/strategies/binding/invalidation/binding_index.rb +1 -3
- data/lib/garner/strategies/binding/invalidation/touch.rb +0 -2
- data/lib/garner/strategies/binding/key/base.rb +1 -3
- data/lib/garner/strategies/binding/key/binding_index.rb +3 -4
- data/lib/garner/strategies/binding/key/cache_key.rb +0 -2
- data/lib/garner/strategies/binding/key/safe_cache_key.rb +2 -3
- data/lib/garner/strategies/context/key/base.rb +1 -3
- data/lib/garner/strategies/context/key/caller.rb +9 -12
- data/lib/garner/strategies/context/key/jsonp.rb +3 -6
- data/lib/garner/strategies/context/key/request_get.rb +2 -4
- data/lib/garner/strategies/context/key/request_path.rb +1 -3
- data/lib/garner/strategies/context/key/request_post.rb +2 -4
- data/lib/garner/version.rb +1 -1
- data/spec/garner/cache/context_spec.rb +38 -0
- data/spec/garner/cache/identity_spec.rb +68 -0
- data/spec/garner/cache_spec.rb +49 -0
- data/spec/garner/config_spec.rb +17 -0
- data/spec/garner/mixins/mongoid/document_spec.rb +80 -0
- data/spec/garner/mixins/mongoid/identity_spec.rb +140 -0
- data/spec/garner/mixins/rack_spec.rb +48 -0
- data/spec/garner/strategies/binding/invalidation/binding_index_spec.rb +14 -0
- data/spec/garner/strategies/binding/invalidation/touch_spec.rb +23 -0
- data/spec/garner/strategies/binding/key/binding_index_spec.rb +245 -0
- data/spec/garner/strategies/binding/key/cache_key_spec.rb +29 -0
- data/spec/garner/strategies/binding/key/safe_cache_key_spec.rb +61 -0
- data/spec/garner/strategies/context/key/caller_spec.rb +106 -0
- data/spec/garner/strategies/context/key/jsonp_spec.rb +22 -0
- data/spec/garner/strategies/context/key/request_get_spec.rb +33 -0
- data/spec/garner/strategies/context/key/request_path_spec.rb +28 -0
- data/spec/garner/strategies/context/key/request_post_spec.rb +34 -0
- data/spec/garner/version_spec.rb +11 -0
- data/spec/integration/active_record_spec.rb +43 -0
- data/spec/integration/grape_spec.rb +33 -0
- data/spec/integration/mongoid_spec.rb +355 -0
- data/spec/integration/rack_spec.rb +77 -0
- data/spec/integration/sinatra_spec.rb +29 -0
- data/spec/performance/strategy_benchmark.rb +59 -0
- data/spec/performance/support/benchmark_context.rb +31 -0
- data/spec/performance/support/benchmark_context_wrapper.rb +67 -0
- data/spec/shared/binding_invalidation_strategy.rb +17 -0
- data/spec/shared/binding_key_strategy.rb +35 -0
- data/spec/shared/conditional_get.rb +48 -0
- data/spec/shared/context_key_strategy.rb +24 -0
- data/spec/spec_helper.rb +24 -0
- data/spec/spec_support.rb +5 -0
- data/spec/support/active_record.rb +36 -0
- data/spec/support/cache.rb +15 -0
- data/spec/support/garner.rb +5 -0
- data/spec/support/mongoid.rb +71 -0
- metadata +155 -157
data/garner.gemspec
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
$LOAD_PATH.push File.expand_path('../lib', __FILE__)
|
2
|
+
require 'garner/version'
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = 'garner'
|
6
|
+
s.version = Garner::VERSION
|
7
|
+
s.authors = ['Daniel Doubrovkine', 'Frank Macreery']
|
8
|
+
s.summary = 'Garner is a cache layer for Ruby and Rack applications, supporting model and instance binding and hierarchical invalidation.'
|
9
|
+
s.email = ['dblock@dblock.org', 'frank.macreery@gmail.com']
|
10
|
+
s.homepage = 'https://github.com/artsy/garner'
|
11
|
+
s.license = 'MIT'
|
12
|
+
|
13
|
+
s.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
14
|
+
s.test_files = s.files.grep(/^(spec)\//)
|
15
|
+
s.extra_rdoc_files = Dir['*.md']
|
16
|
+
s.require_paths = ['lib']
|
17
|
+
|
18
|
+
s.post_install_message = File.read('UPGRADING') if File.exist?('UPGRADING')
|
19
|
+
|
20
|
+
s.add_runtime_dependency 'rack'
|
21
|
+
s.add_runtime_dependency 'json'
|
22
|
+
s.add_runtime_dependency 'multi_json', '>= 1.3.0'
|
23
|
+
s.add_runtime_dependency 'activesupport'
|
24
|
+
|
25
|
+
s.add_development_dependency 'rake'
|
26
|
+
s.add_development_dependency 'rspec', '~> 2.10'
|
27
|
+
s.add_development_dependency 'bundler'
|
28
|
+
s.add_development_dependency 'grape', '~> 0.8.0'
|
29
|
+
s.add_development_dependency 'sinatra'
|
30
|
+
s.add_development_dependency 'rack-test'
|
31
|
+
s.add_development_dependency 'mongoid', '>= 3.0.0'
|
32
|
+
s.add_development_dependency 'mongoid_slug'
|
33
|
+
s.add_development_dependency 'activerecord'
|
34
|
+
if RUBY_PLATFORM =~ /java/
|
35
|
+
s.add_development_dependency 'jdbc-sqlite3'
|
36
|
+
s.add_development_dependency 'activerecord-jdbcsqlite3-adapter'
|
37
|
+
else
|
38
|
+
s.add_development_dependency 'sqlite3'
|
39
|
+
end
|
40
|
+
s.add_development_dependency 'coveralls'
|
41
|
+
s.add_development_dependency 'yard'
|
42
|
+
s.add_development_dependency 'dalli'
|
43
|
+
s.add_development_dependency 'rubocop', '0.24.1'
|
44
|
+
end
|
data/lib/garner.rb
CHANGED
@@ -1,31 +1,31 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'multi_json'
|
2
|
+
require 'active_support'
|
3
3
|
|
4
4
|
# Garner core
|
5
|
-
require
|
6
|
-
require
|
5
|
+
require 'garner/version'
|
6
|
+
require 'garner/config'
|
7
7
|
|
8
8
|
# Key strategies
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
13
|
-
require
|
14
|
-
require
|
9
|
+
require 'garner/strategies/context/key/base'
|
10
|
+
require 'garner/strategies/context/key/caller'
|
11
|
+
require 'garner/strategies/context/key/request_path'
|
12
|
+
require 'garner/strategies/context/key/request_get'
|
13
|
+
require 'garner/strategies/context/key/request_post'
|
14
|
+
require 'garner/strategies/context/key/jsonp'
|
15
15
|
|
16
16
|
# Binding strategies
|
17
|
-
require
|
18
|
-
require
|
19
|
-
require
|
20
|
-
require
|
17
|
+
require 'garner/strategies/binding/key/base'
|
18
|
+
require 'garner/strategies/binding/key/cache_key'
|
19
|
+
require 'garner/strategies/binding/key/safe_cache_key'
|
20
|
+
require 'garner/strategies/binding/key/binding_index'
|
21
21
|
|
22
22
|
# Invalidation strategies
|
23
|
-
require
|
24
|
-
require
|
25
|
-
require
|
23
|
+
require 'garner/strategies/binding/invalidation/base'
|
24
|
+
require 'garner/strategies/binding/invalidation/touch'
|
25
|
+
require 'garner/strategies/binding/invalidation/binding_index'
|
26
26
|
|
27
27
|
# Cache
|
28
|
-
require
|
29
|
-
require
|
30
|
-
require
|
31
|
-
require
|
28
|
+
require 'garner/cache'
|
29
|
+
require 'garner/cache/identity'
|
30
|
+
require 'garner/cache/context'
|
31
|
+
require 'garner/cache/binding'
|
data/lib/garner/cache.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
module Garner
|
2
2
|
module Cache
|
3
|
+
class NilBinding < StandardError; end
|
3
4
|
|
4
5
|
# Fetch a result from cache.
|
5
6
|
#
|
@@ -7,7 +8,7 @@ module Garner
|
|
7
8
|
# bound. These objects' keys are injected into the compound cache key.
|
8
9
|
# @param key_hash [Hash] Hash to comprise the compound cache key.
|
9
10
|
# @param options_hash [Hash] Options to be passed to Garner.config.cache.
|
10
|
-
def self.fetch(bindings, key_hash, options_hash, &
|
11
|
+
def self.fetch(bindings, key_hash, options_hash, &_block)
|
11
12
|
if (compound_key = compound_key(bindings, key_hash))
|
12
13
|
result = Garner.config.cache.fetch(compound_key, options_hash) do
|
13
14
|
yield
|
@@ -19,15 +20,13 @@ module Garner
|
|
19
20
|
result
|
20
21
|
end
|
21
22
|
|
22
|
-
private
|
23
23
|
def self.compound_key(bindings, key_hash)
|
24
|
-
binding_keys = bindings.map(
|
25
|
-
|
24
|
+
binding_keys = bindings.map { |binding| key_for(binding) }.compact
|
26
25
|
if binding_keys.size == bindings.size
|
27
26
|
# All bindings have non-nil cache keys, proceed.
|
28
27
|
{
|
29
|
-
:
|
30
|
-
:
|
28
|
+
binding_keys: binding_keys,
|
29
|
+
context_keys: key_hash
|
31
30
|
}
|
32
31
|
else
|
33
32
|
# A nil cache key was generated. Skip caching.
|
@@ -37,5 +36,13 @@ module Garner
|
|
37
36
|
end
|
38
37
|
end
|
39
38
|
|
39
|
+
def self.key_for(binding)
|
40
|
+
if binding.nil?
|
41
|
+
return nil unless Garner.config.whiny_nils?
|
42
|
+
fail NilBinding
|
43
|
+
else
|
44
|
+
binding.garner_cache_key
|
45
|
+
end
|
46
|
+
end
|
40
47
|
end
|
41
48
|
end
|
data/lib/garner/cache/binding.rb
CHANGED
@@ -1,21 +1,13 @@
|
|
1
1
|
# Set up Garner configuration parameters
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
Garner.config.option(:
|
6
|
-
|
7
|
-
})
|
8
|
-
Garner.config.option(:mongoid_identity_fields, {
|
9
|
-
:default => [:_id]
|
10
|
-
})
|
11
|
-
Garner.config.option(:invalidate_mongoid_root, {
|
12
|
-
:default => true
|
13
|
-
})
|
2
|
+
|
3
|
+
Garner.config.option(:binding_key_strategy, default: Garner::Strategies::Binding::Key::SafeCacheKey)
|
4
|
+
Garner.config.option(:binding_invalidation_strategy, default: Garner::Strategies::Binding::Invalidation::Touch)
|
5
|
+
Garner.config.option(:mongoid_identity_fields, default: [:_id])
|
6
|
+
Garner.config.option(:invalidate_mongoid_root, default: true)
|
14
7
|
|
15
8
|
module Garner
|
16
9
|
module Cache
|
17
10
|
module Binding
|
18
|
-
|
19
11
|
# Override this method to use a custom key strategy.
|
20
12
|
#
|
21
13
|
# @return [Object] The strategy to be used for instances of this class.
|
@@ -46,6 +38,7 @@ module Garner
|
|
46
38
|
end
|
47
39
|
|
48
40
|
protected
|
41
|
+
|
49
42
|
def _invalidate
|
50
43
|
invalidation_strategy.apply(self)
|
51
44
|
end
|
@@ -61,7 +54,6 @@ module Garner
|
|
61
54
|
def _garner_after_destroy
|
62
55
|
_invalidate if invalidation_strategy.apply_on_callback?(:destroy)
|
63
56
|
end
|
64
|
-
|
65
57
|
end
|
66
58
|
end
|
67
59
|
end
|
data/lib/garner/cache/context.rb
CHANGED
@@ -1,20 +1,19 @@
|
|
1
1
|
# Set up Garner configuration parameters
|
2
|
-
Garner.config.option(:context_key_strategies,
|
3
|
-
|
4
|
-
|
5
|
-
Garner.config.option(:rack_context_key_strategies,
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
2
|
+
Garner.config.option(:context_key_strategies,
|
3
|
+
default: [Garner::Strategies::Context::Key::Caller]
|
4
|
+
)
|
5
|
+
Garner.config.option(:rack_context_key_strategies,
|
6
|
+
default: [
|
7
|
+
Garner::Strategies::Context::Key::Caller,
|
8
|
+
Garner::Strategies::Context::Key::RequestGet,
|
9
|
+
Garner::Strategies::Context::Key::RequestPost,
|
10
|
+
Garner::Strategies::Context::Key::RequestPath
|
11
|
+
]
|
12
|
+
)
|
13
13
|
|
14
14
|
module Garner
|
15
15
|
module Cache
|
16
16
|
module Context
|
17
|
-
|
18
17
|
# Instantiate a context-appropriate cache identity.
|
19
18
|
#
|
20
19
|
# @example
|
@@ -12,7 +12,7 @@ module Garner
|
|
12
12
|
|
13
13
|
# Set up options hash with defaults
|
14
14
|
@options_hash = Garner.config.global_cache_options || {}
|
15
|
-
@options_hash.merge!(
|
15
|
+
@options_hash.merge!(expires_in: Garner.config.expires_in)
|
16
16
|
end
|
17
17
|
|
18
18
|
def fetch(&block)
|
data/lib/garner/config.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
module Garner
|
2
|
-
|
3
2
|
class << self
|
4
3
|
# Set the configuration options. Best used by passing a block.
|
5
4
|
#
|
@@ -12,7 +11,7 @@ module Garner
|
|
12
11
|
def configure
|
13
12
|
block_given? ? yield(Garner::Config) : Garner::Config
|
14
13
|
end
|
15
|
-
|
14
|
+
alias_method :config, :configure
|
16
15
|
end
|
17
16
|
|
18
17
|
module Config
|
@@ -77,7 +76,7 @@ module Garner
|
|
77
76
|
#
|
78
77
|
# @return [Cache] The configured cache or a default cache instance.
|
79
78
|
def cache
|
80
|
-
settings[:cache] = default_cache unless settings.
|
79
|
+
settings[:cache] = default_cache unless settings.key?(:cache)
|
81
80
|
settings[:cache]
|
82
81
|
end
|
83
82
|
|
@@ -103,7 +102,7 @@ module Garner
|
|
103
102
|
#
|
104
103
|
# @return [String] The configured caller_root or a default.
|
105
104
|
def caller_root
|
106
|
-
settings[:caller_root] = default_caller_root unless settings.
|
105
|
+
settings[:caller_root] = default_caller_root unless settings.key?(:caller_root)
|
107
106
|
settings[:caller_root]
|
108
107
|
end
|
109
108
|
|
@@ -123,10 +122,16 @@ module Garner
|
|
123
122
|
end
|
124
123
|
|
125
124
|
# Default cache options
|
126
|
-
option(:global_cache_options, :
|
125
|
+
option(:global_cache_options, default: {})
|
127
126
|
|
128
127
|
# Default cache expiration time.
|
129
|
-
option(:expires_in, :
|
128
|
+
option(:expires_in, default: nil)
|
129
|
+
|
130
|
+
# Default behavior on nil bindings
|
131
|
+
option(:whiny_nils, default: true)
|
132
|
+
|
133
|
+
def whiny_nils?
|
134
|
+
whiny_nils
|
135
|
+
end
|
130
136
|
end
|
131
137
|
end
|
132
|
-
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'garner'
|
2
|
+
require 'active_record'
|
3
3
|
|
4
|
-
require
|
4
|
+
require 'garner/mixins/active_record/base'
|
@@ -1,5 +1,5 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'garner'
|
2
|
+
require 'mongoid'
|
3
3
|
|
4
|
-
require
|
5
|
-
require
|
4
|
+
require 'garner/mixins/mongoid/document'
|
5
|
+
require 'garner/mixins/mongoid/identity'
|
@@ -58,30 +58,26 @@ module Garner
|
|
58
58
|
binding = identify(arg)
|
59
59
|
identity = identity.bind(binding)
|
60
60
|
end
|
61
|
-
identity.key(
|
61
|
+
identity.key(garnered_find_args: args) do
|
62
62
|
find(*args)
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
66
|
-
after_create
|
67
|
-
after_update
|
68
|
-
after_destroy
|
66
|
+
after_create :_garner_after_create
|
67
|
+
after_update :_garner_after_update
|
68
|
+
after_destroy :_garner_after_destroy
|
69
69
|
|
70
70
|
protected
|
71
|
+
|
71
72
|
def self._latest_by_updated_at
|
72
73
|
# Only find the latest if we can order by :updated_at
|
73
|
-
return nil unless fields[
|
74
|
-
only(:_id, :_type, :updated_at).order_by(
|
75
|
-
:updated_at => :desc
|
76
|
-
}).first
|
74
|
+
return nil unless fields['updated_at']
|
75
|
+
only(:_id, :_type, :updated_at).order_by(updated_at: :desc).first
|
77
76
|
end
|
78
77
|
|
79
78
|
def _invalidate
|
80
79
|
invalidation_strategy.apply(self)
|
81
|
-
|
82
|
-
if _root != self && Garner.config.invalidate_mongoid_root
|
83
|
-
invalidation_strategy.apply(_root)
|
84
|
-
end
|
80
|
+
invalidation_strategy.apply(_root) if _root != self && Garner.config.invalidate_mongoid_root
|
85
81
|
end
|
86
82
|
|
87
83
|
end
|
@@ -15,7 +15,7 @@ module Garner
|
|
15
15
|
def self.from_class_and_handle(klass, handle)
|
16
16
|
validate_class!(klass)
|
17
17
|
|
18
|
-
|
18
|
+
new.tap do |identity|
|
19
19
|
identity.klass = klass
|
20
20
|
identity.handle = handle
|
21
21
|
identity.conditions = conditions_for(klass, handle)
|
@@ -41,21 +41,20 @@ module Garner
|
|
41
41
|
"#{self.class.name}/klass=#{klass},handle=#{handle}"
|
42
42
|
end
|
43
43
|
|
44
|
-
private
|
45
44
|
def self.validate_class!(klass)
|
46
45
|
if !klass.include?(::Mongoid::Document)
|
47
|
-
|
46
|
+
fail 'Must instantiate from a Mongoid class'
|
48
47
|
elsif klass.embedded?
|
49
|
-
|
48
|
+
fail 'Cannot instantiate from an embedded document class'
|
50
49
|
end
|
51
50
|
end
|
52
51
|
|
53
52
|
def self.conditions_for(klass, handle)
|
54
53
|
# Multiple-ID conditions
|
55
54
|
conditions = {
|
56
|
-
|
55
|
+
'$or' => Garner.config.mongoid_identity_fields.map do |field|
|
57
56
|
{ field => handle }
|
58
|
-
|
57
|
+
end
|
59
58
|
}
|
60
59
|
|
61
60
|
# _type conditions
|
data/lib/garner/mixins/rack.rb
CHANGED
@@ -3,11 +3,10 @@ module Garner
|
|
3
3
|
module Binding
|
4
4
|
module Invalidation
|
5
5
|
class Base
|
6
|
-
|
7
6
|
# Specifies whether invalidation should happen on callbacks.
|
8
7
|
#
|
9
8
|
# @param kind [Symbol] One of :create, :update, :destroy
|
10
|
-
def self.apply_on_callback?(
|
9
|
+
def self.apply_on_callback?(_kind = nil)
|
11
10
|
true
|
12
11
|
end
|
13
12
|
|
@@ -16,10 +15,9 @@ module Garner
|
|
16
15
|
#
|
17
16
|
# @param binding [Object] The binding whose caches are to be
|
18
17
|
# invalidated.
|
19
|
-
def self.apply(
|
18
|
+
def self.apply(_binding)
|
20
19
|
end
|
21
20
|
end
|
22
|
-
|
23
21
|
end
|
24
22
|
end
|
25
23
|
end
|
@@ -3,11 +3,10 @@ module Garner
|
|
3
3
|
module Binding
|
4
4
|
module Invalidation
|
5
5
|
class BindingIndex < Base
|
6
|
-
|
7
6
|
# Specifies whether invalidation should happen on callbacks.
|
8
7
|
#
|
9
8
|
# @param kind [Symbol] One of :create, :update, :destroy
|
10
|
-
def self.apply_on_callback?(
|
9
|
+
def self.apply_on_callback?(_kind = nil)
|
11
10
|
true
|
12
11
|
end
|
13
12
|
|
@@ -29,7 +28,6 @@ module Garner
|
|
29
28
|
end
|
30
29
|
end
|
31
30
|
end
|
32
|
-
|
33
31
|
end
|
34
32
|
end
|
35
33
|
end
|
@@ -3,7 +3,6 @@ module Garner
|
|
3
3
|
module Binding
|
4
4
|
module Invalidation
|
5
5
|
class Touch < Base
|
6
|
-
|
7
6
|
# Specifies whether invalidation should happen on callbacks.
|
8
7
|
#
|
9
8
|
# @param kind [Symbol] One of :create, :update, :destroy
|
@@ -35,7 +34,6 @@ module Garner
|
|
35
34
|
end
|
36
35
|
end
|
37
36
|
end
|
38
|
-
|
39
37
|
end
|
40
38
|
end
|
41
39
|
end
|