active_model_serializers 0.10.0.rc5 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
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