active_model_serializers 0.10.0.rc5 → 0.10.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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +10 -0
  3. data/.travis.yml +0 -8
  4. data/CHANGELOG.md +23 -0
  5. data/CONTRIBUTING.md +14 -4
  6. data/Gemfile +1 -2
  7. data/README.md +3 -3
  8. data/active_model_serializers.gemspec +1 -1
  9. data/appveyor.yml +6 -10
  10. data/docs/ARCHITECTURE.md +1 -1
  11. data/docs/README.md +1 -0
  12. data/docs/general/deserialization.md +19 -19
  13. data/docs/general/serializers.md +33 -0
  14. data/docs/howto/serialize_poro.md +32 -0
  15. data/lib/active_model/serializer.rb +2 -0
  16. data/lib/active_model/serializer/caching.rb +185 -3
  17. data/lib/active_model/serializer/field.rb +36 -2
  18. data/lib/active_model/serializer/lint.rb +8 -18
  19. data/lib/active_model/serializer/version.rb +1 -1
  20. data/lib/active_model_serializers.rb +0 -2
  21. data/lib/active_model_serializers/adapter/attributes.rb +20 -38
  22. data/lib/active_model_serializers/adapter/base.rb +8 -15
  23. data/lib/active_model_serializers/adapter/json.rb +12 -2
  24. data/lib/active_model_serializers/adapter/json_api.rb +33 -30
  25. data/lib/active_model_serializers/adapter/json_api/pagination_links.rb +10 -5
  26. data/lib/active_model_serializers/adapter/null.rb +1 -2
  27. data/lib/active_model_serializers/register_jsonapi_renderer.rb +3 -2
  28. data/lib/active_model_serializers/serializable_resource.rb +1 -1
  29. data/lib/active_model_serializers/test/schema.rb +44 -9
  30. data/test/action_controller/json_api/transform_test.rb +2 -1
  31. data/test/action_controller/serialization_test.rb +3 -1
  32. data/test/active_model_serializers/test/schema_test.rb +5 -3
  33. data/test/active_model_serializers/test/serializer_test.rb +1 -2
  34. data/test/adapter/json/transform_test.rb +14 -14
  35. data/test/adapter/json_api/has_many_test.rb +3 -2
  36. data/test/adapter/json_api/has_one_test.rb +3 -2
  37. data/test/adapter/json_api/pagination_links_test.rb +39 -21
  38. data/test/adapter/json_api/transform_test.rb +36 -34
  39. data/test/adapter/polymorphic_test.rb +111 -12
  40. data/test/adapter_test.rb +27 -0
  41. data/test/array_serializer_test.rb +10 -25
  42. data/test/benchmark/bm_caching.rb +17 -15
  43. data/test/benchmark/controllers.rb +9 -2
  44. data/test/benchmark/fixtures.rb +56 -4
  45. data/test/cache_test.rb +103 -6
  46. data/test/fixtures/active_record.rb +10 -0
  47. data/test/fixtures/poro.rb +31 -3
  48. data/test/serializers/associations_test.rb +43 -15
  49. data/test/serializers/attribute_test.rb +44 -16
  50. data/test/serializers/meta_test.rb +5 -7
  51. data/test/support/isolated_unit.rb +0 -1
  52. data/test/test_helper.rb +19 -21
  53. metadata +7 -12
  54. data/lib/active_model_serializers/cached_serializer.rb +0 -87
  55. data/lib/active_model_serializers/fragment_cache.rb +0 -118
  56. data/test/active_model_serializers/cached_serializer_test.rb +0 -80
  57. data/test/active_model_serializers/fragment_cache_test.rb +0 -34
@@ -67,8 +67,8 @@ module ActiveModelSerializers
67
67
  def test_success_document_transform_default
68
68
  mock_request
69
69
  serializer = PostSerializer.new(@post)
70
- adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
71
- result = adapter.serializable_hash(@options)
70
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options)
71
+ result = adapter.serializable_hash
72
72
  assert_equal({
73
73
  data: {
74
74
  id: '1337',
@@ -102,8 +102,8 @@ module ActiveModelSerializers
102
102
  mock_request
103
103
  result = with_config(key_transform: :camel_lower) do
104
104
  serializer = PostSerializer.new(@post)
105
- adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
106
- adapter.serializable_hash(@options)
105
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options)
106
+ adapter.serializable_hash
107
107
  end
108
108
  assert_equal({
109
109
  data: {
@@ -138,8 +138,8 @@ module ActiveModelSerializers
138
138
  mock_request(:camel)
139
139
  result = with_config(key_transform: :camel_lower) do
140
140
  serializer = PostSerializer.new(@post)
141
- adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
142
- adapter.serializable_hash(@options)
141
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options)
142
+ adapter.serializable_hash
143
143
  end
144
144
  assert_equal({
145
145
  Data: {
@@ -173,8 +173,8 @@ module ActiveModelSerializers
173
173
  def test_success_document_transform_dash
174
174
  mock_request(:dash)
175
175
  serializer = PostSerializer.new(@post)
176
- adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
177
- result = adapter.serializable_hash(@options)
176
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options)
177
+ result = adapter.serializable_hash
178
178
  assert_equal({
179
179
  data: {
180
180
  id: '1337',
@@ -207,8 +207,8 @@ module ActiveModelSerializers
207
207
  def test_success_document_transform_unaltered
208
208
  mock_request(:unaltered)
209
209
  serializer = PostSerializer.new(@post)
210
- adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
211
- result = adapter.serializable_hash(@options)
210
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options)
211
+ result = adapter.serializable_hash
212
212
  assert_equal({
213
213
  data: {
214
214
  id: '1337',
@@ -241,17 +241,18 @@ module ActiveModelSerializers
241
241
  def test_success_document_transform_undefined
242
242
  mock_request(:zoot)
243
243
  serializer = PostSerializer.new(@post)
244
- adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
245
- assert_raises NoMethodError do
246
- adapter.serializable_hash(@options)
244
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options)
245
+ exception = assert_raises NoMethodError do
246
+ adapter.serializable_hash
247
247
  end
248
+ assert_match(/undefined method.*zoot/, exception.message)
248
249
  end
249
250
 
250
251
  def test_success_document_transform_camel
251
252
  mock_request(:camel)
252
253
  serializer = PostSerializer.new(@post)
253
- adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
254
- result = adapter.serializable_hash(@options)
254
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options)
255
+ result = adapter.serializable_hash
255
256
  assert_equal({
256
257
  Data: {
257
258
  Id: '1337',
@@ -284,8 +285,8 @@ module ActiveModelSerializers
284
285
  def test_success_document_transform_camel_lower
285
286
  mock_request(:camel_lower)
286
287
  serializer = PostSerializer.new(@post)
287
- adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
288
- result = adapter.serializable_hash(@options)
288
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options)
289
+ result = adapter.serializable_hash
289
290
  assert_equal({
290
291
  data: {
291
292
  id: '1337',
@@ -321,8 +322,8 @@ module ActiveModelSerializers
321
322
  resource.errors.add(:published_at, 'must be in the future')
322
323
  resource.errors.add(:title, 'must be longer')
323
324
  serializer = ActiveModel::Serializer::ErrorSerializer.new(resource)
324
- adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
325
- result = adapter.serializable_hash(@options)
325
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options)
326
+ result = adapter.serializable_hash
326
327
  expected_errors_object =
327
328
  { :errors =>
328
329
  [
@@ -345,8 +346,8 @@ module ActiveModelSerializers
345
346
  resource.errors.add(:published_at, 'must be in the future')
346
347
  resource.errors.add(:title, 'must be longer')
347
348
  serializer = ActiveModel::Serializer::ErrorSerializer.new(resource)
348
- adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
349
- adapter.serializable_hash(@options)
349
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options)
350
+ adapter.serializable_hash
350
351
  end
351
352
  expected_errors_object =
352
353
  { :Errors =>
@@ -371,8 +372,8 @@ module ActiveModelSerializers
371
372
  resource.errors.add(:published_at, 'must be in the future')
372
373
  resource.errors.add(:title, 'must be longer')
373
374
  serializer = ActiveModel::Serializer::ErrorSerializer.new(resource)
374
- adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
375
- adapter.serializable_hash(@options)
375
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options)
376
+ adapter.serializable_hash
376
377
  end
377
378
  expected_errors_object =
378
379
  { :Errors =>
@@ -398,8 +399,8 @@ module ActiveModelSerializers
398
399
  resource.errors.add(:title, 'must be longer')
399
400
 
400
401
  serializer = ActiveModel::Serializer::ErrorSerializer.new(resource)
401
- adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
402
- result = adapter.serializable_hash(@options)
402
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options)
403
+ result = adapter.serializable_hash
403
404
 
404
405
  expected_errors_object =
405
406
  { :errors =>
@@ -425,8 +426,8 @@ module ActiveModelSerializers
425
426
  resource.errors.add(:title, 'must be longer')
426
427
 
427
428
  serializer = ActiveModel::Serializer::ErrorSerializer.new(resource)
428
- adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
429
- result = adapter.serializable_hash(@options)
429
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options)
430
+ result = adapter.serializable_hash
430
431
 
431
432
  expected_errors_object =
432
433
  { :errors =>
@@ -446,11 +447,12 @@ module ActiveModelSerializers
446
447
  resource.errors.add(:title, 'must be longer')
447
448
 
448
449
  serializer = ActiveModel::Serializer::ErrorSerializer.new(resource)
449
- adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
450
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options)
450
451
 
451
- assert_raises NoMethodError do
452
- adapter.serializable_hash(@options)
452
+ exception = assert_raises NoMethodError do
453
+ adapter.serializable_hash
453
454
  end
455
+ assert_match(/undefined method.*krazy/, exception.message)
454
456
  end
455
457
 
456
458
  def test_error_document_transform_camel
@@ -461,8 +463,8 @@ module ActiveModelSerializers
461
463
  resource.errors.add(:title, 'must be longer')
462
464
 
463
465
  serializer = ActiveModel::Serializer::ErrorSerializer.new(resource)
464
- adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
465
- result = adapter.serializable_hash(@options)
466
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options)
467
+ result = adapter.serializable_hash
466
468
 
467
469
  expected_errors_object =
468
470
  { :Errors =>
@@ -482,8 +484,8 @@ module ActiveModelSerializers
482
484
  resource.errors.add(:title, 'must be longer')
483
485
 
484
486
  serializer = ActiveModel::Serializer::ErrorSerializer.new(resource)
485
- adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
486
- result = adapter.serializable_hash(@options)
487
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options)
488
+ result = adapter.serializable_hash
487
489
 
488
490
  expected_errors_object =
489
491
  { :errors =>
@@ -8,10 +8,17 @@ module ActiveModel
8
8
  @employee = Employee.new(id: 42, name: 'Zoop Zoopler', email: 'zoop@example.com')
9
9
  @picture = @employee.pictures.new(id: 1, title: 'headshot-1.jpg')
10
10
  @picture.imageable = @employee
11
+ end
12
+
13
+ def serialization(resource, adapter = :attributes)
14
+ serializable(resource, adapter: adapter, serializer: PolymorphicBelongsToSerializer).as_json
15
+ end
11
16
 
12
- @attributes_serialization = serializable(@picture, serializer: PolymorphicBelongsToSerializer) # uses default adapter: attributes
13
- @json_serialization = serializable(@picture, adapter: :json, serializer: PolymorphicBelongsToSerializer)
14
- @json_api_serialization = serializable(@picture, adapter: :json_api, serializer: PolymorphicBelongsToSerializer)
17
+ def tag_serialization(adapter = :attributes)
18
+ tag = PolyTag.new(id: 1, phrase: 'foo')
19
+ tag.object_tags << ObjectTag.new(id: 1, poly_tag_id: 1, taggable: @employee)
20
+ tag.object_tags << ObjectTag.new(id: 5, poly_tag_id: 1, taggable: @picture)
21
+ serializable(tag, adapter: adapter, serializer: PolymorphicTagSerializer, include: '*.*').as_json
15
22
  end
16
23
 
17
24
  def test_attributes_serialization
@@ -20,31 +27,123 @@ module ActiveModel
20
27
  id: 1,
21
28
  title: 'headshot-1.jpg',
22
29
  imageable: {
23
- id: 42,
24
- name: 'Zoop Zoopler'
30
+ type: 'employee',
31
+ employee: {
32
+ id: 42,
33
+ name: 'Zoop Zoopler'
34
+ }
25
35
  }
26
36
  }
27
37
 
28
- assert_equal(expected, @attributes_serialization.as_json)
38
+ assert_equal(expected, serialization(@picture))
29
39
  end
30
40
 
31
- def test_json_serializer
41
+ def test_attributes_serialization_without_polymorphic_association
42
+ expected =
43
+ {
44
+ id: 2,
45
+ title: 'headshot-2.jpg',
46
+ imageable: nil
47
+ }
48
+
49
+ simple_picture = Picture.new(id: 2, title: 'headshot-2.jpg')
50
+ assert_equal(expected, serialization(simple_picture))
51
+ end
52
+
53
+ def test_attributes_serialization_with_polymorphic_has_many
54
+ expected =
55
+ {
56
+ id: 1,
57
+ phrase: 'foo',
58
+ object_tags: [
59
+ {
60
+ id: 1,
61
+ taggable: {
62
+ type: 'employee',
63
+ employee: {
64
+ id: 42
65
+ }
66
+ }
67
+ },
68
+ {
69
+ id: 5,
70
+ taggable: {
71
+ type: 'picture',
72
+ picture: {
73
+ id: 1
74
+ }
75
+ }
76
+ }
77
+ ]
78
+ }
79
+ assert_equal(expected, tag_serialization)
80
+ end
81
+
82
+ def test_json_serialization
32
83
  expected =
33
84
  {
34
85
  picture: {
35
86
  id: 1,
36
87
  title: 'headshot-1.jpg',
37
88
  imageable: {
38
- id: 42,
39
- name: 'Zoop Zoopler'
89
+ type: 'employee',
90
+ employee: {
91
+ id: 42,
92
+ name: 'Zoop Zoopler'
93
+ }
40
94
  }
41
95
  }
42
96
  }
43
97
 
44
- assert_equal(expected, @json_serialization.as_json)
98
+ assert_equal(expected, serialization(@picture, :json))
99
+ end
100
+
101
+ def test_json_serialization_without_polymorphic_association
102
+ expected =
103
+ {
104
+ picture: {
105
+ id: 2,
106
+ title: 'headshot-2.jpg',
107
+ imageable: nil
108
+ }
109
+ }
110
+
111
+ simple_picture = Picture.new(id: 2, title: 'headshot-2.jpg')
112
+ assert_equal(expected, serialization(simple_picture, :json))
113
+ end
114
+
115
+ def test_json_serialization_with_polymorphic_has_many
116
+ expected =
117
+ {
118
+ poly_tag: {
119
+ id: 1,
120
+ phrase: 'foo',
121
+ object_tags: [
122
+ {
123
+ id: 1,
124
+ taggable: {
125
+ type: 'employee',
126
+ employee: {
127
+ id: 42
128
+ }
129
+ }
130
+ },
131
+ {
132
+ id: 5,
133
+ taggable: {
134
+ type: 'picture',
135
+ picture: {
136
+ id: 1
137
+ }
138
+ }
139
+ }
140
+ ]
141
+ }
142
+ }
143
+ assert_equal(expected, tag_serialization(:json))
45
144
  end
46
145
 
47
- def test_json_api_serializer
146
+ def test_json_api_serialization
48
147
  expected =
49
148
  {
50
149
  data: {
@@ -64,7 +163,7 @@ module ActiveModel
64
163
  }
65
164
  }
66
165
 
67
- assert_equal(expected, @json_api_serialization.as_json)
166
+ assert_equal(expected, serialization(@picture, :json_api))
68
167
  end
69
168
  end
70
169
  end
@@ -14,6 +14,33 @@ module ActiveModelSerializers
14
14
  end
15
15
  end
16
16
 
17
+ def test_serialization_options_ensures_option_is_a_hash
18
+ adapter = Class.new(ActiveModelSerializers::Adapter::Base) do
19
+ def serializable_hash(options = nil)
20
+ serialization_options(options)
21
+ end
22
+ end.new(@serializer)
23
+ assert_equal({}, adapter.serializable_hash(nil))
24
+ assert_equal({}, adapter.serializable_hash({}))
25
+ ensure
26
+ ActiveModelSerializers::Adapter.adapter_map.delete_if { |k, _| k =~ /class/ }
27
+ end
28
+
29
+ def test_serialization_options_ensures_option_is_one_of_valid_options
30
+ adapter = Class.new(ActiveModelSerializers::Adapter::Base) do
31
+ def serializable_hash(options = nil)
32
+ serialization_options(options)
33
+ end
34
+ end.new(@serializer)
35
+ filtered_options = { now: :see_me, then: :not }
36
+ valid_options = ActiveModel::Serializer::SERIALIZABLE_HASH_VALID_KEYS.each_with_object({}) do |option, result|
37
+ result[option] = option
38
+ end
39
+ assert_equal(valid_options, adapter.serializable_hash(filtered_options.merge(valid_options)))
40
+ ensure
41
+ ActiveModelSerializers::Adapter.adapter_map.delete_if { |k, _| k =~ /class/ }
42
+ end
43
+
17
44
  def test_serializer
18
45
  assert_equal @serializer, @adapter.serializer
19
46
  end
@@ -3,34 +3,19 @@ require_relative 'collection_serializer_test'
3
3
 
4
4
  module ActiveModel
5
5
  class Serializer
6
- # Minitest.run_one_method isn't present in minitest 4
7
- if $minitest_version > 4 # rubocop:disable Style/GlobalVars
8
- class ArraySerializerTest < CollectionSerializerTest
9
- extend Minitest::Assertions
10
- def self.run_one_method(*)
11
- _, stderr = capture_io do
12
- super
13
- end
14
- if stderr !~ /NOTE: ActiveModel::Serializer::ArraySerializer.new is deprecated/
15
- fail Minitest::Assertion, stderr
16
- end
6
+ class ArraySerializerTest < CollectionSerializerTest
7
+ extend Minitest::Assertions
8
+ def self.run_one_method(*)
9
+ _, stderr = capture_io do
10
+ super
17
11
  end
18
-
19
- def collection_serializer
20
- ArraySerializer
12
+ if stderr !~ /NOTE: ActiveModel::Serializer::ArraySerializer.new is deprecated/
13
+ fail Minitest::Assertion, stderr
21
14
  end
22
15
  end
23
- else
24
- class ArraySerializerTest < ActiveSupport::TestCase
25
- def test_json_key_with_root_warns_when_using_array_serializer
26
- _, stderr = capture_io do
27
- comment = Comment.new
28
- post = Post.new
29
- serializer = ArraySerializer.new([comment, post])
30
- assert_equal 'comments', serializer.json_key
31
- end
32
- assert_match(/NOTE: ActiveModel::Serializer::ArraySerializer.new is deprecated/, stderr)
33
- end
16
+
17
+ def collection_serializer
18
+ ArraySerializer
34
19
  end
35
20
  end
36
21
  end
@@ -31,10 +31,22 @@ class ApiAssertion
31
31
  get("/caching/#{on_off}")
32
32
  end
33
33
 
34
+ def get_fragment_caching(on_off = 'on'.freeze)
35
+ get("/fragment_caching/#{on_off}")
36
+ end
37
+
34
38
  def get_non_caching(on_off = 'on'.freeze)
35
39
  get("/non_caching/#{on_off}")
36
40
  end
37
41
 
42
+ def debug(msg = '')
43
+ if block_given? && ENV['DEBUG'] =~ /\Atrue|on|0\z/i
44
+ STDERR.puts yield
45
+ else
46
+ STDERR.puts msg
47
+ end
48
+ end
49
+
38
50
  private
39
51
 
40
52
  def assert_responses(caching, non_caching)
@@ -85,33 +97,23 @@ class ApiAssertion
85
97
  STDERR.puts message unless ENV['SUMMARIZE']
86
98
  end
87
99
  end
88
-
89
- def debug(msg = '')
90
- if block_given? && ENV['DEBUG'] =~ /\Atrue|on|0\z/i
91
- STDERR.puts yield
92
- else
93
- STDERR.puts msg
94
- end
95
- end
96
100
  end
97
101
  assertion = ApiAssertion.new
98
102
  assertion.valid?
99
- # STDERR.puts assertion.get_status
103
+ assertion.debug { assertion.get_status }
100
104
 
101
105
  time = 10
102
106
  {
103
107
  'caching on: caching serializers: gc off' => { disable_gc: true, send: [:get_caching, 'on'] },
104
- # 'caching on: caching serializers: gc on' => { disable_gc: false, send: [:get_caching, 'on'] },
105
- 'caching off: caching serializers: gc off' => { disable_gc: true, send: [:get_caching, 'off'] },
106
- # 'caching off: caching serializers: gc on' => { disable_gc: false, send: [:get_caching, 'off'] },
108
+ 'caching on: fragment caching serializers: gc off' => { disable_gc: true, send: [:get_fragment_caching, 'on'] },
107
109
  'caching on: non-caching serializers: gc off' => { disable_gc: true, send: [:get_non_caching, 'on'] },
108
- # 'caching on: non-caching serializers: gc on' => { disable_gc: false, send: [:get_non_caching, 'on'] },
110
+ 'caching off: caching serializers: gc off' => { disable_gc: true, send: [:get_caching, 'off'] },
111
+ 'caching off: fragment caching serializers: gc off' => { disable_gc: true, send: [:get_fragment_caching, 'off'] },
109
112
  'caching off: non-caching serializers: gc off' => { disable_gc: true, send: [:get_non_caching, 'off'] }
110
- # 'caching off: non-caching serializers: gc on' => { disable_gc: false, send: [:get_non_caching, 'off'] }
111
113
  }.each do |label, options|
112
114
  assertion.clear
113
115
  Benchmark.ams(label, time: time, disable_gc: options[:disable_gc]) do
114
116
  assertion.send(*options[:send])
115
117
  end
116
- # STDERR.puts assertion.get_status(options[:send][-1])
118
+ assertion.debug { assertion.get_status(options[:send][-1]) }
117
119
  end