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.
Files changed (75) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +7 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +35 -0
  5. data/.travis.yml +13 -0
  6. data/CHANGELOG.md +130 -0
  7. data/CONTRIBUTING.md +118 -0
  8. data/Gemfile +3 -0
  9. data/README.md +1 -0
  10. data/Rakefile +39 -0
  11. data/UPGRADING.md +118 -0
  12. data/garner.gemspec +44 -0
  13. data/lib/garner.rb +21 -21
  14. data/lib/garner/cache.rb +13 -6
  15. data/lib/garner/cache/binding.rb +6 -14
  16. data/lib/garner/cache/context.rb +11 -12
  17. data/lib/garner/cache/identity.rb +1 -1
  18. data/lib/garner/config.rb +12 -7
  19. data/lib/garner/mixins/active_record.rb +3 -3
  20. data/lib/garner/mixins/active_record/base.rb +2 -2
  21. data/lib/garner/mixins/mongoid.rb +4 -4
  22. data/lib/garner/mixins/mongoid/document.rb +8 -12
  23. data/lib/garner/mixins/mongoid/identity.rb +5 -6
  24. data/lib/garner/mixins/rack.rb +1 -2
  25. data/lib/garner/strategies/binding/invalidation/base.rb +2 -4
  26. data/lib/garner/strategies/binding/invalidation/binding_index.rb +1 -3
  27. data/lib/garner/strategies/binding/invalidation/touch.rb +0 -2
  28. data/lib/garner/strategies/binding/key/base.rb +1 -3
  29. data/lib/garner/strategies/binding/key/binding_index.rb +3 -4
  30. data/lib/garner/strategies/binding/key/cache_key.rb +0 -2
  31. data/lib/garner/strategies/binding/key/safe_cache_key.rb +2 -3
  32. data/lib/garner/strategies/context/key/base.rb +1 -3
  33. data/lib/garner/strategies/context/key/caller.rb +9 -12
  34. data/lib/garner/strategies/context/key/jsonp.rb +3 -6
  35. data/lib/garner/strategies/context/key/request_get.rb +2 -4
  36. data/lib/garner/strategies/context/key/request_path.rb +1 -3
  37. data/lib/garner/strategies/context/key/request_post.rb +2 -4
  38. data/lib/garner/version.rb +1 -1
  39. data/spec/garner/cache/context_spec.rb +38 -0
  40. data/spec/garner/cache/identity_spec.rb +68 -0
  41. data/spec/garner/cache_spec.rb +49 -0
  42. data/spec/garner/config_spec.rb +17 -0
  43. data/spec/garner/mixins/mongoid/document_spec.rb +80 -0
  44. data/spec/garner/mixins/mongoid/identity_spec.rb +140 -0
  45. data/spec/garner/mixins/rack_spec.rb +48 -0
  46. data/spec/garner/strategies/binding/invalidation/binding_index_spec.rb +14 -0
  47. data/spec/garner/strategies/binding/invalidation/touch_spec.rb +23 -0
  48. data/spec/garner/strategies/binding/key/binding_index_spec.rb +245 -0
  49. data/spec/garner/strategies/binding/key/cache_key_spec.rb +29 -0
  50. data/spec/garner/strategies/binding/key/safe_cache_key_spec.rb +61 -0
  51. data/spec/garner/strategies/context/key/caller_spec.rb +106 -0
  52. data/spec/garner/strategies/context/key/jsonp_spec.rb +22 -0
  53. data/spec/garner/strategies/context/key/request_get_spec.rb +33 -0
  54. data/spec/garner/strategies/context/key/request_path_spec.rb +28 -0
  55. data/spec/garner/strategies/context/key/request_post_spec.rb +34 -0
  56. data/spec/garner/version_spec.rb +11 -0
  57. data/spec/integration/active_record_spec.rb +43 -0
  58. data/spec/integration/grape_spec.rb +33 -0
  59. data/spec/integration/mongoid_spec.rb +355 -0
  60. data/spec/integration/rack_spec.rb +77 -0
  61. data/spec/integration/sinatra_spec.rb +29 -0
  62. data/spec/performance/strategy_benchmark.rb +59 -0
  63. data/spec/performance/support/benchmark_context.rb +31 -0
  64. data/spec/performance/support/benchmark_context_wrapper.rb +67 -0
  65. data/spec/shared/binding_invalidation_strategy.rb +17 -0
  66. data/spec/shared/binding_key_strategy.rb +35 -0
  67. data/spec/shared/conditional_get.rb +48 -0
  68. data/spec/shared/context_key_strategy.rb +24 -0
  69. data/spec/spec_helper.rb +24 -0
  70. data/spec/spec_support.rb +5 -0
  71. data/spec/support/active_record.rb +36 -0
  72. data/spec/support/cache.rb +15 -0
  73. data/spec/support/garner.rb +5 -0
  74. data/spec/support/mongoid.rb +71 -0
  75. metadata +155 -157
@@ -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
@@ -1,31 +1,31 @@
1
- require "multi_json"
2
- require "active_support"
1
+ require 'multi_json'
2
+ require 'active_support'
3
3
 
4
4
  # Garner core
5
- require "garner/version"
6
- require "garner/config"
5
+ require 'garner/version'
6
+ require 'garner/config'
7
7
 
8
8
  # Key strategies
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"
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 "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"
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 "garner/strategies/binding/invalidation/base"
24
- require "garner/strategies/binding/invalidation/touch"
25
- require "garner/strategies/binding/invalidation/binding_index"
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 "garner/cache"
29
- require "garner/cache/identity"
30
- require "garner/cache/context"
31
- require "garner/cache/binding"
28
+ require 'garner/cache'
29
+ require 'garner/cache/identity'
30
+ require 'garner/cache/context'
31
+ require 'garner/cache/binding'
@@ -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, &block)
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(&:garner_cache_key).compact
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
- :binding_keys => binding_keys,
30
- :context_keys => key_hash
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
@@ -1,21 +1,13 @@
1
1
  # Set up Garner configuration parameters
2
- Garner.config.option(:binding_key_strategy, {
3
- :default => Garner::Strategies::Binding::Key::SafeCacheKey
4
- })
5
- Garner.config.option(:binding_invalidation_strategy, {
6
- :default => Garner::Strategies::Binding::Invalidation::Touch
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
@@ -1,20 +1,19 @@
1
1
  # Set up Garner configuration parameters
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
- })
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!({ :expires_in => Garner.config.expires_in })
15
+ @options_hash.merge!(expires_in: Garner.config.expires_in)
16
16
  end
17
17
 
18
18
  def fetch(&block)
@@ -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
- alias :config :configure
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.has_key?(:cache)
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.has_key?(:caller_root)
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, :default => {})
125
+ option(:global_cache_options, default: {})
127
126
 
128
127
  # Default cache expiration time.
129
- option(:expires_in, :default => nil)
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 "garner"
2
- require "active_record"
1
+ require 'garner'
2
+ require 'active_record'
3
3
 
4
- require "garner/mixins/active_record/base"
4
+ require 'garner/mixins/active_record/base'
@@ -1,5 +1,5 @@
1
- require "garner"
2
- require "active_record"
1
+ require 'garner'
2
+ require 'active_record'
3
3
 
4
4
  module Garner
5
5
  module Mixins
@@ -1,5 +1,5 @@
1
- require "garner"
2
- require "mongoid"
1
+ require 'garner'
2
+ require 'mongoid'
3
3
 
4
- require "garner/mixins/mongoid/document"
5
- require "garner/mixins/mongoid/identity"
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({ :garnered_find_args => args }) do
61
+ identity.key(garnered_find_args: args) do
62
62
  find(*args)
63
63
  end
64
64
  end
65
65
 
66
- after_create :_garner_after_create
67
- after_update :_garner_after_update
68
- after_destroy :_garner_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["updated_at"]
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
- self.new.tap do |identity|
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
- raise "Must instantiate from a Mongoid class"
46
+ fail 'Must instantiate from a Mongoid class'
48
47
  elsif klass.embedded?
49
- raise "Cannot instantiate from an embedded document class"
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
- "$or" => Garner.config.mongoid_identity_fields.map { |field|
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
@@ -1,9 +1,8 @@
1
- require "garner"
1
+ require 'garner'
2
2
 
3
3
  module Garner
4
4
  module Mixins
5
5
  module Rack
6
-
7
6
  # Override this method to conditionally disable the cache.
8
7
  #
9
8
  # @return [Boolean]
@@ -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?(kind = nil)
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(binding)
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?(kind = nil)
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