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,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