active_model_serializers 0.10.2 → 0.10.3

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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +8 -1
  3. data/CHANGELOG.md +45 -3
  4. data/CODE_OF_CONDUCT.md +74 -0
  5. data/Gemfile +4 -1
  6. data/README.md +5 -2
  7. data/active_model_serializers.gemspec +2 -2
  8. data/docs/ARCHITECTURE.md +6 -7
  9. data/docs/README.md +2 -0
  10. data/docs/general/caching.md +7 -1
  11. data/docs/general/configuration_options.md +64 -0
  12. data/docs/general/rendering.md +35 -1
  13. data/docs/general/serializers.md +35 -5
  14. data/docs/howto/add_pagination_links.md +2 -2
  15. data/docs/howto/add_relationship_links.md +137 -0
  16. data/docs/howto/add_root_key.md +4 -0
  17. data/docs/howto/grape_integration.md +42 -0
  18. data/docs/howto/outside_controller_use.md +9 -2
  19. data/docs/howto/passing_arbitrary_options.md +2 -2
  20. data/docs/howto/test.md +2 -0
  21. data/docs/howto/upgrade_from_0_8_to_0_10.md +265 -0
  22. data/docs/integrations/ember-and-json-api.md +41 -24
  23. data/lib/action_controller/serialization.rb +9 -0
  24. data/lib/active_model/serializer.rb +19 -22
  25. data/lib/active_model/serializer/association.rb +19 -4
  26. data/lib/active_model/serializer/collection_serializer.rb +8 -5
  27. data/lib/active_model/serializer/{associations.rb → concerns/associations.rb} +8 -5
  28. data/lib/active_model/serializer/{attributes.rb → concerns/attributes.rb} +0 -0
  29. data/lib/active_model/serializer/{caching.rb → concerns/caching.rb} +5 -1
  30. data/lib/active_model/serializer/{configuration.rb → concerns/configuration.rb} +24 -1
  31. data/lib/active_model/serializer/{links.rb → concerns/links.rb} +0 -0
  32. data/lib/active_model/serializer/{meta.rb → concerns/meta.rb} +0 -0
  33. data/lib/active_model/serializer/{type.rb → concerns/type.rb} +0 -0
  34. data/lib/active_model/serializer/reflection.rb +37 -21
  35. data/lib/active_model/serializer/version.rb +1 -1
  36. data/lib/active_model_serializers.rb +1 -0
  37. data/lib/active_model_serializers/adapter/attributes.rb +3 -1
  38. data/lib/active_model_serializers/adapter/json_api.rb +15 -22
  39. data/lib/active_model_serializers/adapter/json_api/relationship.rb +30 -19
  40. data/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +23 -9
  41. data/lib/active_model_serializers/key_transform.rb +4 -0
  42. data/lib/active_model_serializers/lookup_chain.rb +80 -0
  43. data/lib/active_model_serializers/model.rb +1 -1
  44. data/lib/active_model_serializers/serializable_resource.rb +6 -5
  45. data/lib/generators/rails/serializer_generator.rb +1 -1
  46. data/test/action_controller/json_api/fields_test.rb +57 -0
  47. data/test/action_controller/json_api/transform_test.rb +3 -3
  48. data/test/action_controller/lookup_proc_test.rb +49 -0
  49. data/test/action_controller/namespace_lookup_test.rb +226 -0
  50. data/test/active_model_serializers/key_transform_test.rb +32 -0
  51. data/test/active_model_serializers/model_test.rb +11 -0
  52. data/test/adapter/attributes_test.rb +43 -0
  53. data/test/adapter/json/transform_test.rb +1 -1
  54. data/test/adapter/json_api/fields_test.rb +4 -3
  55. data/test/adapter/json_api/has_many_test.rb +21 -0
  56. data/test/adapter/json_api/include_data_if_sideloaded_test.rb +166 -0
  57. data/test/adapter/json_api/linked_test.rb +24 -6
  58. data/test/adapter/json_api/links_test.rb +1 -1
  59. data/test/adapter/json_api/pagination_links_test.rb +17 -2
  60. data/test/adapter/json_api/relationship_test.rb +309 -73
  61. data/test/adapter/json_api/resource_identifier_test.rb +20 -0
  62. data/test/adapter/json_api/transform_test.rb +4 -3
  63. data/test/benchmark/benchmarking_support.rb +1 -1
  64. data/test/benchmark/bm_active_record.rb +81 -0
  65. data/test/benchmark/bm_adapter.rb +38 -0
  66. data/test/benchmark/bm_caching.rb +1 -1
  67. data/test/benchmark/bm_lookup_chain.rb +83 -0
  68. data/test/cache_test.rb +42 -4
  69. data/test/collection_serializer_test.rb +1 -1
  70. data/test/fixtures/poro.rb +44 -41
  71. data/test/generators/serializer_generator_test.rb +22 -5
  72. data/test/serializers/association_macros_test.rb +3 -2
  73. data/test/serializers/associations_test.rb +97 -22
  74. data/test/serializers/attribute_test.rb +1 -1
  75. data/test/serializers/serializer_for_test.rb +3 -3
  76. data/test/serializers/serializer_for_with_namespace_test.rb +87 -0
  77. data/test/support/serialization_testing.rb +16 -0
  78. data/test/test_helper.rb +1 -0
  79. metadata +35 -14
  80. data/test/adapter/json_api/relationships_test.rb +0 -204
@@ -5,7 +5,9 @@ module ActionController
5
5
  class JsonApi
6
6
  class KeyTransformTest < ActionController::TestCase
7
7
  class KeyTransformTestController < ActionController::Base
8
- Post = Class.new(::Model)
8
+ class Post < ::Model; end
9
+ class Author < ::Model; end
10
+ class TopComment < ::Model; end
9
11
  class PostSerializer < ActiveModel::Serializer
10
12
  type 'posts'
11
13
  attributes :title, :body, :publish_at
@@ -22,13 +24,11 @@ module ActionController
22
24
  end
23
25
  end
24
26
 
25
- Author = Class.new(::Model)
26
27
  class AuthorSerializer < ActiveModel::Serializer
27
28
  type 'authors'
28
29
  attributes :first_name, :last_name
29
30
  end
30
31
 
31
- TopComment = Class.new(::Model)
32
32
  class TopCommentSerializer < ActiveModel::Serializer
33
33
  type 'top_comments'
34
34
  attributes :body
@@ -0,0 +1,49 @@
1
+ require 'test_helper'
2
+
3
+ module ActionController
4
+ module Serialization
5
+ class LookupProcTest < ActionController::TestCase
6
+ module Api
7
+ module V3
8
+ class PostCustomSerializer < ActiveModel::Serializer
9
+ attributes :title, :body
10
+
11
+ belongs_to :author
12
+ end
13
+
14
+ class AuthorCustomSerializer < ActiveModel::Serializer
15
+ attributes :name
16
+ end
17
+
18
+ class LookupProcTestController < ActionController::Base
19
+ def implicit_namespaced_serializer
20
+ author = Author.new(name: 'Bob')
21
+ post = Post.new(title: 'New Post', body: 'Body', author: author)
22
+
23
+ render json: post
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ tests Api::V3::LookupProcTestController
30
+
31
+ test 'implicitly uses namespaced serializer' do
32
+ controller_namespace = lambda do |resource_class, _parent_serializer_class, namespace|
33
+ "#{namespace}::#{resource_class}CustomSerializer" if namespace
34
+ end
35
+
36
+ with_prepended_lookup(controller_namespace) do
37
+ get :implicit_namespaced_serializer
38
+
39
+ assert_serializer Api::V3::PostCustomSerializer
40
+
41
+ expected = { 'title' => 'New Post', 'body' => 'Body', 'author' => { 'name' => 'Bob' } }
42
+ actual = JSON.parse(@response.body)
43
+
44
+ assert_equal expected, actual
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,226 @@
1
+ require 'test_helper'
2
+
3
+ module ActionController
4
+ module Serialization
5
+ class NamespaceLookupTest < ActionController::TestCase
6
+ class Book < ::Model; end
7
+ class Page < ::Model; end
8
+ class Chapter < ::Model; end
9
+ class Writer < ::Model; end
10
+
11
+ module Api
12
+ module V2
13
+ class BookSerializer < ActiveModel::Serializer
14
+ attributes :title
15
+ end
16
+ end
17
+
18
+ module VHeader
19
+ class BookSerializer < ActiveModel::Serializer
20
+ attributes :title, :body
21
+
22
+ def body
23
+ 'header'
24
+ end
25
+ end
26
+ end
27
+
28
+ module V3
29
+ class BookSerializer < ActiveModel::Serializer
30
+ attributes :title, :body
31
+
32
+ belongs_to :writer
33
+ has_many :chapters
34
+ end
35
+
36
+ class ChapterSerializer < ActiveModel::Serializer
37
+ attribute :title do
38
+ "Chapter - #{object.title}"
39
+ end
40
+ end
41
+
42
+ class WriterSerializer < ActiveModel::Serializer
43
+ attributes :name
44
+ end
45
+
46
+ class LookupTestController < ActionController::Base
47
+ before_action only: [:namespace_set_in_before_filter] do
48
+ self.namespace_for_serializer = Api::V2
49
+ end
50
+
51
+ def implicit_namespaced_serializer
52
+ writer = Writer.new(name: 'Bob')
53
+ book = Book.new(title: 'New Post', body: 'Body', writer: writer, chapters: [])
54
+
55
+ render json: book
56
+ end
57
+
58
+ def implicit_namespaced_collection_serializer
59
+ chapter1 = Chapter.new(title: 'Oh')
60
+ chapter2 = Chapter.new(title: 'Oh my')
61
+
62
+ render json: [chapter1, chapter2]
63
+ end
64
+
65
+ def implicit_has_many_namespaced_serializer
66
+ chapter1 = Chapter.new(title: 'Odd World')
67
+ chapter2 = Chapter.new(title: 'New World')
68
+ book = Book.new(title: 'New Post', body: 'Body', chapters: [chapter1, chapter2])
69
+
70
+ render json: book
71
+ end
72
+
73
+ def explicit_namespace_as_module
74
+ book = Book.new(title: 'New Post', body: 'Body')
75
+
76
+ render json: book, namespace: Api::V2
77
+ end
78
+
79
+ def explicit_namespace_as_string
80
+ book = Book.new(title: 'New Post', body: 'Body')
81
+
82
+ # because this is a string, ruby can't auto-lookup the constant, so otherwise
83
+ # the looku things we mean ::Api::V2
84
+ render json: book, namespace: 'ActionController::Serialization::NamespaceLookupTest::Api::V2'
85
+ end
86
+
87
+ def explicit_namespace_as_symbol
88
+ book = Book.new(title: 'New Post', body: 'Body')
89
+
90
+ # because this is a string, ruby can't auto-lookup the constant, so otherwise
91
+ # the looku things we mean ::Api::V2
92
+ render json: book, namespace: :'ActionController::Serialization::NamespaceLookupTest::Api::V2'
93
+ end
94
+
95
+ def invalid_namespace
96
+ book = Book.new(title: 'New Post', body: 'Body')
97
+
98
+ render json: book, namespace: :api_v2
99
+ end
100
+
101
+ def namespace_set_in_before_filter
102
+ book = Book.new(title: 'New Post', body: 'Body')
103
+ render json: book
104
+ end
105
+
106
+ def namespace_set_by_request_headers
107
+ book = Book.new(title: 'New Post', body: 'Body')
108
+ version_from_header = request.headers['X-API_VERSION']
109
+ namespace = "ActionController::Serialization::NamespaceLookupTest::#{version_from_header}"
110
+
111
+ render json: book, namespace: namespace
112
+ end
113
+ end
114
+ end
115
+ end
116
+
117
+ tests Api::V3::LookupTestController
118
+
119
+ setup do
120
+ @test_namespace = self.class.parent
121
+ end
122
+
123
+ test 'uses request headers to determine the namespace' do
124
+ request.env['X-API_VERSION'] = 'Api::VHeader'
125
+ get :namespace_set_by_request_headers
126
+
127
+ assert_serializer Api::VHeader::BookSerializer
128
+ end
129
+
130
+ test 'implicitly uses namespaced serializer' do
131
+ get :implicit_namespaced_serializer
132
+
133
+ assert_serializer Api::V3::BookSerializer
134
+
135
+ expected = { 'title' => 'New Post', 'body' => 'Body', 'writer' => { 'name' => 'Bob' }, 'chapters' => [] }
136
+ actual = JSON.parse(@response.body)
137
+
138
+ assert_equal expected, actual
139
+ end
140
+
141
+ test 'implicitly uses namespaced serializer for collection' do
142
+ get :implicit_namespaced_collection_serializer
143
+
144
+ assert_serializer 'ActiveModel::Serializer::CollectionSerializer'
145
+
146
+ expected = [{ 'title' => 'Chapter - Oh' }, { 'title' => 'Chapter - Oh my' }]
147
+ actual = JSON.parse(@response.body)
148
+
149
+ assert_equal expected, actual
150
+ end
151
+
152
+ test 'implicitly uses namespaced serializer for has_many' do
153
+ get :implicit_has_many_namespaced_serializer
154
+
155
+ assert_serializer Api::V3::BookSerializer
156
+
157
+ expected = {
158
+ 'title' => 'New Post',
159
+ 'body' => 'Body', 'writer' => nil,
160
+ 'chapters' => [
161
+ { 'title' => 'Chapter - Odd World' },
162
+ { 'title' => 'Chapter - New World' }
163
+ ]
164
+ }
165
+ actual = JSON.parse(@response.body)
166
+
167
+ assert_equal expected, actual
168
+ end
169
+
170
+ test 'explicit namespace as module' do
171
+ get :explicit_namespace_as_module
172
+
173
+ assert_serializer Api::V2::BookSerializer
174
+
175
+ expected = { 'title' => 'New Post' }
176
+ actual = JSON.parse(@response.body)
177
+
178
+ assert_equal expected, actual
179
+ end
180
+
181
+ test 'explicit namespace as string' do
182
+ get :explicit_namespace_as_string
183
+
184
+ assert_serializer Api::V2::BookSerializer
185
+
186
+ expected = { 'title' => 'New Post' }
187
+ actual = JSON.parse(@response.body)
188
+
189
+ assert_equal expected, actual
190
+ end
191
+
192
+ test 'explicit namespace as symbol' do
193
+ get :explicit_namespace_as_symbol
194
+
195
+ assert_serializer Api::V2::BookSerializer
196
+
197
+ expected = { 'title' => 'New Post' }
198
+ actual = JSON.parse(@response.body)
199
+
200
+ assert_equal expected, actual
201
+ end
202
+
203
+ test 'invalid namespace' do
204
+ get :invalid_namespace
205
+
206
+ assert_serializer ActiveModel::Serializer::Null
207
+
208
+ expected = { 'title' => 'New Post', 'body' => 'Body' }
209
+ actual = JSON.parse(@response.body)
210
+
211
+ assert_equal expected, actual
212
+ end
213
+
214
+ test 'namespace set in before filter' do
215
+ get :namespace_set_in_before_filter
216
+
217
+ assert_serializer Api::V2::BookSerializer
218
+
219
+ expected = { 'title' => 'New Post' }
220
+ actual = JSON.parse(@response.body)
221
+
222
+ assert_equal expected, actual
223
+ end
224
+ end
225
+ end
226
+ end
@@ -60,6 +60,14 @@ module ActiveModelSerializers
60
60
  {
61
61
  value: nil,
62
62
  expected: nil
63
+ },
64
+ {
65
+ value: [
66
+ { some_value: 'value' }
67
+ ],
68
+ expected: [
69
+ { SomeValue: 'value' }
70
+ ]
63
71
  }
64
72
  ]
65
73
  scenarios.each do |s|
@@ -126,6 +134,14 @@ module ActiveModelSerializers
126
134
  {
127
135
  value: nil,
128
136
  expected: nil
137
+ },
138
+ {
139
+ value: [
140
+ { some_value: 'value' }
141
+ ],
142
+ expected: [
143
+ { someValue: 'value' }
144
+ ]
129
145
  }
130
146
  ]
131
147
  scenarios.each do |s|
@@ -188,6 +204,14 @@ module ActiveModelSerializers
188
204
  {
189
205
  value: nil,
190
206
  expected: nil
207
+ },
208
+ {
209
+ value: [
210
+ { 'some_value' => 'value' }
211
+ ],
212
+ expected: [
213
+ { 'some-value' => 'value' }
214
+ ]
191
215
  }
192
216
  ]
193
217
  scenarios.each do |s|
@@ -254,6 +278,14 @@ module ActiveModelSerializers
254
278
  {
255
279
  value: nil,
256
280
  expected: nil
281
+ },
282
+ {
283
+ value: [
284
+ { 'some-value' => 'value' }
285
+ ],
286
+ expected: [
287
+ { 'some_value' => 'value' }
288
+ ]
257
289
  }
258
290
  ]
259
291
  scenarios.each do |s|
@@ -7,5 +7,16 @@ module ActiveModelSerializers
7
7
  def setup
8
8
  @resource = ActiveModelSerializers::Model.new
9
9
  end
10
+
11
+ def test_initialization_with_string_keys
12
+ klass = Class.new(ActiveModelSerializers::Model) do
13
+ attr_accessor :key
14
+ end
15
+ value = 'value'
16
+
17
+ model_instance = klass.new('key' => value)
18
+
19
+ assert_equal model_instance.read_attribute_for_serialization(:key), value
20
+ end
10
21
  end
11
22
  end
@@ -0,0 +1,43 @@
1
+ require 'test_helper'
2
+
3
+ module ActiveModelSerializers
4
+ module Adapter
5
+ class AttributesTest < ActiveSupport::TestCase
6
+ class Person
7
+ include ActiveModel::Model
8
+ include ActiveModel::Serialization
9
+
10
+ attr_accessor :first_name, :last_name
11
+ end
12
+
13
+ class PersonSerializer < ActiveModel::Serializer
14
+ attributes :first_name, :last_name
15
+ end
16
+
17
+ def setup
18
+ ActionController::Base.cache_store.clear
19
+ end
20
+
21
+ def test_serializable_hash
22
+ person = Person.new(first_name: 'Arthur', last_name: 'Dent')
23
+ serializer = PersonSerializer.new(person)
24
+ adapter = ActiveModelSerializers::Adapter::Attributes.new(serializer)
25
+
26
+ assert_equal({ first_name: 'Arthur', last_name: 'Dent' },
27
+ adapter.serializable_hash)
28
+ end
29
+
30
+ def test_serializable_hash_with_transform_key_casing
31
+ person = Person.new(first_name: 'Arthur', last_name: 'Dent')
32
+ serializer = PersonSerializer.new(person)
33
+ adapter = ActiveModelSerializers::Adapter::Attributes.new(
34
+ serializer,
35
+ key_transform: :camel_lower
36
+ )
37
+
38
+ assert_equal({ firstName: 'Arthur', lastName: 'Dent' },
39
+ adapter.serializable_hash)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -15,7 +15,7 @@ module ActiveModelSerializers
15
15
  @adapter = ActiveModelSerializers::Adapter::Json.new(serializer, options)
16
16
  end
17
17
 
18
- Post = Class.new(::Model)
18
+ class Post < ::Model; end
19
19
  class PostSerializer < ActiveModel::Serializer
20
20
  attributes :id, :title, :body, :publish_at
21
21
  end
@@ -4,7 +4,10 @@ module ActiveModelSerializers
4
4
  module Adapter
5
5
  class JsonApi
6
6
  class FieldsTest < ActiveSupport::TestCase
7
- Post = Class.new(::Model)
7
+ class Post < ::Model; end
8
+ class Author < ::Model; end
9
+ class Comment < ::Model; end
10
+
8
11
  class PostSerializer < ActiveModel::Serializer
9
12
  type 'posts'
10
13
  attributes :title, :body
@@ -12,13 +15,11 @@ module ActiveModelSerializers
12
15
  has_many :comments
13
16
  end
14
17
 
15
- Author = Class.new(::Model)
16
18
  class AuthorSerializer < ActiveModel::Serializer
17
19
  type 'authors'
18
20
  attributes :name, :birthday
19
21
  end
20
22
 
21
- Comment = Class.new(::Model)
22
23
  class CommentSerializer < ActiveModel::Serializer
23
24
  type 'comments'
24
25
  attributes :body