cache_crispies 1.3.2 → 1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1a93c5ab1a386306488b41307cb945164dcf1a0d0646cd5b02f828b87b2836d8
4
- data.tar.gz: dadb16037bb994b1a17157cc7568afbd962d446a007838ba003a0fea29e7745f
3
+ metadata.gz: bce114ae1c4a30b76f0f72c18996ade2d18bdf4efec52b3e3cf7d889a4c7b444
4
+ data.tar.gz: d42c3daa8142d5f0dea1ae43890f67d232064ca2c505bca4a3edac9c4f282980
5
5
  SHA512:
6
- metadata.gz: '078816fce3ff0dc88fd45dd378dec482843229bdc28b83c188dd0ae04e984b18b14e81ab1d62f5930e26d3724133df3b6aac2033fbd647acad278409ce77c47b'
7
- data.tar.gz: 26ec8aa1c7f4a55c1e58b76606ca0cafb1384ce2579b0131d3f9a2fa09391da8c0882524a379420c92c13eda3e0173b2c4fbad3356bdd529eb35c1beb1348e18
6
+ metadata.gz: 7deb2b2f954c8cb56fdf6bd23f7b99d441ec45252d5ea7a5012b6bac04fe67ff6ba38ef640d803e285aafa77c6b2f626cce9726a8542c7e1d9c895a4543c1928
7
+ data.tar.gz: 6d44ca0bb510c8a10ac14fea8c1b30333e73e9f060446aaa6c31c00c6a6e0e66f8a41c9fae05d872c233dbb4850f81e22f624fa258fb80a9c4059b5c900f2789
@@ -63,18 +63,29 @@ module CacheCrispies
63
63
  # @raise [InvalidCoercionType] when an invalid argument is passed in the
64
64
  # to: argument
65
65
  def value_for(target, options)
66
- value =
67
- if block?
68
- block.call(target, options)
69
- elsif through?
70
- target.public_send(through)&.public_send(method_name)
71
- else
72
- target.public_send(method_name)
73
- end
66
+ value = retrieve_value(target, options)
74
67
 
75
68
  serializer ? serialize(value, options) : coerce(value)
76
69
  end
77
70
 
71
+ # Writes a key-value pair of the attribute for the given target object
72
+ # and options into the given Oj::StringWriter
73
+ #
74
+ # @param json_writer [Oj::StringWriter] any Oj::StringWriter instance
75
+ # @param target [Object] typically ActiveRecord::Base, but could be anything
76
+ # @param options [Hash] any optional values from the serializer instance
77
+ # @raise [InvalidCoercionType] when an invalid argument is passed in the
78
+ # to: argument
79
+ def write_to_json(json_writer, target, options)
80
+ value = retrieve_value(target, options)
81
+
82
+ if serializer
83
+ write_serialized_value(json_writer, value, options)
84
+ else
85
+ json_writer.push_value(coerce(value), key.to_s)
86
+ end
87
+ end
88
+
78
89
  private
79
90
 
80
91
  def through?
@@ -85,6 +96,16 @@ module CacheCrispies
85
96
  !block.nil?
86
97
  end
87
98
 
99
+ def retrieve_value(target, options)
100
+ if block?
101
+ block.call(target, options)
102
+ elsif through?
103
+ target.public_send(through)&.public_send(method_name)
104
+ else
105
+ target.public_send(method_name)
106
+ end
107
+ end
108
+
88
109
  # Here we'll render the attribute with a given serializer and attempt to
89
110
  # cache the results for better cache reusability
90
111
  def serialize(value, options)
@@ -99,6 +120,20 @@ module CacheCrispies
99
120
  end
100
121
  end
101
122
 
123
+ def write_serialized_value(json_writer, value, options)
124
+ plan = CacheCrispies::Plan.new(
125
+ serializer, value, collection: collection, **options
126
+ )
127
+
128
+ json_writer.push_key(key.to_s) if key
129
+
130
+ if key && plan.collection?
131
+ Collection.new(value, serializer, options).write_to_json(json_writer)
132
+ else
133
+ JsonBuilder.new(serializer.new(value, options)).call(json_writer, flat: !key)
134
+ end
135
+ end
136
+
102
137
  def coerce(value)
103
138
  return value if coerce_to.nil?
104
139
 
@@ -43,6 +43,18 @@ module CacheCrispies
43
43
  HashBuilder.new(self).call
44
44
  end
45
45
 
46
+ # Renders the serializer instance to an Oj::StringWriter instance
47
+ #
48
+ # @return [Oj::StringWriter] an Oj::StringWriter instance with the
49
+ # serialized content
50
+ def write_to_json(json_writer = nil)
51
+ json_writer ||= Oj::StringWriter.new(mode: :rails)
52
+
53
+ JsonBuilder.new(self).call(json_writer)
54
+
55
+ json_writer
56
+ end
57
+
46
58
  # Get or set whether or not this serializer class should allow caching of
47
59
  # results. It returns false by default, but can be overridden in child
48
60
  # classes. Calling the method with an argument will set the value, calling
@@ -64,6 +76,21 @@ module CacheCrispies
64
76
  alias do_caching? do_caching
65
77
  end
66
78
 
79
+ # Get or set root path of Rails application or engine.
80
+ # It uses Rails by default, but can be overriden with your Rails Engine class.
81
+ # Calling the method with an argument will set the value, calling it without
82
+ # any arguments will get the value.
83
+ def self.engine(value = nil)
84
+ @engine ||= superclass.try(:engine)
85
+ @engine ||= Rails
86
+
87
+ # method called with no args so act as a getter
88
+ return @engine if value.nil?
89
+
90
+ # method called with args so act as a setter
91
+ @engine = value
92
+ end
93
+
67
94
  # Get or set a JSON key to use as a root key on a non-collection
68
95
  # serializable. By default it's the name of the class without the
69
96
  # "Serializer" part. But it can be overridden in a subclass to be anything.
@@ -178,6 +205,13 @@ module CacheCrispies
178
205
  ).uniq.sort
179
206
  end
180
207
 
208
+ def self.attributes_by_nesting
209
+ @attributes_by_nesting ||= (
210
+ attributes.sort_by(&:nesting).group_by(&:nesting)
211
+ )
212
+ end
213
+ delegate :attributes_by_nesting, to: :class
214
+
181
215
  private
182
216
 
183
217
  def self.file_hash
@@ -190,7 +224,7 @@ module CacheCrispies
190
224
  parts = %w[app serializers]
191
225
  parts += to_s.deconstantize.split('::').map(&:underscore)
192
226
  parts << "#{to_s.demodulize.underscore}.rb"
193
- Rails.root.join(*parts)
227
+ engine.root.join(*parts)
194
228
  end
195
229
  end
196
230
  private_class_method :path
@@ -29,6 +29,22 @@ module CacheCrispies
29
29
  end
30
30
  end
31
31
 
32
+ # Renders the collection to an Oj::StringWriter instance without caching
33
+ #
34
+ # @return [Oj::StringWriter] an Oj::StringWriter instance with the
35
+ # serialized content
36
+ def write_to_json(json_writer = nil)
37
+ json_writer ||= Oj::StringWriter.new(mode: :rails)
38
+ json_writer.push_array
39
+
40
+ collection.each do |model|
41
+ serializer.new(model, options).write_to_json(json_writer)
42
+ end
43
+
44
+ json_writer.pop
45
+ json_writer
46
+ end
47
+
32
48
  private
33
49
 
34
50
  attr_reader :collection, :serializer, :options
@@ -30,7 +30,7 @@ module CacheCrispies
30
30
  def cache_render(
31
31
  serializer,
32
32
  cacheable,
33
- key: nil, collection: nil, status: nil,
33
+ key: UNDEFINED, collection: nil, status: nil,
34
34
  meta: {}, meta_key: :meta,
35
35
  **options
36
36
  )
@@ -16,7 +16,7 @@ module CacheCrispies
16
16
  #
17
17
  # @return [Hash]
18
18
  def call
19
- return unless @serializer.model
19
+ return if @serializer.model.nil?
20
20
 
21
21
  hash = {}
22
22
 
@@ -42,7 +42,7 @@ module CacheCrispies
42
42
  hash
43
43
  end
44
44
 
45
- private
45
+ protected
46
46
 
47
47
  attr_reader :serializer, :condition_results
48
48
 
@@ -56,18 +56,19 @@ module CacheCrispies
56
56
  end
57
57
  end
58
58
 
59
- def value_for(attribute)
59
+ def target_for(attribute)
60
60
  meth = attribute.method_name
61
61
 
62
- target =
63
- if meth != :itself && serializer.respond_to?(meth)
64
- serializer
65
- else
66
- serializer.model
67
- end
62
+ if serializer.respond_to?(meth) && meth != :itself
63
+ serializer
64
+ else
65
+ serializer.model
66
+ end
67
+ end
68
68
 
69
+ def value_for(attribute)
69
70
  # TODO: rescue NoMethodErrors here with something more telling
70
- attribute.value_for(target, serializer.options)
71
+ attribute.value_for(target_for(attribute), serializer.options)
71
72
  end
72
73
  end
73
74
  end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CacheCrispies
4
+ # Builds out a JSON string directly with Oj::StringWriter
5
+ class JsonBuilder < HashBuilder
6
+ # Builds the JSON string with Oj::StringWriter
7
+ #
8
+ # @return [Oj::StringWriter]
9
+ def call(json_writer, flat: false)
10
+ if @serializer.model.nil?
11
+ json_writer.push_value(nil)
12
+ return
13
+ end
14
+
15
+ json_writer.push_object unless flat
16
+
17
+ last_nesting = []
18
+
19
+ serializer.attributes_by_nesting.each do |nesting, attributes|
20
+ prefix_length = common_prefix_length(last_nesting, nesting)
21
+
22
+ (last_nesting.length - prefix_length).times do
23
+ json_writer.pop
24
+ end
25
+
26
+ nesting[prefix_length..-1].each do |key|
27
+ json_writer.push_object(key.to_s)
28
+ end
29
+
30
+ attributes.each do |attrib|
31
+ write_attribute(json_writer, attrib) if show?(attrib)
32
+ end
33
+
34
+ last_nesting = nesting
35
+ end
36
+
37
+ last_nesting.each do
38
+ json_writer.pop
39
+ end
40
+
41
+ json_writer.pop unless flat
42
+
43
+ json_writer
44
+ end
45
+
46
+ protected
47
+
48
+ def write_attribute(json_writer, attribute)
49
+ # TODO: rescue NoMethodErrors here with something more telling
50
+ attribute.write_to_json(
51
+ json_writer,
52
+ target_for(attribute),
53
+ serializer.options
54
+ )
55
+ end
56
+
57
+ private
58
+
59
+ def common_prefix_length(array1, array2)
60
+ shorter_length = [array1.length, array2.length].min
61
+
62
+ (0...shorter_length).each do |i|
63
+ return i if array1[i] != array2[i]
64
+ end
65
+
66
+ shorter_length
67
+ end
68
+ end
69
+ end
@@ -16,7 +16,7 @@ module CacheCrispies
16
16
  # data under
17
17
  # @option options [Boolean] :collection whether to render the data as a
18
18
  # collection/array or a single object
19
- def initialize(serializer, cacheable, key: nil, collection: nil, **options)
19
+ def initialize(serializer, cacheable, key: UNDEFINED, collection: nil, **options)
20
20
  @serializer = serializer
21
21
  @cacheable = cacheable
22
22
 
@@ -91,7 +91,7 @@ module CacheCrispies
91
91
  private
92
92
 
93
93
  def key
94
- return @key unless @key.nil?
94
+ return @key unless @key == UNDEFINED
95
95
 
96
96
  (collection? ? serializer.collection_key : serializer.key)
97
97
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module CacheCrispies
4
4
  # The version of the gem
5
- VERSION = '1.3.2'
5
+ VERSION = '1.5.0'
6
6
  end
@@ -12,6 +12,9 @@ module CacheCrispies
12
12
  # The string to use to join parts of the cache keys together
13
13
  CACHE_KEY_SEPARATOR = '+'
14
14
 
15
+ # Magic value for undefined arguments
16
+ UNDEFINED = Object.new.freeze
17
+
15
18
  require 'cache_crispies/version'
16
19
 
17
20
  # Use autoload for better Rails development
@@ -22,6 +25,7 @@ module CacheCrispies
22
25
  autoload :Optional, 'cache_crispies/optional'
23
26
  autoload :Configuration, 'cache_crispies/configuration'
24
27
  autoload :HashBuilder, 'cache_crispies/hash_builder'
28
+ autoload :JsonBuilder, 'cache_crispies/json_builder'
25
29
  autoload :Memoizer, 'cache_crispies/memoizer'
26
30
  autoload :Controller, 'cache_crispies/controller'
27
31
  autoload :Plan, 'cache_crispies/plan'
@@ -39,6 +39,15 @@ describe CacheCrispies::Base do
39
39
  end
40
40
  end
41
41
 
42
+ class MyEngine
43
+ def self.root
44
+ end
45
+ end
46
+
47
+ class CacheCrispiesEngineTestSerializer < CacheCrispiesTestSerializer
48
+ engine MyEngine
49
+ end
50
+
42
51
  let(:model) do
43
52
  OpenStruct.new(
44
53
  id: 42,
@@ -52,6 +61,7 @@ describe CacheCrispies::Base do
52
61
  end
53
62
 
54
63
  let(:serializer) { CacheCrispiesTestSerializer }
64
+ let(:engine_serializer) { CacheCrispiesEngineTestSerializer }
55
65
  let(:instance) { serializer.new(model) }
56
66
  subject { instance }
57
67
 
@@ -95,6 +105,50 @@ describe CacheCrispies::Base do
95
105
  end
96
106
  end
97
107
 
108
+ describe '#write_to_json' do
109
+ let(:recovered_hash) { Oj.compat_load(subject.write_to_json.to_s, symbol_keys: true) }
110
+
111
+ it 'serializes to an Oj::StringWriter' do
112
+ expect(subject.write_to_json).to be_kind_of(Oj::StringWriter)
113
+
114
+ expect(recovered_hash).to eq(
115
+ id: '42',
116
+ name: 'Cookie Crisp',
117
+ company: 'General Mills',
118
+ nested: {
119
+ nested_again: {
120
+ deeply_nested: 'TRUE'
121
+ }
122
+ },
123
+ nutrition_info: {
124
+ calories: 1000
125
+ },
126
+ organic: true,
127
+ parent_company: 'Disney probably'
128
+ )
129
+ end
130
+
131
+ context 'when nutrition_info is nil' do
132
+ before { model.nutrition_info = nil }
133
+
134
+ it 'serializes to an Oj::StringWriter' do
135
+ expect(recovered_hash).to eq(
136
+ id: '42',
137
+ name: 'Cookie Crisp',
138
+ company: 'General Mills',
139
+ nested: {
140
+ nested_again: {
141
+ deeply_nested: 'TRUE'
142
+ }
143
+ },
144
+ nutrition_info: nil,
145
+ organic: true,
146
+ parent_company: 'Disney probably'
147
+ )
148
+ end
149
+ end
150
+ end
151
+
98
152
  describe '.key' do
99
153
  it 'underscores the demodulized class name by default' do
100
154
  expect(serializer.key).to eq :cache_crispies_test
@@ -161,6 +215,24 @@ describe CacheCrispies::Base do
161
215
  end
162
216
  end
163
217
 
218
+ describe '.engine' do
219
+ it 'defaults to Rails' do
220
+ expect(serializer.engine).to eq Rails
221
+ end
222
+
223
+ context 'when called with an argument' do
224
+ after { serializer.remove_instance_variable :@engine }
225
+
226
+ it 'sets and returns a value' do
227
+ expect {
228
+ serializer.engine MyEngine
229
+ }.to change {
230
+ serializer.engine
231
+ }.from(Rails).to MyEngine
232
+ end
233
+ end
234
+ end
235
+
164
236
  describe '.cache_key_addons' do
165
237
  it 'returns an empty array by default' do
166
238
  expect(serializer.cache_key_addons).to eq []
@@ -203,6 +275,12 @@ describe CacheCrispies::Base do
203
275
  let(:serializer_file_digest) {
204
276
  Digest::MD5.file(serializer_file_path).to_s
205
277
  }
278
+ let(:engine_serializer_file_path) {
279
+ File.expand_path('../fixtures/engine_test_serializer.rb', __dir__)
280
+ }
281
+ let(:engine_serializer_file_digest) {
282
+ Digest::MD5.file(engine_serializer_file_path).to_s
283
+ }
206
284
 
207
285
  before do
208
286
  allow(NutritionSerializer).to receive(:file_hash).and_return(
@@ -211,6 +289,9 @@ describe CacheCrispies::Base do
211
289
  allow(Rails).to receive_message_chain(:root, :join).and_return(
212
290
  serializer_file_path
213
291
  )
292
+ allow(MyEngine).to receive_message_chain(:root, :join).and_return(
293
+ engine_serializer_file_path
294
+ )
214
295
  end
215
296
 
216
297
  it 'includes the file name' do
@@ -230,6 +311,12 @@ describe CacheCrispies::Base do
230
311
  "#{serializer}-#{serializer_file_digest}+#{nested_serializer_digest}"
231
312
  )
232
313
  end
314
+
315
+ context 'when an engine is set' do
316
+ it "includes a digest of the serializer class file's contents" do
317
+ expect(engine_serializer.cache_key_base).to include engine_serializer_file_digest
318
+ end
319
+ end
233
320
  end
234
321
 
235
322
  describe '.attributes' do
@@ -80,4 +80,12 @@ describe CacheCrispies::Collection do
80
80
  end
81
81
  end
82
82
  end
83
+
84
+ describe '#write_to_json' do
85
+ let(:recovered_hash) { Oj.compat_load(subject.write_to_json.to_s, symbol_keys: true) }
86
+
87
+ it 'serializes to an Oj::StringWriter' do
88
+ expect(recovered_hash).to eq [{ name: name1 }, { name: name2 }]
89
+ end
90
+ end
83
91
  end
@@ -38,7 +38,7 @@ describe CacheCrispies::Controller do
38
38
 
39
39
  context 'with etags disabled' do
40
40
  it 'does not set etags' do
41
- expect(subject).to receive(:render).with json: collection_json
41
+ expect(subject).to receive(:render).with({ json: collection_json })
42
42
  response_double = double
43
43
  allow(subject).to receive(:response).and_return response_double
44
44
  expect(response_double).to_not receive(:weak_etag=)
@@ -51,7 +51,7 @@ describe CacheCrispies::Controller do
51
51
  let(:etags) { true }
52
52
 
53
53
  it 'sets etags' do
54
- expect(subject).to receive(:render).with json: collection_json
54
+ expect(subject).to receive(:render).with({ json: collection_json })
55
55
  expect_any_instance_of(
56
56
  CacheCrispies::Plan
57
57
  ).to receive(:etag).and_return 'test-etag'
@@ -62,23 +62,53 @@ describe CacheCrispies::Controller do
62
62
  end
63
63
 
64
64
  it 'renders a json collection' do
65
- expect(subject).to receive(:render).with json: collection_json
65
+ expect(subject).to receive(:render).with({ json: collection_json })
66
66
 
67
67
  subject.cache_render CerealSerializerForController, collection
68
68
  end
69
69
 
70
70
  it 'renders a single json object' do
71
- expect(subject).to receive(:render).with json: single_json
71
+ expect(subject).to receive(:render).with({ json: single_json })
72
72
 
73
73
  subject.cache_render CerealSerializerForController, collection.first
74
74
  end
75
75
 
76
+ context 'with a key: option'do
77
+ context 'overriding the key in the serializer' do
78
+ it 'renders a json collection' do
79
+ expect(subject).to receive(:render).with({ json: { serials: cereal_names.map { |name| { name: name } } }.to_json })
80
+
81
+ subject.cache_render CerealSerializerForController, collection, key: "serials"
82
+ end
83
+
84
+ it 'renders a single json object' do
85
+ expect(subject).to receive(:render).with({ json: { serial: { name: cereal_names.first} }.to_json })
86
+
87
+ subject.cache_render CerealSerializerForController, collection.first, key: "serial"
88
+ end
89
+ end
90
+
91
+ context 'set to nil' do
92
+ it 'renders a json collection' do
93
+ expect(subject).to receive(:render).with({ json: cereal_names.map { |name| { name: name } }.to_json })
94
+
95
+ subject.cache_render CerealSerializerForController, collection, key: nil
96
+ end
97
+
98
+ it 'renders a single json object' do
99
+ expect(subject).to receive(:render).with({ json: { name: cereal_names.first }.to_json })
100
+
101
+ subject.cache_render CerealSerializerForController, collection.first, key: nil
102
+ end
103
+ end
104
+ end
105
+
76
106
  context 'with a status: option' do
77
107
  it 'passes the status option to the Rails render call' do
78
- expect(subject).to receive(:render).with(
108
+ expect(subject).to receive(:render).with({
79
109
  json: single_json,
80
110
  status: 418
81
- )
111
+ })
82
112
 
83
113
  subject.cache_render(
84
114
  CerealSerializerForController,
@@ -90,9 +120,9 @@ describe CacheCrispies::Controller do
90
120
 
91
121
  context 'with a meta: option' do
92
122
  it 'adds a meta data hash to the JSON' do
93
- expect(subject).to receive(:render).with(
123
+ expect(subject).to receive(:render).with({
94
124
  json: single_hash.merge(meta: { page: 42 }).to_json
95
- )
125
+ })
96
126
 
97
127
  subject.cache_render(
98
128
  CerealSerializerForController,
@@ -104,9 +134,9 @@ describe CacheCrispies::Controller do
104
134
 
105
135
  context 'with a meta_key: option' do
106
136
  it 'adds a meta data hash to the JSON with the provided key' do
107
- expect(subject).to receive(:render).with(
137
+ expect(subject).to receive(:render).with({
108
138
  json: single_hash.merge(test_meta_data: { page: 42 }).to_json
109
- )
139
+ })
110
140
 
111
141
  subject.cache_render(
112
142
  CerealSerializerForController,
@@ -0,0 +1,218 @@
1
+ require 'spec_helper'
2
+
3
+ describe CacheCrispies::JsonBuilder 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 ConstituentSerializer < CacheCrispies::Base
8
+ serialize :name
9
+ end
10
+
11
+ class AllergenSerializer < CacheCrispies::Base
12
+ serialize :name
13
+ end
14
+
15
+ class BuzzwordSerializer < CacheCrispies::Base
16
+ serialize :tagline, :small_print
17
+
18
+ def tagline
19
+ "#{model.tagline}#{options[:footnote_marker]}"
20
+ end
21
+
22
+ def small_print
23
+ "#{options[:footnote_marker]}this doesn't mean jack-squat"
24
+ end
25
+ end
26
+
27
+ class CerealSerializerForJsonBuilder < CacheCrispies::Base
28
+ serialize :uid, from: :id, to: String
29
+ serialize :name, :company
30
+ merge :itself, with: BuzzwordSerializer
31
+
32
+ nest_in :about do
33
+ nest_in :nutritional_information do
34
+ serialize :calories
35
+ serialize :ingredients, with: ConstituentSerializer
36
+
37
+ serialize :allergies, with: AllergenSerializer, optional: true
38
+ end
39
+ end
40
+
41
+ show_if ->(_model, options) { options[:be_trendy] } do
42
+ nest_in :health do
43
+ serialize :organic
44
+
45
+ show_if ->(model) { model.organic } do
46
+ serialize :certification
47
+ end
48
+ end
49
+ end
50
+
51
+ def certification
52
+ 'Totally Not A Scam Certifiers Inc'
53
+ end
54
+ end
55
+
56
+ let(:organic) { false }
57
+ let(:ingredients) {
58
+ [
59
+ OpenStruct.new(name: 'Sugar'),
60
+ OpenStruct.new(name: 'Other Kind of Sugar')
61
+ ]
62
+ }
63
+ let(:allergies) {
64
+ [
65
+ OpenStruct.new(name: 'Peanuts'),
66
+ OpenStruct.new(name: 'Lactose')
67
+ ]
68
+ }
69
+ let(:model) {
70
+ OpenStruct.new(
71
+ id: 42,
72
+ name: 'Lucky Charms',
73
+ company: 'General Mills',
74
+ calories: 1_000,
75
+ organic: organic,
76
+ tagline: "Part of a balanced breakfast",
77
+ ingredients: ingredients,
78
+ allergies: allergies
79
+ )
80
+ }
81
+ let(:options) { { footnote_marker: '*' } }
82
+ let(:serializer) { CerealSerializerForJsonBuilder.new(model, options) }
83
+ let(:json_writer) { Oj::StringWriter.new(mode: :rails) }
84
+ subject { described_class.new(serializer) }
85
+
86
+ describe '#call' do
87
+ let(:recovered_hash) { Oj.compat_load(subject.call(json_writer).to_s, symbol_keys: true) }
88
+
89
+ it 'correctly renders the hash' do
90
+ expect(recovered_hash).to eq ({
91
+ uid: '42',
92
+ name: 'Lucky Charms',
93
+ company: 'General Mills',
94
+ tagline: 'Part of a balanced breakfast*',
95
+ small_print: "*this doesn't mean jack-squat",
96
+ about: {
97
+ nutritional_information: {
98
+ calories: 1000,
99
+ ingredients: [
100
+ { name: 'Sugar' },
101
+ { name: 'Other Kind of Sugar' },
102
+ ]
103
+ }
104
+ },
105
+ health: {}
106
+ })
107
+ end
108
+
109
+ context 'when the outer show_if is true' do
110
+ let(:options) { { footnote_marker: '†', be_trendy: true } }
111
+
112
+ it 'builds values wrapped in the outer if' do
113
+ expect(recovered_hash).to eq ({
114
+ uid: '42',
115
+ name: 'Lucky Charms',
116
+ company: 'General Mills',
117
+ tagline: 'Part of a balanced breakfast†',
118
+ small_print: "†this doesn't mean jack-squat",
119
+ about: {
120
+ nutritional_information: {
121
+ calories: 1000,
122
+ ingredients: [
123
+ { name: 'Sugar' },
124
+ { name: 'Other Kind of Sugar' },
125
+ ]
126
+ }
127
+ },
128
+ health: {
129
+ organic: false
130
+ }
131
+ })
132
+ end
133
+
134
+ context 'when the inner show_if is true' do
135
+ let(:organic) { true }
136
+
137
+ it 'builds values wrapped in the outer and inner if' do
138
+ expect(recovered_hash).to eq ({
139
+ uid: '42',
140
+ name: 'Lucky Charms',
141
+ company: 'General Mills',
142
+ tagline: 'Part of a balanced breakfast†',
143
+ small_print: "†this doesn't mean jack-squat",
144
+ about: {
145
+ nutritional_information: {
146
+ calories: 1000,
147
+ ingredients: [
148
+ { name: 'Sugar' },
149
+ { name: 'Other Kind of Sugar' },
150
+ ]
151
+ }
152
+ },
153
+ health: {
154
+ organic: true,
155
+ certification: 'Totally Not A Scam Certifiers Inc'
156
+ }
157
+ })
158
+ end
159
+ end
160
+ end
161
+
162
+ context 'when allergies are included' do
163
+ let(:options) { { footnote_marker: '*', include: :allergies } }
164
+
165
+ it 'includes the allergies' do
166
+ expect(recovered_hash).to eq ({
167
+ uid: '42',
168
+ name: 'Lucky Charms',
169
+ company: 'General Mills',
170
+ tagline: 'Part of a balanced breakfast*',
171
+ small_print: "*this doesn't mean jack-squat",
172
+ about: {
173
+ nutritional_information: {
174
+ calories: 1000,
175
+ ingredients: [
176
+ { name: 'Sugar' },
177
+ { name: 'Other Kind of Sugar' },
178
+ ],
179
+ allergies: [
180
+ { name: 'Peanuts' },
181
+ { name: 'Lactose' },
182
+ ]
183
+ }
184
+ },
185
+ health: {}
186
+ })
187
+ end
188
+ end
189
+
190
+ context 'when everything is included' do
191
+ let(:options) { { footnote_marker: '*', include: '*' } }
192
+
193
+ it 'includes the allergies' do
194
+ expect(recovered_hash).to eq ({
195
+ uid: '42',
196
+ name: 'Lucky Charms',
197
+ company: 'General Mills',
198
+ tagline: 'Part of a balanced breakfast*',
199
+ small_print: "*this doesn't mean jack-squat",
200
+ about: {
201
+ nutritional_information: {
202
+ calories: 1000,
203
+ ingredients: [
204
+ { name: 'Sugar' },
205
+ { name: 'Other Kind of Sugar' },
206
+ ],
207
+ allergies: [
208
+ { name: 'Peanuts' },
209
+ { name: 'Lactose' },
210
+ ]
211
+ }
212
+ },
213
+ health: {}
214
+ })
215
+ end
216
+ end
217
+ end
218
+ end
@@ -0,0 +1,3 @@
1
+ # Nothing has to actually be in this file.
2
+ # It's just here so test can MD5 sum it's content.
3
+ # This is an alternative serializer file.
data/spec/spec_helper.rb CHANGED
@@ -1,7 +1,22 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'simplecov'
2
- SimpleCov.start
4
+ require 'simplecov-lcov'
5
+
6
+ SimpleCov::Formatter::LcovFormatter.config.report_with_single_file = true
7
+
8
+ SimpleCov.start do
9
+ enable_coverage :branch
10
+ formatter(
11
+ SimpleCov::Formatter::MultiFormatter.new(
12
+ [
13
+ SimpleCov::Formatter::HTMLFormatter,
14
+ SimpleCov::Formatter::LcovFormatter
15
+ ]
16
+ )
17
+ )
18
+ end
3
19
 
4
- require 'byebug'
5
20
  require_relative '../lib/cache_crispies'
6
21
 
7
22
  RSpec.configure do |config|
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cache_crispies
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.2
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adam Crownoble
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2022-06-07 00:00:00.000000000 Z
10
+ date: 2025-03-23 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: railties
@@ -16,20 +15,20 @@ dependencies:
16
15
  requirements:
17
16
  - - ">="
18
17
  - !ruby/object:Gem::Version
19
- version: 5.0.0
18
+ version: 7.0.0
20
19
  - - "<"
21
20
  - !ruby/object:Gem::Version
22
- version: '8.0'
21
+ version: '9.0'
23
22
  type: :runtime
24
23
  prerelease: false
25
24
  version_requirements: !ruby/object:Gem::Requirement
26
25
  requirements:
27
26
  - - ">="
28
27
  - !ruby/object:Gem::Version
29
- version: 5.0.0
28
+ version: 7.0.0
30
29
  - - "<"
31
30
  - !ruby/object:Gem::Version
32
- version: '8.0'
31
+ version: '9.0'
33
32
  - !ruby/object:Gem::Dependency
34
33
  name: oj
35
34
  requirement: !ruby/object:Gem::Requirement
@@ -50,105 +49,132 @@ dependencies:
50
49
  requirements:
51
50
  - - ">="
52
51
  - !ruby/object:Gem::Version
53
- version: 5.0.0
52
+ version: 7.0.0
54
53
  - - "<"
55
54
  - !ruby/object:Gem::Version
56
- version: '8.0'
55
+ version: '9.0'
57
56
  type: :development
58
57
  prerelease: false
59
58
  version_requirements: !ruby/object:Gem::Requirement
60
59
  requirements:
61
60
  - - ">="
62
61
  - !ruby/object:Gem::Version
63
- version: 5.0.0
62
+ version: 7.0.0
64
63
  - - "<"
65
64
  - !ruby/object:Gem::Version
66
- version: '8.0'
65
+ version: '9.0'
67
66
  - !ruby/object:Gem::Dependency
68
67
  name: appraisal
69
68
  requirement: !ruby/object:Gem::Requirement
70
69
  requirements:
71
70
  - - "~>"
72
71
  - !ruby/object:Gem::Version
73
- version: '2.2'
72
+ version: '2.5'
74
73
  type: :development
75
74
  prerelease: false
76
75
  version_requirements: !ruby/object:Gem::Requirement
77
76
  requirements:
78
77
  - - "~>"
79
78
  - !ruby/object:Gem::Version
80
- version: '2.2'
79
+ version: '2.5'
81
80
  - !ruby/object:Gem::Dependency
82
- name: bundler
81
+ name: base64
83
82
  requirement: !ruby/object:Gem::Requirement
84
83
  requirements:
85
84
  - - "~>"
86
85
  - !ruby/object:Gem::Version
87
- version: '2.0'
86
+ version: '0.2'
88
87
  type: :development
89
88
  prerelease: false
90
89
  version_requirements: !ruby/object:Gem::Requirement
91
90
  requirements:
92
91
  - - "~>"
93
92
  - !ruby/object:Gem::Version
94
- version: '2.0'
93
+ version: '0.2'
95
94
  - !ruby/object:Gem::Dependency
96
- name: byebug
95
+ name: debug
97
96
  requirement: !ruby/object:Gem::Requirement
98
97
  requirements:
99
98
  - - "~>"
100
99
  - !ruby/object:Gem::Version
101
- version: '11.0'
100
+ version: '1.10'
102
101
  type: :development
103
102
  prerelease: false
104
103
  version_requirements: !ruby/object:Gem::Requirement
105
104
  requirements:
106
105
  - - "~>"
107
106
  - !ruby/object:Gem::Version
108
- version: '11.0'
107
+ version: '1.10'
109
108
  - !ruby/object:Gem::Dependency
110
- name: rspec
109
+ name: mutex_m
111
110
  requirement: !ruby/object:Gem::Requirement
112
111
  requirements:
113
112
  - - "~>"
114
113
  - !ruby/object:Gem::Version
115
- version: 3.8.0
114
+ version: '0.3'
116
115
  type: :development
117
116
  prerelease: false
118
117
  version_requirements: !ruby/object:Gem::Requirement
119
118
  requirements:
120
119
  - - "~>"
121
120
  - !ruby/object:Gem::Version
122
- version: 3.8.0
121
+ version: '0.3'
123
122
  - !ruby/object:Gem::Dependency
124
123
  name: rspec_junit_formatter
125
124
  requirement: !ruby/object:Gem::Requirement
126
125
  requirements:
127
126
  - - "~>"
128
127
  - !ruby/object:Gem::Version
129
- version: '0.4'
128
+ version: '0.6'
129
+ type: :development
130
+ prerelease: false
131
+ version_requirements: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - "~>"
134
+ - !ruby/object:Gem::Version
135
+ version: '0.6'
136
+ - !ruby/object:Gem::Dependency
137
+ name: rspec
138
+ requirement: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - "~>"
141
+ - !ruby/object:Gem::Version
142
+ version: 3.13.0
130
143
  type: :development
131
144
  prerelease: false
132
145
  version_requirements: !ruby/object:Gem::Requirement
133
146
  requirements:
134
147
  - - "~>"
135
148
  - !ruby/object:Gem::Version
136
- version: '0.4'
149
+ version: 3.13.0
137
150
  - !ruby/object:Gem::Dependency
138
151
  name: simplecov
139
152
  requirement: !ruby/object:Gem::Requirement
140
153
  requirements:
141
- - - '='
154
+ - - "~>"
142
155
  - !ruby/object:Gem::Version
143
- version: '0.17'
156
+ version: '0.22'
144
157
  type: :development
145
158
  prerelease: false
146
159
  version_requirements: !ruby/object:Gem::Requirement
147
160
  requirements:
148
- - - '='
161
+ - - "~>"
149
162
  - !ruby/object:Gem::Version
150
- version: '0.17'
151
- description:
163
+ version: '0.22'
164
+ - !ruby/object:Gem::Dependency
165
+ name: simplecov-lcov
166
+ requirement: !ruby/object:Gem::Requirement
167
+ requirements:
168
+ - - "~>"
169
+ - !ruby/object:Gem::Version
170
+ version: '0.8'
171
+ type: :development
172
+ prerelease: false
173
+ version_requirements: !ruby/object:Gem::Requirement
174
+ requirements:
175
+ - - "~>"
176
+ - !ruby/object:Gem::Version
177
+ version: '0.8'
152
178
  email: adam@codenoble.com
153
179
  executables: []
154
180
  extensions: []
@@ -163,6 +189,7 @@ files:
163
189
  - lib/cache_crispies/configuration.rb
164
190
  - lib/cache_crispies/controller.rb
165
191
  - lib/cache_crispies/hash_builder.rb
192
+ - lib/cache_crispies/json_builder.rb
166
193
  - lib/cache_crispies/memoizer.rb
167
194
  - lib/cache_crispies/optional.rb
168
195
  - lib/cache_crispies/plan.rb
@@ -174,16 +201,18 @@ files:
174
201
  - spec/cache_crispies/configuration_spec.rb
175
202
  - spec/cache_crispies/controller_spec.rb
176
203
  - spec/cache_crispies/hash_builder_spec.rb
204
+ - spec/cache_crispies/json_builder_spec.rb
177
205
  - spec/cache_crispies/memoizer_spec.rb
178
206
  - spec/cache_crispies/plan_spec.rb
179
207
  - spec/cache_crispies_spec.rb
208
+ - spec/fixtures/engine_test_serializer.rb
180
209
  - spec/fixtures/test_serializer.rb
181
210
  - spec/spec_helper.rb
182
211
  homepage: https://github.com/codenoble/cache-crispies
183
212
  licenses:
184
213
  - MIT
185
- metadata: {}
186
- post_install_message:
214
+ metadata:
215
+ source_code_uri: https://github.com/codenoble/cache-crispies
187
216
  rdoc_options: []
188
217
  require_paths:
189
218
  - lib
@@ -191,15 +220,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
191
220
  requirements:
192
221
  - - ">="
193
222
  - !ruby/object:Gem::Version
194
- version: '0'
223
+ version: 3.1.0
195
224
  required_rubygems_version: !ruby/object:Gem::Requirement
196
225
  requirements:
197
226
  - - ">="
198
227
  - !ruby/object:Gem::Version
199
228
  version: '0'
200
229
  requirements: []
201
- rubygems_version: 3.3.7
202
- signing_key:
230
+ rubygems_version: 3.6.2
203
231
  specification_version: 4
204
232
  summary: Fast Rails serializer with built-in caching
205
233
  test_files:
@@ -210,8 +238,10 @@ test_files:
210
238
  - spec/cache_crispies/configuration_spec.rb
211
239
  - spec/cache_crispies/controller_spec.rb
212
240
  - spec/cache_crispies/hash_builder_spec.rb
241
+ - spec/cache_crispies/json_builder_spec.rb
213
242
  - spec/cache_crispies/memoizer_spec.rb
214
243
  - spec/cache_crispies/plan_spec.rb
215
244
  - spec/cache_crispies_spec.rb
245
+ - spec/fixtures/engine_test_serializer.rb
216
246
  - spec/fixtures/test_serializer.rb
217
247
  - spec/spec_helper.rb