garner 0.4.5 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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,80 @@
1
+ require 'spec_helper'
2
+ require 'garner/mixins/mongoid'
3
+
4
+ describe Garner::Mixins::Mongoid::Document do
5
+ context 'at the instance level' do
6
+ before(:each) do
7
+ Garner.configure do |config|
8
+ config.mongoid_identity_fields = [:_id, :_slugs]
9
+ end
10
+
11
+ @monger = Monger.create(name: 'M1')
12
+ @cheese = Cheese.create(name: 'M1')
13
+ end
14
+
15
+ describe 'proxied_classes' do
16
+ it 'returns all Mongoid superclasses' do
17
+ @monger.proxied_classes.should eq [Monger]
18
+ @cheese.proxied_classes.should eq [Cheese, Food]
19
+ end
20
+ end
21
+ end
22
+
23
+ context 'at the class level' do
24
+ subject { Monger }
25
+
26
+ describe '_latest_by_updated_at' do
27
+ it 'returns a Mongoid::Document instance' do
28
+ subject.create
29
+ subject.send(:_latest_by_updated_at).should be_a(subject)
30
+ end
31
+
32
+ it 'returns the _latest_by_updated_at document by :updated_at' do
33
+ mongers = 3.times.map { |i| subject.create(name: "M#{i}") }
34
+ mongers[1].touch
35
+
36
+ subject.send(:_latest_by_updated_at)._id.should eq mongers[1]._id
37
+ subject.send(:_latest_by_updated_at).updated_at.should eq mongers[1].reload.updated_at
38
+ end
39
+
40
+ it 'returns nil if there are no documents' do
41
+ subject.send(:_latest_by_updated_at).should be_nil
42
+ end
43
+
44
+ it 'returns nil if updated_at does not exist' do
45
+ subject.create
46
+ subject.stub(:fields) { {} }
47
+ subject.send(:_latest_by_updated_at).should be_nil
48
+ end
49
+ end
50
+
51
+ describe 'proxy_binding' do
52
+ it 'returns the _latest_by_updated_at document' do
53
+ subject.create
54
+ subject.proxy_binding.should be_a(Monger)
55
+ end
56
+
57
+ it 'responds to :touch' do
58
+ subject.create
59
+ subject.any_instance.should_receive(:touch)
60
+ subject.proxy_binding.touch
61
+ end
62
+
63
+ describe 'cache_key' do
64
+ it 'matches what would be returned from the full object' do
65
+ monger = subject.create
66
+ subject.proxy_binding.cache_key.should eq monger.reload.cache_key
67
+ end
68
+
69
+ context 'with Mongoid subclasses' do
70
+ subject { Cheese }
71
+
72
+ it 'matches what would be returned from the full object' do
73
+ cheese = subject.create
74
+ subject.proxy_binding.cache_key.should eq cheese.reload.cache_key
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,140 @@
1
+ require 'spec_helper'
2
+ require 'garner/mixins/mongoid'
3
+
4
+ describe Garner::Mixins::Mongoid::Identity do
5
+ before(:each) do
6
+ @mock_strategy = double('strategy')
7
+ @mock_strategy.stub(:apply)
8
+ @mock_mongoid_strategy = double('mongoid_strategy')
9
+ @mock_mongoid_strategy.stub(:apply)
10
+ end
11
+
12
+ describe 'from_class_and_handle' do
13
+ before(:each) do
14
+ Garner.configure do |config|
15
+ config.mongoid_identity_fields = [:_id, :_slugs]
16
+ end
17
+ end
18
+
19
+ subject { Garner::Mixins::Mongoid::Identity }
20
+
21
+ it 'raises an exception if called on a non-Mongoid class' do
22
+ expect do
23
+ subject.from_class_and_handle(Class.new, 'id')
24
+ end.to raise_error
25
+
26
+ expect do
27
+ subject.from_class_and_handle(Monger.new, 'id')
28
+ end.to raise_error
29
+ end
30
+
31
+ it 'raises an exception if called on an embedded document' do
32
+ expect do
33
+ subject.from_class_and_handle(Fish, 'id')
34
+ end.to raise_error
35
+ end
36
+
37
+ it 'sets klass, handle and a conditions hash' do
38
+ identity = subject.from_class_and_handle(Monger, 'id')
39
+ identity.klass.should eq Monger
40
+ identity.handle.should eq 'id'
41
+ identity.conditions['$or'].should eq [
42
+ { _id: 'id' },
43
+ { _slugs: 'id' }
44
+ ]
45
+ end
46
+
47
+ context 'on a Mongoid subclass' do
48
+ it 'sets klass to parent and includes the _type field' do
49
+ identity = subject.from_class_and_handle(Cheese, 'id')
50
+ identity.klass.should eq Cheese
51
+ identity.conditions[:_type].should eq('$in' => ['Cheese'])
52
+ identity.conditions['$or'].should eq [
53
+ { _id: 'id' },
54
+ { _slugs: 'id' }
55
+ ]
56
+ end
57
+ end
58
+ end
59
+
60
+ describe 'to_s' do
61
+ subject { Monger.identify('m1').to_s }
62
+
63
+ it 'stringizes the binding and includes klass and handle' do
64
+ subject.should be_a(String)
65
+ subject.should =~ /Monger/
66
+ subject.should =~ /m1/
67
+ end
68
+
69
+ it 'should not change across identical instances' do
70
+ subject.should eq Monger.identify('m1').to_s
71
+ end
72
+
73
+ it 'should be different across different instances' do
74
+ subject.should_not == Monger.identify('m2').to_s
75
+ end
76
+ end
77
+
78
+ context 'with default configuration and real documents' do
79
+ before(:each) do
80
+ Garner.configure do |config|
81
+ config.mongoid_identity_fields = [:_id, :_slugs]
82
+ end
83
+
84
+ @monger = Monger.create(name: 'M1')
85
+ @cheese = Cheese.create(name: 'Havarti')
86
+ @cheese.reload
87
+ end
88
+
89
+ describe 'proxy_binding' do
90
+ it 'returns nil for nonexistent bindings' do
91
+ Monger.identify('m2').proxy_binding.should be_nil
92
+ end
93
+
94
+ it 'returns nil for nil bindings' do
95
+ @monger.unset(:_slugs)
96
+ Monger.identify(nil).proxy_binding.should be_nil
97
+ end
98
+
99
+ it 'limits the query' do
100
+ Mongoid::Slug::Criteria.any_instance.should_receive(:limit).with(1).and_return([@monger])
101
+ Monger.identify('m1').proxy_binding
102
+ end
103
+
104
+ describe 'cache_key' do
105
+ it "generates a cache key equal to Mongoid::Document's" do
106
+ Monger.identify('m1').proxy_binding.cache_key.should eq @monger.cache_key
107
+
108
+ # Also test for Mongoid subclasses
109
+ Cheese.identify('havarti').proxy_binding.cache_key.should eq @cheese.cache_key
110
+ Food.identify(@cheese.id).proxy_binding.cache_key.should eq @cheese.cache_key
111
+ end
112
+
113
+ context 'without Mongoid::Timestamps' do
114
+ before(:each) do
115
+ @monger.unset(:updated_at)
116
+ @cheese.unset(:updated_at)
117
+ end
118
+
119
+ it "generates a cache key equal to Mongoid::Document's" do
120
+ Monger.identify('m1').proxy_binding.cache_key.should eq @monger.cache_key
121
+
122
+ # Also test for Mongoid subclasses
123
+ Cheese.identify('havarti').proxy_binding.cache_key.should eq @cheese.cache_key
124
+ Food.identify(@cheese.id).proxy_binding.cache_key.should eq @cheese.cache_key
125
+ end
126
+ end
127
+ end
128
+
129
+ describe 'updated_at' do
130
+ it "returns :updated_at equal to Mongoid::Document's" do
131
+ Monger.identify('m1').proxy_binding.updated_at.should eq Monger.find('m1').updated_at
132
+
133
+ # Also test for Mongoid subclasses
134
+ Cheese.identify('havarti').proxy_binding.updated_at.should eq @cheese.updated_at
135
+ Food.identify(@cheese.id).proxy_binding.updated_at.should eq @cheese.updated_at
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+ require 'garner/mixins/rack'
3
+
4
+ describe Garner::Mixins::Rack do
5
+
6
+ describe 'garner' do
7
+
8
+ before(:each) do
9
+ class MockApp
10
+ include Garner::Mixins::Rack
11
+
12
+ def request
13
+ Rack::Request.new('REQUEST_METHOD' => 'GET', 'QUERY_STRING' => 'foo=bar')
14
+ end
15
+ end
16
+
17
+ @mock_app = MockApp.new
18
+ end
19
+
20
+ subject do
21
+ -> { @mock_app.garner }
22
+ end
23
+
24
+ it 'returns a Garner::Cache::Identity' do
25
+ subject.call.should be_a(Garner::Cache::Identity)
26
+ end
27
+
28
+ it "sets the identity's ruby_binding to self" do
29
+ subject.call.ruby_context.should eq @mock_app
30
+ end
31
+
32
+ it 'applies each of Garner.config.rack_context_key_strategies' do
33
+ # Default :context_key_strategies
34
+ subject.call.key_hash[:caller].should_not be_nil
35
+ subject.call.key_hash[:request_params].should eq('foo' => 'bar')
36
+
37
+ # Custom :context_key_strategies
38
+ Garner.configure do |config|
39
+ config.rack_context_key_strategies = [
40
+ Garner::Strategies::Context::Key::RequestGet
41
+ ]
42
+ end
43
+ subject.call.key_hash[:caller].should be_nil
44
+ subject.call.key_hash[:request_params].should eq('foo' => 'bar')
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ describe Garner::Strategies::Binding::Invalidation::BindingIndex do
4
+
5
+ before(:each) do
6
+ @mock = double('model')
7
+ @mock.stub(:touch)
8
+ end
9
+
10
+ subject { Garner::Strategies::Binding::Invalidation::BindingIndex }
11
+
12
+ it_behaves_like 'Garner::Strategies::Binding::Invalidation strategy'
13
+
14
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ describe Garner::Strategies::Binding::Invalidation::Touch do
4
+
5
+ before(:each) do
6
+ @mock = double('model')
7
+ @mock.stub(:touch)
8
+ end
9
+
10
+ subject { Garner::Strategies::Binding::Invalidation::Touch }
11
+
12
+ it_behaves_like 'Garner::Strategies::Binding::Invalidation strategy'
13
+
14
+ it 'calls :touch on the object' do
15
+ @mock.should_receive(:touch)
16
+ subject.apply(@mock)
17
+ end
18
+
19
+ it 'does not raise error if :touch is undefined' do
20
+ @mock.unstub(:touch)
21
+ expect { subject.apply(@mock) }.to_not raise_error
22
+ end
23
+ end
@@ -0,0 +1,245 @@
1
+ require 'spec_helper'
2
+
3
+ describe Garner::Strategies::Binding::Key::BindingIndex do
4
+
5
+ before(:each) do
6
+ @new_mock = double('new_mock')
7
+
8
+ @persisted_mock = double('persisted_mock')
9
+ @persisted_mock.stub(:identity_string) { 'Mocker/id=4' }
10
+ @persisted_mock.stub(:updated_at) { @time_dot_now }
11
+
12
+ @persisted_mock_alias = double('persisted_mock_alias')
13
+ @persisted_mock_alias.stub(:identity_string) { 'MockerAlias/id=alias-4' }
14
+ @persisted_mock_alias.stub(:proxy_binding) { @persisted_mock }
15
+
16
+ subject.stub(:canonical?) do |binding|
17
+ binding == @persisted_mock
18
+ end
19
+
20
+ # Marshal.load will return a new mock object, breaking equivalence tests
21
+ # when fetching from cache.
22
+ load_method = Marshal.method(:load)
23
+ Marshal.stub(:load) do |dump|
24
+ default = load_method.call(dump)
25
+ if default.is_a?(RSpec::Mocks::Double) &&
26
+ default.instance_variable_get(:@name) == 'persisted_mock'
27
+ @persisted_mock
28
+ else
29
+ default
30
+ end
31
+ end
32
+
33
+ # Stub SecureRandom.hex()-generated keys for consistency.
34
+ @mock_key = 'cc318d04bac07d5d91f06f8c'
35
+ @mock_alias_key = 'f254b853d7b32406b5749410'
36
+ @random_key = 'b1d44bb6b369903b28549271'
37
+ subject.stub(:new_cache_key_for) do |binding|
38
+ if binding == @persisted_mock
39
+ @mock_key
40
+ elsif binding == @persisted_mock_alias
41
+ @mock_alias_key
42
+ else
43
+ SecureRandom.hex(12)
44
+ end
45
+ end
46
+ end
47
+
48
+ subject { Garner::Strategies::Binding::Key::BindingIndex }
49
+
50
+ it_behaves_like 'Garner::Strategies::Binding::Key strategy' do
51
+ let(:known_bindings) { [@persisted_mock, @persisted_mock_alias] }
52
+ let(:unknown_bindings) { [] }
53
+ end
54
+
55
+ describe 'apply' do
56
+ it 'calls fetch_cache_key_for' do
57
+ subject.should_receive(:fetch_cache_key_for).with(@persisted_mock)
58
+ subject.apply(@persisted_mock)
59
+ end
60
+ end
61
+
62
+ describe 'fetch_cache_key_for' do
63
+ context 'with a canonical binding' do
64
+ it 'returns a cache key string' do
65
+ subject.fetch_cache_key_for(@persisted_mock).should eq @mock_key
66
+ end
67
+
68
+ it 'stores the cache key to cache' do
69
+ subject.fetch_cache_key_for(@persisted_mock)
70
+ Garner.config.cache.read(
71
+ strategy: subject,
72
+ proxied_binding: 'Mocker/id=4'
73
+ ).should eq @mock_key
74
+ end
75
+ end
76
+
77
+ context 'with a non-canonical binding' do
78
+ it 'returns a cache key string' do
79
+ subject.fetch_cache_key_for(@persisted_mock_alias).should eq @mock_key
80
+ end
81
+
82
+ it 'stores the canonical binding to cache' do
83
+ subject.fetch_cache_key_for(@persisted_mock_alias)
84
+ Garner.config.cache.read(
85
+ strategy: subject,
86
+ proxied_binding: 'MockerAlias/id=alias-4'
87
+ ).should eq @persisted_mock
88
+ end
89
+
90
+ it 'stores the cache key to cache' do
91
+ subject.fetch_cache_key_for(@persisted_mock_alias)
92
+ Garner.config.cache.read(
93
+ strategy: subject,
94
+ proxied_binding: 'Mocker/id=4'
95
+ ).should eq @mock_key
96
+ end
97
+
98
+ context 'whose canonical binding is nil' do
99
+ before(:each) do
100
+ @persisted_mock_alias.stub(:proxy_binding) { nil }
101
+ end
102
+
103
+ it 'returns a nil cache key' do
104
+ subject.fetch_cache_key_for(@persisted_mock_alias).should be_nil
105
+ end
106
+
107
+ it 'does not store the cache key to cache' do
108
+ subject.fetch_cache_key_for(@persisted_mock_alias)
109
+ Garner.config.cache.read(
110
+ strategy: subject,
111
+ proxied_binding: ''
112
+ ).should be_nil
113
+ end
114
+ end
115
+ end
116
+ end
117
+
118
+ describe 'write_cache_key_for' do
119
+ context 'with a canonical binding' do
120
+ it 'returns a cache key string' do
121
+ subject.write_cache_key_for(@persisted_mock).should eq @mock_key
122
+ end
123
+ end
124
+
125
+ context 'with a non-canonical binding' do
126
+ it 'returns a cache key string' do
127
+ subject.write_cache_key_for(@persisted_mock_alias).should eq @mock_key
128
+ end
129
+
130
+ context 'whose canonical binding is nil' do
131
+ before(:each) do
132
+ @persisted_mock_alias.stub(:proxy_binding) { nil }
133
+ end
134
+
135
+ it 'returns a nil cache key' do
136
+ subject.write_cache_key_for(@persisted_mock_alias).should be_nil
137
+ end
138
+ end
139
+ end
140
+ end
141
+
142
+ describe 'fetch_canonical_binding_for' do
143
+ context 'with a canonical binding' do
144
+ it 'returns the canonical binding' do
145
+ subject.fetch_canonical_binding_for(@persisted_mock).should eq @persisted_mock
146
+ end
147
+ end
148
+
149
+ context 'with a non-canonical binding' do
150
+ it 'returns the canonical binding' do
151
+ subject.fetch_canonical_binding_for(@persisted_mock_alias).should eq @persisted_mock
152
+ end
153
+
154
+ it 'stores the canonical binding to cache' do
155
+ subject.fetch_canonical_binding_for(@persisted_mock_alias)
156
+ Garner.config.cache.read(
157
+ strategy: subject,
158
+ proxied_binding: 'MockerAlias/id=alias-4'
159
+ ).should eq @persisted_mock
160
+ end
161
+ end
162
+
163
+ context 'with a proxyless binding' do
164
+ it 'returns nil' do
165
+ subject.fetch_canonical_binding_for(@new_mock).should.nil?
166
+ end
167
+ end
168
+ end
169
+
170
+ describe 'write_canonical_binding_for' do
171
+ context 'with a canonical binding' do
172
+ it 'returns the canonical binding' do
173
+ subject.write_canonical_binding_for(@persisted_mock).should eq @persisted_mock
174
+ end
175
+ end
176
+
177
+ context 'with a non-canonical binding' do
178
+ it 'returns the canonical binding' do
179
+ subject.write_canonical_binding_for(@persisted_mock_alias).should eq @persisted_mock
180
+ end
181
+ end
182
+
183
+ context 'with a proxyless binding' do
184
+ it 'returns nil' do
185
+ subject.write_canonical_binding_for(@new_mock).should.nil?
186
+ end
187
+ end
188
+ end
189
+
190
+ context 'with real objects' do
191
+ before(:each) do
192
+ subject.unstub(:canonical?)
193
+ subject.unstub(:new_cache_key_for)
194
+ Garner.configure do |config|
195
+ config.mongoid_identity_fields = [:_id, :_slugs]
196
+ end
197
+
198
+ @cheese = Cheese.create(name: 'M1')
199
+ @food = Food.create(name: 'F1')
200
+ end
201
+
202
+ it_behaves_like 'Garner::Strategies::Binding::Key strategy' do
203
+ let(:known_bindings) do
204
+ [Cheese, @cheese, Cheese.identify(@cheese.id), Cheese.identify('m1')]
205
+ end
206
+
207
+ let(:unknown_bindings) do
208
+ [Cheese.identify('m2'), Food.identify(nil)]
209
+ end
210
+ end
211
+
212
+ describe 'apply' do
213
+ it 'retrieves the correct key' do
214
+ key = subject.apply(Cheese.find('m1'))
215
+ subject.apply(Cheese.identify('m1')).should eq key
216
+ end
217
+
218
+ it 'stores the appropriate values to cache' do
219
+ key1 = subject.apply(Food.identify(@cheese.id))
220
+ key2 = subject.apply(Cheese.identify('m1'))
221
+ key1.should eq key2
222
+
223
+ Garner.config.cache.read(
224
+ strategy: subject,
225
+ proxied_binding: "Garner::Mixins::Mongoid::Identity/klass=Food,handle=#{@cheese.id}"
226
+ ).should eq @cheese
227
+
228
+ Garner.config.cache.read(
229
+ strategy: subject,
230
+ proxied_binding: 'Garner::Mixins::Mongoid::Identity/klass=Cheese,handle=m1'
231
+ ).should eq @cheese
232
+
233
+ Garner.config.cache.read(
234
+ strategy: subject,
235
+ proxied_binding: "Cheese/id=#{@cheese.id}"
236
+ ).should eq key1
237
+
238
+ Garner.config.cache.read(
239
+ strategy: subject,
240
+ proxied_binding: "Food/id=#{@cheese.id}"
241
+ ).should be_nil
242
+ end
243
+ end
244
+ end
245
+ end