garner 0.4.5 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -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
|