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