active_model_serializers 0.10.5 → 0.10.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +4 -1
  3. data/CHANGELOG.md +16 -2
  4. data/README.md +2 -2
  5. data/Rakefile +1 -30
  6. data/active_model_serializers.gemspec +1 -1
  7. data/bin/rubocop +38 -0
  8. data/docs/general/adapters.md +25 -9
  9. data/docs/general/getting_started.md +1 -1
  10. data/docs/general/rendering.md +18 -4
  11. data/docs/general/serializers.md +21 -2
  12. data/docs/howto/add_pagination_links.md +1 -1
  13. data/docs/integrations/ember-and-json-api.md +3 -0
  14. data/lib/active_model/serializer.rb +252 -74
  15. data/lib/active_model/serializer/association.rb +51 -14
  16. data/lib/active_model/serializer/belongs_to_reflection.rb +5 -1
  17. data/lib/active_model/serializer/concerns/caching.rb +29 -21
  18. data/lib/active_model/serializer/has_many_reflection.rb +4 -1
  19. data/lib/active_model/serializer/has_one_reflection.rb +1 -1
  20. data/lib/active_model/serializer/lazy_association.rb +95 -0
  21. data/lib/active_model/serializer/reflection.rb +119 -75
  22. data/lib/active_model/serializer/version.rb +1 -1
  23. data/lib/active_model_serializers/adapter/json_api.rb +30 -17
  24. data/lib/active_model_serializers/adapter/json_api/relationship.rb +38 -9
  25. data/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +9 -0
  26. data/lib/active_model_serializers/model.rb +5 -4
  27. data/lib/tasks/rubocop.rake +53 -0
  28. data/test/action_controller/adapter_selector_test.rb +2 -2
  29. data/test/serializers/associations_test.rb +64 -31
  30. data/test/serializers/reflection_test.rb +427 -0
  31. metadata +9 -12
  32. data/lib/active_model/serializer/collection_reflection.rb +0 -7
  33. data/lib/active_model/serializer/concerns/associations.rb +0 -102
  34. data/lib/active_model/serializer/concerns/attributes.rb +0 -82
  35. data/lib/active_model/serializer/concerns/configuration.rb +0 -59
  36. data/lib/active_model/serializer/concerns/links.rb +0 -35
  37. data/lib/active_model/serializer/concerns/meta.rb +0 -29
  38. data/lib/active_model/serializer/concerns/type.rb +0 -25
  39. data/lib/active_model/serializer/singular_reflection.rb +0 -7
@@ -0,0 +1,427 @@
1
+ require 'test_helper'
2
+ module ActiveModel
3
+ class Serializer
4
+ class ReflectionTest < ActiveSupport::TestCase
5
+ class Blog < ActiveModelSerializers::Model
6
+ attributes :id
7
+ end
8
+ class BlogSerializer < ActiveModel::Serializer
9
+ type 'blog'
10
+ attributes :id
11
+ end
12
+
13
+ setup do
14
+ @expected_meta = { id: 1 }
15
+ @expected_links = { self: 'no_uri_validation' }
16
+ @empty_links = {}
17
+ model_attributes = { blog: Blog.new(@expected_meta) }
18
+ @model = Class.new(ActiveModelSerializers::Model) do
19
+ attributes(*model_attributes.keys)
20
+
21
+ def self.name
22
+ 'TestModel'
23
+ end
24
+ end.new(model_attributes)
25
+ @instance_options = {}
26
+ end
27
+
28
+ def evaluate_association_value(association)
29
+ association.lazy_association.eval_reflection_block
30
+ end
31
+
32
+ # TODO: Remaining tests
33
+ # test_reflection_value_block_with_scope
34
+ # test_reflection_value_uses_serializer_instance_method
35
+ # test_reflection_excluded_eh_blank_is_false
36
+ # test_reflection_excluded_eh_if
37
+ # test_reflection_excluded_eh_unless
38
+ # test_evaluate_condition_symbol_serializer_method
39
+ # test_evaluate_condition_string_serializer_method
40
+ # test_evaluate_condition_proc
41
+ # test_evaluate_condition_proc_yields_serializer
42
+ # test_evaluate_condition_other
43
+ # test_options_key
44
+ # test_options_polymorphic
45
+ # test_options_serializer
46
+ # test_options_virtual_value
47
+ # test_options_namespace
48
+
49
+ def test_reflection_value
50
+ serializer_class = Class.new(ActiveModel::Serializer) do
51
+ has_one :blog
52
+ end
53
+ serializer_instance = serializer_class.new(@model, @instance_options)
54
+
55
+ # Get Reflection
56
+ reflection = serializer_class._reflections.fetch(:blog)
57
+
58
+ # Assert
59
+ assert_nil reflection.block
60
+ assert_equal Serializer.config.include_data_default, reflection.options.fetch(:include_data_setting)
61
+ assert_equal true, reflection.options.fetch(:include_data_setting)
62
+
63
+ include_slice = :does_not_matter
64
+ assert_equal @model.blog, reflection.send(:value, serializer_instance, include_slice)
65
+ end
66
+
67
+ def test_reflection_value_block
68
+ serializer_class = Class.new(ActiveModel::Serializer) do
69
+ has_one :blog do
70
+ object.blog
71
+ end
72
+ end
73
+ serializer_instance = serializer_class.new(@model, @instance_options)
74
+
75
+ # Get Reflection
76
+ reflection = serializer_class._reflections.fetch(:blog)
77
+
78
+ # Assert
79
+ assert_respond_to reflection.block, :call
80
+ assert_equal Serializer.config.include_data_default, reflection.options.fetch(:include_data_setting)
81
+ assert_equal true, reflection.options.fetch(:include_data_setting)
82
+
83
+ include_slice = :does_not_matter
84
+ assert_equal @model.blog, reflection.send(:value, serializer_instance, include_slice)
85
+ end
86
+
87
+ def test_reflection_value_block_with_explicit_include_data_true
88
+ serializer_class = Class.new(ActiveModel::Serializer) do
89
+ has_one :blog do
90
+ include_data true
91
+ object.blog
92
+ end
93
+ end
94
+ serializer_instance = serializer_class.new(@model, @instance_options)
95
+
96
+ # Get Reflection
97
+ reflection = serializer_class._reflections.fetch(:blog)
98
+
99
+ # Assert
100
+ assert_respond_to reflection.block, :call
101
+ assert_equal Serializer.config.include_data_default, reflection.options.fetch(:include_data_setting)
102
+ assert_equal true, reflection.options.fetch(:include_data_setting)
103
+
104
+ include_slice = :does_not_matter
105
+ assert_equal @model.blog, reflection.send(:value, serializer_instance, include_slice)
106
+ end
107
+
108
+ def test_reflection_value_block_with_include_data_false_mutates_the_reflection_include_data
109
+ serializer_class = Class.new(ActiveModel::Serializer) do
110
+ has_one :blog do
111
+ include_data false
112
+ object.blog
113
+ end
114
+ end
115
+ serializer_instance = serializer_class.new(@model, @instance_options)
116
+
117
+ # Get Reflection
118
+ reflection = serializer_class._reflections.fetch(:blog)
119
+
120
+ # Assert
121
+ assert_respond_to reflection.block, :call
122
+ assert_equal true, reflection.options.fetch(:include_data_setting)
123
+ include_slice = :does_not_matter
124
+ assert_nil reflection.send(:value, serializer_instance, include_slice)
125
+ assert_equal false, reflection.options.fetch(:include_data_setting)
126
+ end
127
+
128
+ def test_reflection_value_block_with_include_data_if_sideloaded_included_mutates_the_reflection_include_data
129
+ serializer_class = Class.new(ActiveModel::Serializer) do
130
+ has_one :blog do
131
+ include_data :if_sideloaded
132
+ object.blog
133
+ end
134
+ end
135
+ serializer_instance = serializer_class.new(@model, @instance_options)
136
+
137
+ # Get Reflection
138
+ reflection = serializer_class._reflections.fetch(:blog)
139
+
140
+ # Assert
141
+ assert_respond_to reflection.block, :call
142
+ assert_equal true, reflection.options.fetch(:include_data_setting)
143
+ include_slice = {}
144
+ assert_nil reflection.send(:value, serializer_instance, include_slice)
145
+ assert_equal :if_sideloaded, reflection.options.fetch(:include_data_setting)
146
+ end
147
+
148
+ def test_reflection_value_block_with_include_data_if_sideloaded_excluded_mutates_the_reflection_include_data
149
+ serializer_class = Class.new(ActiveModel::Serializer) do
150
+ has_one :blog do
151
+ include_data :if_sideloaded
152
+ object.blog
153
+ end
154
+ end
155
+ serializer_instance = serializer_class.new(@model, @instance_options)
156
+
157
+ # Get Reflection
158
+ reflection = serializer_class._reflections.fetch(:blog)
159
+
160
+ # Assert
161
+ assert_respond_to reflection.block, :call
162
+ assert_equal true, reflection.options.fetch(:include_data_setting)
163
+ include_slice = { blog: :does_not_matter }
164
+ assert_equal @model.blog, reflection.send(:value, serializer_instance, include_slice)
165
+ assert_equal :if_sideloaded, reflection.options.fetch(:include_data_setting)
166
+ end
167
+
168
+ def test_reflection_block_with_link_mutates_the_reflection_links
169
+ serializer_class = Class.new(ActiveModel::Serializer) do
170
+ has_one :blog do
171
+ link :self, 'no_uri_validation'
172
+ end
173
+ end
174
+ serializer_instance = serializer_class.new(@model, @instance_options)
175
+
176
+ # Get Reflection
177
+ reflection = serializer_class._reflections.fetch(:blog)
178
+ assert_equal @empty_links, reflection.options.fetch(:links)
179
+
180
+ # Build Association
181
+ association = reflection.build_association(serializer_instance, @instance_options)
182
+
183
+ # Assert association links empty when not yet evaluated
184
+ assert_equal @empty_links, reflection.options.fetch(:links)
185
+ assert_equal @empty_links, association.links
186
+
187
+ evaluate_association_value(association)
188
+
189
+ assert_equal @expected_links, association.links
190
+ assert_equal @expected_links, reflection.options.fetch(:links)
191
+ end
192
+
193
+ def test_reflection_block_with_link_block_mutates_the_reflection_links
194
+ serializer_class = Class.new(ActiveModel::Serializer) do
195
+ has_one :blog do
196
+ link :self do
197
+ 'no_uri_validation'
198
+ end
199
+ end
200
+ end
201
+ serializer_instance = serializer_class.new(@model, @instance_options)
202
+
203
+ # Get Reflection
204
+ reflection = serializer_class._reflections.fetch(:blog)
205
+ assert_equal @empty_links, reflection.options.fetch(:links)
206
+
207
+ # Build Association
208
+ association = reflection.build_association(serializer_instance, @instance_options)
209
+
210
+ # Assert association links empty when not yet evaluated
211
+ assert_equal @empty_links, association.links
212
+
213
+ evaluate_association_value(association)
214
+
215
+ # Assert before instance_eval link
216
+ link = association.links.fetch(:self)
217
+ assert_respond_to link, :call
218
+ assert_respond_to reflection.options.fetch(:links).fetch(:self), :call
219
+
220
+ # Assert after instance_eval link
221
+ assert_equal @expected_links.fetch(:self), reflection.instance_eval(&link)
222
+ assert_respond_to reflection.options.fetch(:links).fetch(:self), :call
223
+ end
224
+
225
+ def test_reflection_block_with_meta_mutates_the_reflection_meta
226
+ serializer_class = Class.new(ActiveModel::Serializer) do
227
+ has_one :blog do
228
+ meta(id: object.blog.id)
229
+ end
230
+ end
231
+ serializer_instance = serializer_class.new(@model, @instance_options)
232
+
233
+ # Get Reflection
234
+ reflection = serializer_class._reflections.fetch(:blog)
235
+ assert_nil reflection.options.fetch(:meta)
236
+
237
+ # Build Association
238
+ association = reflection.build_association(serializer_instance, @instance_options)
239
+
240
+ evaluate_association_value(association)
241
+
242
+ assert_equal @expected_meta, association.meta
243
+ assert_equal @expected_meta, reflection.options.fetch(:meta)
244
+ end
245
+
246
+ def test_reflection_block_with_meta_block_mutates_the_reflection_meta
247
+ serializer_class = Class.new(ActiveModel::Serializer) do
248
+ has_one :blog do
249
+ meta do
250
+ { id: object.blog.id }
251
+ end
252
+ end
253
+ end
254
+ serializer_instance = serializer_class.new(@model, @instance_options)
255
+
256
+ # Get Reflection
257
+ reflection = serializer_class._reflections.fetch(:blog)
258
+ assert_nil reflection.options.fetch(:meta)
259
+
260
+ # Build Association
261
+ association = reflection.build_association(serializer_instance, @instance_options)
262
+ # Assert before instance_eval meta
263
+
264
+ evaluate_association_value(association)
265
+
266
+ assert_respond_to association.meta, :call
267
+ assert_respond_to reflection.options.fetch(:meta), :call
268
+
269
+ # Assert after instance_eval meta
270
+ assert_equal @expected_meta, reflection.instance_eval(&association.meta)
271
+ assert_respond_to reflection.options.fetch(:meta), :call
272
+ assert_respond_to association.meta, :call
273
+ end
274
+
275
+ # rubocop:disable Metrics/AbcSize
276
+ def test_reflection_block_with_meta_in_link_block_mutates_the_reflection_meta
277
+ serializer_class = Class.new(ActiveModel::Serializer) do
278
+ has_one :blog do
279
+ link :self do
280
+ meta(id: object.blog.id)
281
+ 'no_uri_validation'
282
+ end
283
+ end
284
+ end
285
+ serializer_instance = serializer_class.new(@model, @instance_options)
286
+
287
+ # Get Reflection
288
+ reflection = serializer_class._reflections.fetch(:blog)
289
+ assert_nil reflection.options.fetch(:meta)
290
+ assert_equal @empty_links, reflection.options.fetch(:links)
291
+
292
+ # Build Association
293
+ association = reflection.build_association(serializer_instance, @instance_options)
294
+ # Assert before instance_eval link meta
295
+ assert_nil association.meta
296
+ assert_nil reflection.options.fetch(:meta)
297
+
298
+ evaluate_association_value(association)
299
+
300
+ link = association.links.fetch(:self)
301
+ assert_respond_to link, :call
302
+ assert_respond_to reflection.options.fetch(:links).fetch(:self), :call
303
+ assert_nil reflection.options.fetch(:meta)
304
+
305
+ # Assert after instance_eval link
306
+ assert_equal 'no_uri_validation', reflection.instance_eval(&link)
307
+ assert_equal @expected_meta, reflection.options.fetch(:meta)
308
+ assert_equal @expected_meta, association.meta
309
+ end
310
+ # rubocop:enable Metrics/AbcSize
311
+
312
+ # rubocop:disable Metrics/AbcSize
313
+ def test_reflection_block_with_meta_block_in_link_block_mutates_the_reflection_meta
314
+ serializer_class = Class.new(ActiveModel::Serializer) do
315
+ has_one :blog do
316
+ link :self do
317
+ meta do
318
+ { id: object.blog.id }
319
+ end
320
+ 'no_uri_validation'
321
+ end
322
+ end
323
+ end
324
+ serializer_instance = serializer_class.new(@model, @instance_options)
325
+
326
+ # Get Reflection
327
+ reflection = serializer_class._reflections.fetch(:blog)
328
+ assert_nil reflection.options.fetch(:meta)
329
+
330
+ # Build Association
331
+ association = reflection.build_association(serializer_instance, @instance_options)
332
+ assert_nil association.meta
333
+ assert_nil reflection.options.fetch(:meta)
334
+
335
+ # Assert before instance_eval link
336
+
337
+ evaluate_association_value(association)
338
+
339
+ link = association.links.fetch(:self)
340
+ assert_nil reflection.options.fetch(:meta)
341
+ assert_respond_to link, :call
342
+ assert_respond_to association.links.fetch(:self), :call
343
+
344
+ # Assert after instance_eval link
345
+ assert_equal 'no_uri_validation', reflection.instance_eval(&link)
346
+ assert_respond_to association.links.fetch(:self), :call
347
+ # Assert before instance_eval link meta
348
+ assert_respond_to reflection.options.fetch(:meta), :call
349
+ assert_respond_to association.meta, :call
350
+
351
+ # Assert after instance_eval link meta
352
+ assert_equal @expected_meta, reflection.instance_eval(&reflection.options.fetch(:meta))
353
+ assert_respond_to association.meta, :call
354
+ end
355
+ # rubocop:enable Metrics/AbcSize
356
+
357
+ def test_no_href_in_vanilla_reflection
358
+ serializer_class = Class.new(ActiveModel::Serializer) do
359
+ has_one :blog do
360
+ link :self do
361
+ href 'no_uri_validation'
362
+ end
363
+ end
364
+ end
365
+ serializer_instance = serializer_class.new(@model, @instance_options)
366
+
367
+ # Get Reflection
368
+ reflection = serializer_class._reflections.fetch(:blog)
369
+ assert_equal @empty_links, reflection.options.fetch(:links)
370
+
371
+ # Build Association
372
+ association = reflection.build_association(serializer_instance, @instance_options)
373
+ # Assert before instance_eval link
374
+
375
+ evaluate_association_value(association)
376
+
377
+ link = association.links.fetch(:self)
378
+ assert_respond_to link, :call
379
+
380
+ # Assert after instance_eval link
381
+ exception = assert_raise(NoMethodError) do
382
+ reflection.instance_eval(&link)
383
+ end
384
+ assert_match(/undefined method `href'/, exception.message)
385
+ end
386
+
387
+ # rubocop:disable Metrics/AbcSize
388
+ def test_mutating_reflection_block_is_not_thread_safe
389
+ serializer_class = Class.new(ActiveModel::Serializer) do
390
+ has_one :blog do
391
+ meta(id: object.blog.id)
392
+ end
393
+ end
394
+ model1_meta = @expected_meta
395
+ # Evaluate reflection meta for model with id 1
396
+ serializer_instance = serializer_class.new(@model, @instance_options)
397
+ reflection = serializer_class._reflections.fetch(:blog)
398
+ assert_nil reflection.options.fetch(:meta)
399
+ association = reflection.build_association(serializer_instance, @instance_options)
400
+
401
+ evaluate_association_value(association)
402
+
403
+ assert_equal model1_meta, association.meta
404
+ assert_equal model1_meta, reflection.options.fetch(:meta)
405
+
406
+ model2_meta = @expected_meta.merge(id: 2)
407
+ # Evaluate reflection meta for model with id 2
408
+ @model.blog.id = 2
409
+ assert_equal 2, @model.blog.id # sanity check
410
+ serializer_instance = serializer_class.new(@model, @instance_options)
411
+ reflection = serializer_class._reflections.fetch(:blog)
412
+
413
+ # WARN: Thread-safety issue
414
+ # Before the reflection is evaluated, it has the value from the previous evaluation
415
+ assert_equal model1_meta, reflection.options.fetch(:meta)
416
+
417
+ association = reflection.build_association(serializer_instance, @instance_options)
418
+
419
+ evaluate_association_value(association)
420
+
421
+ assert_equal model2_meta, association.meta
422
+ assert_equal model2_meta, reflection.options.fetch(:meta)
423
+ end
424
+ # rubocop:enable Metrics/AbcSize
425
+ end
426
+ end
427
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_model_serializers
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.5
4
+ version: 0.10.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steve Klabnik
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-03-07 00:00:00.000000000 Z
11
+ date: 2017-05-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -209,7 +209,7 @@ dependencies:
209
209
  version: '0.13'
210
210
  - - "<"
211
211
  - !ruby/object:Gem::Version
212
- version: '1.0'
212
+ version: 0.19.1
213
213
  type: :development
214
214
  prerelease: false
215
215
  version_requirements: !ruby/object:Gem::Requirement
@@ -219,7 +219,7 @@ dependencies:
219
219
  version: '0.13'
220
220
  - - "<"
221
221
  - !ruby/object:Gem::Version
222
- version: '1.0'
222
+ version: 0.19.1
223
223
  - !ruby/object:Gem::Dependency
224
224
  name: json_schema
225
225
  requirement: !ruby/object:Gem::Requirement
@@ -279,6 +279,7 @@ files:
279
279
  - appveyor.yml
280
280
  - bin/bench
281
281
  - bin/bench_regression
282
+ - bin/rubocop
282
283
  - bin/serve_benchmark
283
284
  - docs/README.md
284
285
  - docs/STYLE.md
@@ -323,25 +324,18 @@ files:
323
324
  - lib/active_model/serializer/association.rb
324
325
  - lib/active_model/serializer/attribute.rb
325
326
  - lib/active_model/serializer/belongs_to_reflection.rb
326
- - lib/active_model/serializer/collection_reflection.rb
327
327
  - lib/active_model/serializer/collection_serializer.rb
328
- - lib/active_model/serializer/concerns/associations.rb
329
- - lib/active_model/serializer/concerns/attributes.rb
330
328
  - lib/active_model/serializer/concerns/caching.rb
331
- - lib/active_model/serializer/concerns/configuration.rb
332
- - lib/active_model/serializer/concerns/links.rb
333
- - lib/active_model/serializer/concerns/meta.rb
334
- - lib/active_model/serializer/concerns/type.rb
335
329
  - lib/active_model/serializer/error_serializer.rb
336
330
  - lib/active_model/serializer/errors_serializer.rb
337
331
  - lib/active_model/serializer/field.rb
338
332
  - lib/active_model/serializer/fieldset.rb
339
333
  - lib/active_model/serializer/has_many_reflection.rb
340
334
  - lib/active_model/serializer/has_one_reflection.rb
335
+ - lib/active_model/serializer/lazy_association.rb
341
336
  - lib/active_model/serializer/lint.rb
342
337
  - lib/active_model/serializer/null.rb
343
338
  - lib/active_model/serializer/reflection.rb
344
- - lib/active_model/serializer/singular_reflection.rb
345
339
  - lib/active_model/serializer/version.rb
346
340
  - lib/active_model_serializers.rb
347
341
  - lib/active_model_serializers/adapter.rb
@@ -379,6 +373,7 @@ files:
379
373
  - lib/grape/active_model_serializers.rb
380
374
  - lib/grape/formatters/active_model_serializers.rb
381
375
  - lib/grape/helpers/active_model_serializers.rb
376
+ - lib/tasks/rubocop.rake
382
377
  - test/action_controller/adapter_selector_test.rb
383
378
  - test/action_controller/explicit_serializer_test.rb
384
379
  - test/action_controller/json/include_test.rb
@@ -464,6 +459,7 @@ files:
464
459
  - test/serializers/meta_test.rb
465
460
  - test/serializers/options_test.rb
466
461
  - test/serializers/read_attribute_for_serialization_test.rb
462
+ - test/serializers/reflection_test.rb
467
463
  - test/serializers/root_test.rb
468
464
  - test/serializers/serialization_test.rb
469
465
  - test/serializers/serializer_for_test.rb
@@ -590,6 +586,7 @@ test_files:
590
586
  - test/serializers/meta_test.rb
591
587
  - test/serializers/options_test.rb
592
588
  - test/serializers/read_attribute_for_serialization_test.rb
589
+ - test/serializers/reflection_test.rb
593
590
  - test/serializers/root_test.rb
594
591
  - test/serializers/serialization_test.rb
595
592
  - test/serializers/serializer_for_test.rb