cache_crispies 0.1.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/.rspec +1 -0
- data/lib/cache_crispies/attribute.rb +66 -0
- data/lib/cache_crispies/base.rb +110 -0
- data/lib/cache_crispies/collection.rb +43 -0
- data/lib/cache_crispies/condition.rb +30 -0
- data/lib/cache_crispies/controller.rb +26 -0
- data/lib/cache_crispies/hash_builder.rb +61 -0
- data/lib/cache_crispies/memoizer.rb +18 -0
- data/lib/cache_crispies/plan.rb +68 -0
- data/lib/cache_crispies/version.rb +3 -0
- data/lib/cache_crispies.rb +18 -0
- data/spec/attribute_spec.rb +159 -0
- data/spec/base_spec.rb +153 -0
- data/spec/collection_spec.rb +75 -0
- data/spec/condition_spec.rb +45 -0
- data/spec/controller_spec.rb +54 -0
- data/spec/fixtures/test_serializer.rb +2 -0
- data/spec/hash_builder_spec.rb +145 -0
- data/spec/memoizer_spec.rb +24 -0
- data/spec/plan_spec.rb +151 -0
- data/spec/spec_helper.rb +102 -0
- metadata +143 -0
data/spec/base_spec.rb
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
describe CacheCrispies::Base do
|
5
|
+
class NutritionSerializer < CacheCrispies::Base
|
6
|
+
serialize :calories
|
7
|
+
end
|
8
|
+
|
9
|
+
class CacheCrispiesTestSerializer < CacheCrispies::Base
|
10
|
+
serialize :id, :company, to: String
|
11
|
+
|
12
|
+
show_if -> { true } do
|
13
|
+
show_if -> { true } do
|
14
|
+
show_if -> { true } do
|
15
|
+
serialize :name, from: :brand
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
nest_in :nested do
|
21
|
+
nest_in :nested_again do
|
22
|
+
serialize :deeply_nested
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
serialize :nutrition_info, with: NutritionSerializer
|
27
|
+
|
28
|
+
serialize :organic, to: :bool
|
29
|
+
|
30
|
+
def id
|
31
|
+
model.id.to_s
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
let(:model) do
|
36
|
+
OpenStruct.new(
|
37
|
+
id: 42,
|
38
|
+
brand: 'Cookie Crisp',
|
39
|
+
company: 'General Mills',
|
40
|
+
deeply_nested: true,
|
41
|
+
nutrition_info: OpenStruct.new(calories: 1_000),
|
42
|
+
organic: 'true'
|
43
|
+
)
|
44
|
+
end
|
45
|
+
|
46
|
+
let(:instance) { CacheCrispiesTestSerializer.new(model) }
|
47
|
+
subject { instance }
|
48
|
+
|
49
|
+
describe '#as_json' do
|
50
|
+
it 'serializes to a hash' do
|
51
|
+
expect(subject.as_json).to eq(
|
52
|
+
id: '42',
|
53
|
+
name: 'Cookie Crisp',
|
54
|
+
company: 'General Mills',
|
55
|
+
nested: {
|
56
|
+
nested_again: {
|
57
|
+
deeply_nested: true
|
58
|
+
}
|
59
|
+
},
|
60
|
+
nutrition_info: {
|
61
|
+
calories: 1000
|
62
|
+
},
|
63
|
+
organic: true
|
64
|
+
)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe '.key' do
|
69
|
+
it 'underscores the demodulized class name' do
|
70
|
+
expect(subject.class.key).to eq :cache_crispies_test
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe '.collection_key' do
|
75
|
+
it 'pluralizes the #key' do
|
76
|
+
expect(subject.class.collection_key).to eq :cache_crispies_tests
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe '.do_caching?' do
|
81
|
+
it 'is false by default' do
|
82
|
+
expect(subject.class.do_caching?).to be false
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe '.cache_key_addons' do
|
87
|
+
it 'returns an empty array by default' do
|
88
|
+
expect(subject.class.cache_key_addons).to eq []
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe '.cache_key_base' do
|
93
|
+
# TODO
|
94
|
+
end
|
95
|
+
|
96
|
+
describe '.attributes' do
|
97
|
+
subject { instance.class.attributes }
|
98
|
+
|
99
|
+
it 'contains all the attributes' do
|
100
|
+
expect(subject.length).to eq 6
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'preserves the attribute order' do
|
104
|
+
expect(subject.map(&:key)).to eq(
|
105
|
+
%i[id company name deeply_nested nutrition_info organic]
|
106
|
+
)
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'contains the correct attribute values' do
|
110
|
+
expect(subject[0].method_name).to eq :id
|
111
|
+
expect(subject[0].key).to eq :id
|
112
|
+
expect(subject[0].serializer).to be nil
|
113
|
+
expect(subject[0].coerce_to).to be String
|
114
|
+
expect(subject[0].nesting).to eq []
|
115
|
+
expect(subject[0].conditions).to eq []
|
116
|
+
|
117
|
+
expect(subject[1].method_name).to eq :company
|
118
|
+
expect(subject[1].key).to eq :company
|
119
|
+
expect(subject[1].serializer).to be nil
|
120
|
+
expect(subject[1].coerce_to).to be String
|
121
|
+
expect(subject[1].nesting).to eq []
|
122
|
+
expect(subject[1].conditions).to eq []
|
123
|
+
|
124
|
+
expect(subject[2].method_name).to eq :brand
|
125
|
+
expect(subject[2].key).to eq :name
|
126
|
+
expect(subject[2].serializer).to be nil
|
127
|
+
expect(subject[2].coerce_to).to be nil
|
128
|
+
expect(subject[2].nesting).to eq []
|
129
|
+
expect(subject[2].conditions.length).to be 3
|
130
|
+
|
131
|
+
expect(subject[3].method_name).to eq :deeply_nested
|
132
|
+
expect(subject[3].key).to eq :deeply_nested
|
133
|
+
expect(subject[3].serializer).to be nil
|
134
|
+
expect(subject[3].coerce_to).to be nil
|
135
|
+
expect(subject[3].nesting).to eq %i[nested nested_again]
|
136
|
+
expect(subject[3].conditions).to eq []
|
137
|
+
|
138
|
+
expect(subject[4].method_name).to eq :nutrition_info
|
139
|
+
expect(subject[4].key).to eq :nutrition_info
|
140
|
+
expect(subject[4].serializer).to be NutritionSerializer
|
141
|
+
expect(subject[4].coerce_to).to be nil
|
142
|
+
expect(subject[4].nesting).to eq []
|
143
|
+
expect(subject[4].conditions).to eq []
|
144
|
+
|
145
|
+
expect(subject[5].method_name).to eq :organic
|
146
|
+
expect(subject[5].key).to eq :organic
|
147
|
+
expect(subject[5].serializer).to be nil
|
148
|
+
expect(subject[5].coerce_to).to be :bool
|
149
|
+
expect(subject[5].nesting).to eq []
|
150
|
+
expect(subject[5].conditions).to eq []
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CacheCrispies::Collection do
|
4
|
+
class UncacheableCerealSerializerForCollection < CacheCrispies::Base
|
5
|
+
serialize :name
|
6
|
+
end
|
7
|
+
|
8
|
+
class CacheableCerealSerializerForCollection < CacheCrispies::Base
|
9
|
+
serialize :name
|
10
|
+
|
11
|
+
def self.do_caching?
|
12
|
+
true
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:name1) { 'Cinnamon Toast Crunch' }
|
17
|
+
let(:name2) { 'Cocoa Puffs' }
|
18
|
+
let(:model1) { OpenStruct.new(name: name1) }
|
19
|
+
let(:model2) { OpenStruct.new(name: name2) }
|
20
|
+
let(:uncacheable_models) { [model1, model2] }
|
21
|
+
let(:cacheable_models) {
|
22
|
+
[model1, model2].tap do |models|
|
23
|
+
def models.cache_key
|
24
|
+
'cereals-key'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
}
|
28
|
+
let(:collection) { cacheable_models }
|
29
|
+
let(:serializer) { CacheableCerealSerializerForCollection }
|
30
|
+
let(:options) { {} }
|
31
|
+
subject { described_class.new(collection, serializer, options) }
|
32
|
+
|
33
|
+
describe '#as_json' do
|
34
|
+
context "when it's not cacheable" do
|
35
|
+
context 'because the collection is not cacheable' do
|
36
|
+
let(:collection) { uncacheable_models }
|
37
|
+
|
38
|
+
it "doesn't cache the results" do
|
39
|
+
expect(CacheCrispies::Plan).to_not receive(:new)
|
40
|
+
expect(Rails).to_not receive :cache
|
41
|
+
expect(subject.as_json).to eq [ {name: name1}, {name: name2} ]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'because the serializer is not cacheable' do
|
46
|
+
let(:serializer) { UncacheableCerealSerializerForCollection }
|
47
|
+
|
48
|
+
it "doesn't cache the results" do
|
49
|
+
expect(CacheCrispies::Plan).to_not receive(:new)
|
50
|
+
expect(Rails).to_not receive :cache
|
51
|
+
expect(subject.as_json).to eq [ {name: name1}, {name: name2} ]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'when it is cacheable' do
|
57
|
+
it 'caches the results' do
|
58
|
+
expect(CacheCrispies::Plan).to receive(:new).with(
|
59
|
+
serializer, model1, options
|
60
|
+
).and_return double(cache_key: 'cereal-key-1')
|
61
|
+
|
62
|
+
expect(CacheCrispies::Plan).to receive(:new).with(
|
63
|
+
serializer, model2, options
|
64
|
+
).and_return double(cache_key: 'cereal-key-2')
|
65
|
+
|
66
|
+
expect(Rails).to receive_message_chain(:cache, :fetch_multi).with(
|
67
|
+
%w[cereal-key-1 cereal-key-2]
|
68
|
+
).and_yield('cereal-key-1').and_return(name: name1)
|
69
|
+
.and_yield('cereal-key-2').and_return(name: name2)
|
70
|
+
|
71
|
+
subject.as_json
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CacheCrispies::Condition do
|
4
|
+
let(:block) { -> {} }
|
5
|
+
subject { described_class.new(block) }
|
6
|
+
|
7
|
+
describe '#uid' do
|
8
|
+
it "is the same as the block's object_id" do
|
9
|
+
expect(subject.uid).to be block.object_id
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '#true_for?' do
|
14
|
+
let(:block) { ->(_arg1, _arg2) { 'truthy string' } }
|
15
|
+
let(:model) { Object.new }
|
16
|
+
let(:options) { {} }
|
17
|
+
|
18
|
+
it 'calls the block with model and options arguments' do
|
19
|
+
expect(block).to receive(:call).with(model, options)
|
20
|
+
subject.true_for? model, options
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'when the block has one argument' do
|
24
|
+
let(:block) { ->(_arg1) { } }
|
25
|
+
|
26
|
+
it 'calls the block with the model only' do
|
27
|
+
expect(block).to receive(:call).with(model)
|
28
|
+
subject.true_for? model, options
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'when the block has no arguments' do
|
33
|
+
let(:block) { -> {} }
|
34
|
+
|
35
|
+
it 'calls the block with no arguments' do
|
36
|
+
expect(block).to receive(:call).with(no_args)
|
37
|
+
subject.true_for? model, options
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'returns a boolean' do
|
42
|
+
expect(subject.true_for?(model, options)).to be true
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'active_model'
|
3
|
+
require 'action_controller'
|
4
|
+
|
5
|
+
describe CacheCrispies::Controller do
|
6
|
+
class Cereal
|
7
|
+
include ActiveModel::Model
|
8
|
+
attr_accessor :name
|
9
|
+
end
|
10
|
+
|
11
|
+
class CerealController < ActionController::Base
|
12
|
+
include CacheCrispies::Controller
|
13
|
+
end
|
14
|
+
|
15
|
+
class CerealSerializerForController < CacheCrispies::Base
|
16
|
+
serialize :name
|
17
|
+
|
18
|
+
def self.key
|
19
|
+
'cereal'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
let(:cereal_names) { ['Count Chocula', 'Eyeholes'] }
|
24
|
+
let(:collection) { cereal_names.map { |name| Cereal.new(name: name) } }
|
25
|
+
|
26
|
+
subject { CerealController.new }
|
27
|
+
|
28
|
+
before do
|
29
|
+
allow_any_instance_of(
|
30
|
+
CacheCrispies::Plan
|
31
|
+
).to receive(:etag).and_return 'test-etag'
|
32
|
+
allow(subject).to receive_message_chain(:response, :weak_etag=).with 'test-etag'
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '#cache_render' do
|
36
|
+
it 'renders a json collection' do
|
37
|
+
expect(subject).to receive(:render).with(
|
38
|
+
json: {
|
39
|
+
cereals: cereal_names.map { |name| { name: name } }
|
40
|
+
}.to_json
|
41
|
+
)
|
42
|
+
|
43
|
+
subject.cache_render CerealSerializerForController, collection
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'renders a single json object' do
|
47
|
+
expect(subject).to receive(:render).with(
|
48
|
+
json: { cereal: { name: cereal_names.first } }.to_json
|
49
|
+
)
|
50
|
+
|
51
|
+
subject.cache_render CerealSerializerForController, collection.first
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CacheCrispies::HashBuilder do
|
4
|
+
# These example serializers are meant to show a variety of options,
|
5
|
+
# configurations, and data types in order to really put the HashBuilder class
|
6
|
+
# through the ringer.
|
7
|
+
class IngredientSerializer < CacheCrispies::Base
|
8
|
+
serialize :name
|
9
|
+
end
|
10
|
+
|
11
|
+
class MarketingBsSerializer < CacheCrispies::Base
|
12
|
+
serialize :tagline, :small_print
|
13
|
+
|
14
|
+
def tagline
|
15
|
+
"#{model.tagline}#{options[:footnote_marker]}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def small_print
|
19
|
+
"#{options[:footnote_marker]}this doesn't mean jack-squat"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class CerealSerializerForHashBuilder < CacheCrispies::Base
|
24
|
+
serialize :uid, from: :id, to: String
|
25
|
+
serialize :name, :company
|
26
|
+
merge :itself, with: MarketingBsSerializer
|
27
|
+
|
28
|
+
nest_in :about do
|
29
|
+
nest_in :nutritional_information do
|
30
|
+
serialize :calories
|
31
|
+
serialize :ingredients, with: IngredientSerializer
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
show_if ->(model, options) { options[:be_trendy] } do
|
36
|
+
nest_in :health do
|
37
|
+
serialize :organic
|
38
|
+
|
39
|
+
show_if ->(model) { model.organic } do
|
40
|
+
serialize :certification
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def certification
|
46
|
+
'Totally Not A Scam Certifiers Inc'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
let(:organic) { false }
|
51
|
+
let(:ingredients) {
|
52
|
+
[
|
53
|
+
OpenStruct.new(name: 'Sugar'),
|
54
|
+
OpenStruct.new(name: 'Other Kind of Sugar')
|
55
|
+
]
|
56
|
+
}
|
57
|
+
let(:model) {
|
58
|
+
OpenStruct.new(
|
59
|
+
id: 42,
|
60
|
+
name: 'Lucky Charms',
|
61
|
+
company: 'General Mills',
|
62
|
+
calories: 1_000,
|
63
|
+
organic: organic,
|
64
|
+
tagline: "Part of a balanced breakfast",
|
65
|
+
ingredients: ingredients
|
66
|
+
)
|
67
|
+
}
|
68
|
+
let(:options) { { footnote_marker: '*' } }
|
69
|
+
let(:serializer) { CerealSerializerForHashBuilder.new(model, options) }
|
70
|
+
subject { described_class.new(serializer) }
|
71
|
+
|
72
|
+
describe '#call' do
|
73
|
+
it 'correctly renders the hash' do
|
74
|
+
expect(subject.call).to eq ({
|
75
|
+
uid: '42',
|
76
|
+
name: 'Lucky Charms',
|
77
|
+
company: 'General Mills',
|
78
|
+
tagline: 'Part of a balanced breakfast*',
|
79
|
+
small_print: "*this doesn't mean jack-squat",
|
80
|
+
about: {
|
81
|
+
nutritional_information: {
|
82
|
+
calories: 1000,
|
83
|
+
ingredients: [
|
84
|
+
{ name: 'Sugar' },
|
85
|
+
{ name: 'Other Kind of Sugar' },
|
86
|
+
]
|
87
|
+
}
|
88
|
+
}
|
89
|
+
})
|
90
|
+
end
|
91
|
+
|
92
|
+
context 'when the outer show_if is true' do
|
93
|
+
let(:options) { { footnote_marker: '†', be_trendy: true } }
|
94
|
+
|
95
|
+
it 'builds values wrapped in the outer if' do
|
96
|
+
expect(subject.call).to eq ({
|
97
|
+
uid: '42',
|
98
|
+
name: 'Lucky Charms',
|
99
|
+
company: 'General Mills',
|
100
|
+
tagline: 'Part of a balanced breakfast†',
|
101
|
+
small_print: "†this doesn't mean jack-squat",
|
102
|
+
about: {
|
103
|
+
nutritional_information: {
|
104
|
+
calories: 1000,
|
105
|
+
ingredients: [
|
106
|
+
{ name: 'Sugar' },
|
107
|
+
{ name: 'Other Kind of Sugar' },
|
108
|
+
]
|
109
|
+
}
|
110
|
+
},
|
111
|
+
health: {
|
112
|
+
organic: false
|
113
|
+
}
|
114
|
+
})
|
115
|
+
end
|
116
|
+
|
117
|
+
context 'when the inner show_if is true' do
|
118
|
+
let(:organic) { true }
|
119
|
+
|
120
|
+
it 'builds values wrapped in the outer and inner if' do
|
121
|
+
expect(subject.call).to eq ({
|
122
|
+
uid: '42',
|
123
|
+
name: 'Lucky Charms',
|
124
|
+
company: 'General Mills',
|
125
|
+
tagline: 'Part of a balanced breakfast†',
|
126
|
+
small_print: "†this doesn't mean jack-squat",
|
127
|
+
about: {
|
128
|
+
nutritional_information: {
|
129
|
+
calories: 1000,
|
130
|
+
ingredients: [
|
131
|
+
{ name: 'Sugar' },
|
132
|
+
{ name: 'Other Kind of Sugar' },
|
133
|
+
]
|
134
|
+
}
|
135
|
+
},
|
136
|
+
health: {
|
137
|
+
organic: true,
|
138
|
+
certification: 'Totally Not A Scam Certifiers Inc'
|
139
|
+
}
|
140
|
+
})
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CacheCrispies::Memoizer do
|
4
|
+
describe '#fetch' do
|
5
|
+
it 'only calls the block once per key' do
|
6
|
+
block = -> {}
|
7
|
+
|
8
|
+
expect { |block| subject.fetch 1, &block }.to yield_with_no_args
|
9
|
+
expect { |block| subject.fetch 1, &block }.to_not yield_with_no_args
|
10
|
+
expect { |block| subject.fetch 2, &block }.to yield_with_no_args
|
11
|
+
end
|
12
|
+
|
13
|
+
it "returns the block's initial cached value" do
|
14
|
+
block = -> {
|
15
|
+
@num ||= 0
|
16
|
+
@num += 1
|
17
|
+
}
|
18
|
+
|
19
|
+
expect(subject.fetch(:a, &block)).to eq 1
|
20
|
+
expect(subject.fetch(:a, &block)).to eq 1
|
21
|
+
expect(subject.fetch(:b, &block)).to eq 2
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/spec/plan_spec.rb
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CacheCrispies::Plan do
|
4
|
+
class CerealSerializerForPlan < CacheCrispies::Base
|
5
|
+
def self.key
|
6
|
+
:cereal
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.cache_key_addons(options)
|
10
|
+
['addon1', options[:extra_addon]]
|
11
|
+
end
|
12
|
+
|
13
|
+
serialize :name
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:serializer) { CerealSerializerForPlan }
|
17
|
+
let(:serializer_file_path) {
|
18
|
+
File.expand_path('fixtures/test_serializer.rb', __dir__)
|
19
|
+
}
|
20
|
+
let(:model_cache_key) { 'model-cache-key' }
|
21
|
+
let(:model) { OpenStruct.new(name: 'Sugar Smacks', cache_key: model_cache_key) }
|
22
|
+
let(:cacheable) { model }
|
23
|
+
let(:options) { {} }
|
24
|
+
let(:instance) { described_class.new(serializer, cacheable, options) }
|
25
|
+
subject { instance }
|
26
|
+
|
27
|
+
before do
|
28
|
+
allow(Rails).to receive_message_chain(:root, :join).and_return(
|
29
|
+
serializer_file_path
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#collection?' do
|
34
|
+
context 'when not a collection' do
|
35
|
+
let(:cacheable) { Object.new }
|
36
|
+
|
37
|
+
it 'returns false' do
|
38
|
+
expect(subject.collection?).to be false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'when a collection' do
|
43
|
+
let(:cacheable) { [Object.new] }
|
44
|
+
|
45
|
+
it 'returns false' do
|
46
|
+
expect(subject.collection?).to be true
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '#etag' do
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
describe '#cache_key' do
|
56
|
+
let(:options) { { extra_addon: 'addon2' } }
|
57
|
+
|
58
|
+
it 'returns a string' do
|
59
|
+
expect(subject.cache_key).to be_a String
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'includes the CACHE_KEY_PREFIX' do
|
63
|
+
expect(subject.cache_key).to include CacheCrispies::CACHE_KEY_PREFIX
|
64
|
+
end
|
65
|
+
|
66
|
+
it "includes the serializer's #cache_key_base" do
|
67
|
+
expect(subject.cache_key).to include serializer.cache_key_base
|
68
|
+
end
|
69
|
+
|
70
|
+
it "includes the addons_key" do
|
71
|
+
expect(subject.cache_key).to include(
|
72
|
+
Digest::MD5.hexdigest('addon1|addon2')
|
73
|
+
)
|
74
|
+
end
|
75
|
+
|
76
|
+
it "includes the cacheable #cache_key" do
|
77
|
+
expect(subject.cache_key).to include model_cache_key
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'includes the CACHE_KEY_SEPARATOR' do
|
81
|
+
expect(subject.cache_key).to include CacheCrispies::CACHE_KEY_SEPARATOR
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'generates the key correctly' do
|
85
|
+
expect(subject.cache_key).to eq(
|
86
|
+
'cache-crispies' \
|
87
|
+
"+CerealSerializerForPlan-#{Digest::MD5.file(serializer_file_path)}" \
|
88
|
+
"+#{Digest::MD5.hexdigest('addon1|addon2')}" \
|
89
|
+
'+model-cache-key'
|
90
|
+
)
|
91
|
+
end
|
92
|
+
|
93
|
+
context 'without addons' do
|
94
|
+
it 'generates the key without that section' do
|
95
|
+
expect(serializer).to receive(:cache_key_addons).and_return []
|
96
|
+
|
97
|
+
expect(subject.cache_key).to eq(
|
98
|
+
'cache-crispies' \
|
99
|
+
"+CerealSerializerForPlan-#{Digest::MD5.file(serializer_file_path)}" \
|
100
|
+
'+model-cache-key'
|
101
|
+
)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe '#cache' do
|
107
|
+
end
|
108
|
+
|
109
|
+
describe '#wrap' do
|
110
|
+
let(:json_hash) { { name: 'Kix' } }
|
111
|
+
subject { instance.wrap(json_hash) }
|
112
|
+
|
113
|
+
context 'when the serializer has no key' do
|
114
|
+
before { expect(serializer).to receive(:key).and_return nil }
|
115
|
+
|
116
|
+
it 'returns the json Hash directly' do
|
117
|
+
expect(subject).to be json_hash
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
context 'when key is false' do
|
122
|
+
let(:options) { { key: false } }
|
123
|
+
|
124
|
+
it 'returns json_hash unchanged' do
|
125
|
+
expect(subject).to be json_hash
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
context 'with an optional key' do
|
130
|
+
let(:options) { { key: :cereal_test } }
|
131
|
+
|
132
|
+
it 'wraps the hash using the provided key option' do
|
133
|
+
expect(subject).to eq cereal_test: json_hash
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
context "when it's a colleciton" do
|
138
|
+
let(:cacheable) { [model] }
|
139
|
+
|
140
|
+
it "wraps the hash in the serializer's colleciton_key" do
|
141
|
+
expect(subject).to eq cereals: json_hash
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
context "when it's not a collection" do
|
146
|
+
it "wraps the hash in the serializer's key" do
|
147
|
+
expect(subject).to eq cereal: json_hash
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|