active_model_serializers 0.10.4 → 0.10.5

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 (53) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +7 -3
  3. data/CHANGELOG.md +25 -1
  4. data/README.md +4 -4
  5. data/active_model_serializers.gemspec +1 -1
  6. data/appveyor.yml +9 -3
  7. data/docs/general/logging.md +7 -0
  8. data/docs/general/serializers.md +3 -3
  9. data/docs/howto/add_pagination_links.md +13 -13
  10. data/docs/howto/add_relationship_links.md +24 -21
  11. data/docs/howto/outside_controller_use.md +3 -2
  12. data/docs/howto/serialize_poro.md +46 -5
  13. data/docs/howto/upgrade_from_0_8_to_0_10.md +1 -1
  14. data/lib/active_model/serializer/version.rb +1 -1
  15. data/lib/active_model_serializers.rb +8 -0
  16. data/lib/active_model_serializers/model.rb +108 -30
  17. data/lib/active_model_serializers/test/schema.rb +2 -2
  18. data/lib/generators/rails/resource_override.rb +1 -1
  19. data/test/action_controller/adapter_selector_test.rb +11 -2
  20. data/test/action_controller/json_api/fields_test.rb +15 -6
  21. data/test/action_controller/json_api/transform_test.rb +11 -3
  22. data/test/action_controller/namespace_lookup_test.rb +14 -8
  23. data/test/action_controller/serialization_scope_name_test.rb +12 -6
  24. data/test/action_controller/serialization_test.rb +1 -1
  25. data/test/active_model_serializers/model_test.rb +122 -2
  26. data/test/active_model_serializers/railtie_test_isolated.rb +12 -7
  27. data/test/active_model_serializers/register_jsonapi_renderer_test_isolated.rb +37 -19
  28. data/test/adapter/attributes_test.rb +2 -5
  29. data/test/adapter/json/has_many_test.rb +10 -2
  30. data/test/adapter/json_api/fields_test.rb +11 -3
  31. data/test/adapter/json_api/has_many_test.rb +10 -2
  32. data/test/adapter/json_api/include_data_if_sideloaded_test.rb +22 -5
  33. data/test/adapter/json_api/linked_test.rb +3 -3
  34. data/test/adapter/json_api/links_test.rb +1 -1
  35. data/test/adapter/json_api/relationship_test.rb +1 -1
  36. data/test/adapter/json_api/transform_test.rb +11 -3
  37. data/test/cache_test.rb +100 -28
  38. data/test/collection_serializer_test.rb +23 -10
  39. data/test/fixtures/active_record.rb +45 -10
  40. data/test/fixtures/poro.rb +115 -176
  41. data/test/generators/serializer_generator_test.rb +1 -0
  42. data/test/grape_test.rb +20 -2
  43. data/test/serializers/associations_test.rb +28 -7
  44. data/test/serializers/attribute_test.rb +4 -2
  45. data/test/serializers/caching_configuration_test_isolated.rb +6 -6
  46. data/test/serializers/options_test.rb +17 -6
  47. data/test/serializers/read_attribute_for_serialization_test.rb +3 -3
  48. data/test/serializers/serialization_test.rb +2 -2
  49. data/test/serializers/serializer_for_test.rb +6 -6
  50. data/test/serializers/serializer_for_with_namespace_test.rb +6 -5
  51. data/test/support/rails_app.rb +2 -0
  52. data/test/test_helper.rb +12 -0
  53. metadata +13 -7
@@ -3,14 +3,27 @@ require 'test_helper'
3
3
  module ActiveModel
4
4
  class Serializer
5
5
  class CollectionSerializerTest < ActiveSupport::TestCase
6
+ class SingularModel < ::Model; end
7
+ class SingularModelSerializer < ActiveModel::Serializer
8
+ end
9
+ class HasManyModel < ::Model
10
+ associations :singular_models
11
+ end
12
+ class HasManyModelSerializer < ActiveModel::Serializer
13
+ has_many :singular_models
14
+
15
+ def custom_options
16
+ instance_options
17
+ end
18
+ end
6
19
  class MessagesSerializer < ActiveModel::Serializer
7
20
  type 'messages'
8
21
  end
9
22
 
10
23
  def setup
11
- @comment = Comment.new
12
- @post = Post.new
13
- @resource = build_named_collection @comment, @post
24
+ @singular_model = SingularModel.new
25
+ @has_many_model = HasManyModel.new
26
+ @resource = build_named_collection @singular_model, @has_many_model
14
27
  @serializer = collection_serializer.new(@resource, some: :options)
15
28
  end
16
29
 
@@ -34,29 +47,29 @@ module ActiveModel
34
47
  def test_each_object_should_be_serialized_with_appropriate_serializer
35
48
  serializers = @serializer.to_a
36
49
 
37
- assert_kind_of CommentSerializer, serializers.first
38
- assert_kind_of Comment, serializers.first.object
50
+ assert_kind_of SingularModelSerializer, serializers.first
51
+ assert_kind_of SingularModel, serializers.first.object
39
52
 
40
- assert_kind_of PostSerializer, serializers.last
41
- assert_kind_of Post, serializers.last.object
53
+ assert_kind_of HasManyModelSerializer, serializers.last
54
+ assert_kind_of HasManyModel, serializers.last.object
42
55
 
43
56
  assert_equal :options, serializers.last.custom_options[:some]
44
57
  end
45
58
 
46
59
  def test_serializer_option_not_passed_to_each_serializer
47
- serializers = collection_serializer.new([@post], serializer: PostSerializer).to_a
60
+ serializers = collection_serializer.new([@has_many_model], serializer: HasManyModelSerializer).to_a
48
61
 
49
62
  refute serializers.first.custom_options.key?(:serializer)
50
63
  end
51
64
 
52
65
  def test_root_default
53
- @serializer = collection_serializer.new([@comment, @post])
66
+ @serializer = collection_serializer.new([@singular_model, @has_many_model])
54
67
  assert_nil @serializer.root
55
68
  end
56
69
 
57
70
  def test_root
58
71
  expected = 'custom_root'
59
- @serializer = collection_serializer.new([@comment, @post], root: expected)
72
+ @serializer = collection_serializer.new([@singular_model, @has_many_model], root: expected)
60
73
  assert_equal expected, @serializer.root
61
74
  end
62
75
 
@@ -47,16 +47,6 @@ module ARModels
47
47
  has_many :comments
48
48
  belongs_to :author
49
49
  end
50
-
51
- class Comment < ActiveRecord::Base
52
- belongs_to :post
53
- belongs_to :author
54
- end
55
-
56
- class Author < ActiveRecord::Base
57
- has_many :posts
58
- end
59
-
60
50
  class PostSerializer < ActiveModel::Serializer
61
51
  attributes :id, :title, :body
62
52
 
@@ -64,15 +54,60 @@ module ARModels
64
54
  belongs_to :author
65
55
  end
66
56
 
57
+ class Comment < ActiveRecord::Base
58
+ belongs_to :post
59
+ belongs_to :author
60
+ end
67
61
  class CommentSerializer < ActiveModel::Serializer
68
62
  attributes :id, :contents
69
63
 
70
64
  belongs_to :author
71
65
  end
72
66
 
67
+ class Author < ActiveRecord::Base
68
+ has_many :posts
69
+ end
73
70
  class AuthorSerializer < ActiveModel::Serializer
74
71
  attributes :id, :name
75
72
 
76
73
  has_many :posts
77
74
  end
78
75
  end
76
+
77
+ class Employee < ActiveRecord::Base
78
+ has_many :pictures, as: :imageable
79
+ has_many :object_tags, as: :taggable
80
+ end
81
+
82
+ class PolymorphicSimpleSerializer < ActiveModel::Serializer
83
+ attributes :id
84
+ end
85
+
86
+ class ObjectTag < ActiveRecord::Base
87
+ belongs_to :poly_tag
88
+ belongs_to :taggable, polymorphic: true
89
+ end
90
+ class PolymorphicObjectTagSerializer < ActiveModel::Serializer
91
+ attributes :id
92
+ has_many :taggable, serializer: PolymorphicSimpleSerializer, polymorphic: true
93
+ end
94
+
95
+ class PolyTag < ActiveRecord::Base
96
+ has_many :object_tags
97
+ end
98
+ class PolymorphicTagSerializer < ActiveModel::Serializer
99
+ attributes :id, :phrase
100
+ has_many :object_tags, serializer: PolymorphicObjectTagSerializer
101
+ end
102
+
103
+ class Picture < ActiveRecord::Base
104
+ belongs_to :imageable, polymorphic: true
105
+ has_many :object_tags, as: :taggable
106
+ end
107
+ class PolymorphicHasManySerializer < ActiveModel::Serializer
108
+ attributes :id, :name
109
+ end
110
+ class PolymorphicBelongsToSerializer < ActiveModel::Serializer
111
+ attributes :id, :title
112
+ has_one :imageable, serializer: PolymorphicHasManySerializer, polymorphic: true
113
+ end
@@ -1,25 +1,28 @@
1
- verbose = $VERBOSE
2
- $VERBOSE = nil
3
1
  class Model < ActiveModelSerializers::Model
4
- FILE_DIGEST = Digest::MD5.hexdigest(File.open(__FILE__).read)
2
+ rand(2).zero? && derive_attributes_from_names_and_fix_accessors
5
3
 
6
- ### Helper methods, not required to be serializable
4
+ attr_writer :id
7
5
 
8
- # Convenience when not adding @attributes readers and writers
9
- def method_missing(meth, *args)
10
- if meth.to_s =~ /^(.*)=$/
11
- attributes[Regexp.last_match(1).to_sym] = args[0]
12
- elsif attributes.key?(meth)
13
- attributes[meth]
14
- else
15
- super
6
+ # At this time, just for organization of intent
7
+ class_attribute :association_names
8
+ self.association_names = []
9
+
10
+ def self.associations(*names)
11
+ self.association_names |= names.map(&:to_sym)
12
+ # Silence redefinition of methods warnings
13
+ ActiveModelSerializers.silence_warnings do
14
+ attr_accessor(*names)
16
15
  end
17
16
  end
18
17
 
19
- # required for ActiveModel::AttributeAssignment#_assign_attribute
20
- # in Rails 5
21
- def respond_to_missing?(method_name, _include_private = false)
22
- attributes.key?(method_name.to_s.tr('=', '').to_sym) || super
18
+ def associations
19
+ association_names.each_with_object({}) do |association_name, result|
20
+ result[association_name] = public_send(association_name).freeze
21
+ end.with_indifferent_access.freeze
22
+ end
23
+
24
+ def attributes
25
+ super.except(*association_names)
23
26
  end
24
27
  end
25
28
 
@@ -30,67 +33,59 @@ end
30
33
  # model = ModelWithErrors.new
31
34
  # model.validate! # => ["cannot be nil"]
32
35
  # model.errors.full_messages # => ["name cannot be nil"]
33
- class ModelWithErrors < ::ActiveModelSerializers::Model
34
- attr_accessor :name
36
+ class ModelWithErrors < Model
37
+ attributes :name
35
38
  end
36
39
 
37
40
  class Profile < Model
41
+ attributes :name, :description
42
+ associations :comments
38
43
  end
39
-
40
44
  class ProfileSerializer < ActiveModel::Serializer
41
45
  attributes :name, :description
42
-
43
- # TODO: is this used anywhere?
44
- def arguments_passed_in?
45
- instance_options[:my_options] == :accessible
46
- end
47
46
  end
48
-
49
47
  class ProfilePreviewSerializer < ActiveModel::Serializer
50
48
  attributes :name
51
49
  end
52
50
 
53
- class Post < Model; end
54
- class Like < Model; end
55
- class Author < Model; end
56
- class Bio < Model; end
57
- class Blog < Model; end
58
- class Role < Model; end
59
- class User < Model; end
60
- class Location < Model; end
61
- class Place < Model; end
62
- class Tag < Model; end
63
- class VirtualValue < Model; end
64
- class Comment < Model
65
- # Uses a custom non-time-based cache key
66
- def cache_key
67
- "#{self.class.name.downcase}/#{id}"
68
- end
51
+ class Author < Model
52
+ attributes :name
53
+ associations :posts, :bio, :roles, :comments
69
54
  end
55
+ class AuthorSerializer < ActiveModel::Serializer
56
+ cache key: 'writer', skip_digest: true
57
+ attribute :id
58
+ attribute :name
70
59
 
71
- class Employee < ActiveRecord::Base
72
- has_many :pictures, as: :imageable
73
- has_many :object_tags, as: :taggable
60
+ has_many :posts
61
+ has_many :roles
62
+ has_one :bio
74
63
  end
75
-
76
- class ObjectTag < ActiveRecord::Base
77
- belongs_to :poly_tag
78
- belongs_to :taggable, polymorphic: true
64
+ class AuthorPreviewSerializer < ActiveModel::Serializer
65
+ attributes :id
66
+ has_many :posts
79
67
  end
80
68
 
81
- class Picture < ActiveRecord::Base
82
- belongs_to :imageable, polymorphic: true
83
- has_many :object_tags, as: :taggable
69
+ class Comment < Model
70
+ attributes :body, :date
71
+ associations :post, :author, :likes
84
72
  end
85
-
86
- class PolyTag < ActiveRecord::Base
87
- has_many :object_tags
73
+ class CommentSerializer < ActiveModel::Serializer
74
+ cache expires_in: 1.day, skip_digest: true
75
+ attributes :id, :body
76
+ belongs_to :post
77
+ belongs_to :author
88
78
  end
79
+ class CommentPreviewSerializer < ActiveModel::Serializer
80
+ attributes :id
89
81
 
90
- module Spam
91
- class UnrelatedLink < Model; end
82
+ belongs_to :post
92
83
  end
93
84
 
85
+ class Post < Model
86
+ attributes :title, :body
87
+ associations :author, :comments, :blog, :tags, :related
88
+ end
94
89
  class PostSerializer < ActiveModel::Serializer
95
90
  cache key: 'post', expires_in: 0.1, skip_digest: true
96
91
  attributes :id, :title, :body
@@ -102,75 +97,28 @@ class PostSerializer < ActiveModel::Serializer
102
97
  def blog
103
98
  Blog.new(id: 999, name: 'Custom blog')
104
99
  end
105
-
106
- # TODO: is this used anywhere?
107
- def custom_options
108
- instance_options
109
- end
110
100
  end
111
-
112
101
  class SpammyPostSerializer < ActiveModel::Serializer
113
102
  attributes :id
114
103
  has_many :related
115
104
  end
105
+ class PostPreviewSerializer < ActiveModel::Serializer
106
+ attributes :title, :body, :id
116
107
 
117
- class CommentSerializer < ActiveModel::Serializer
118
- cache expires_in: 1.day, skip_digest: true
119
- attributes :id, :body
120
-
121
- belongs_to :post
122
- belongs_to :author
123
-
124
- def custom_options
125
- instance_options
126
- end
127
- end
128
-
129
- class AuthorSerializer < ActiveModel::Serializer
130
- cache key: 'writer', skip_digest: true
131
- attribute :id
132
- attribute :name
133
-
134
- has_many :posts
135
- has_many :roles
136
- has_one :bio
137
- end
138
-
139
- class RoleSerializer < ActiveModel::Serializer
140
- cache only: [:name, :slug], skip_digest: true
141
- attributes :id, :name, :description
142
- attribute :friendly_id, key: :slug
143
-
144
- def friendly_id
145
- "#{object.name}-#{object.id}"
146
- end
147
-
148
- belongs_to :author
149
- end
150
-
151
- class LikeSerializer < ActiveModel::Serializer
152
- attributes :id, :time
153
-
154
- belongs_to :likeable
108
+ has_many :comments, serializer: ::CommentPreviewSerializer
109
+ belongs_to :author, serializer: ::AuthorPreviewSerializer
155
110
  end
156
-
157
- class LocationSerializer < ActiveModel::Serializer
158
- cache only: [:address], skip_digest: true
159
- attributes :id, :lat, :lng
160
-
161
- belongs_to :place, key: :address
162
-
163
- def place
164
- 'Nowhere'
165
- end
111
+ class PostWithCustomKeysSerializer < ActiveModel::Serializer
112
+ attributes :id
113
+ has_many :comments, key: :reviews
114
+ belongs_to :author, key: :writer
115
+ has_one :blog, key: :site
166
116
  end
167
117
 
168
- class PlaceSerializer < ActiveModel::Serializer
169
- attributes :id, :name
170
-
171
- has_many :locations
118
+ class Bio < Model
119
+ attributes :content, :rating
120
+ associations :author
172
121
  end
173
-
174
122
  class BioSerializer < ActiveModel::Serializer
175
123
  cache except: [:content], skip_digest: true
176
124
  attributes :id, :content, :rating
@@ -178,6 +126,10 @@ class BioSerializer < ActiveModel::Serializer
178
126
  belongs_to :author
179
127
  end
180
128
 
129
+ class Blog < Model
130
+ attributes :name, :type, :special_attribute
131
+ associations :writer, :articles
132
+ end
181
133
  class BlogSerializer < ActiveModel::Serializer
182
134
  cache key: 'blog'
183
135
  attributes :id, :name
@@ -185,61 +137,76 @@ class BlogSerializer < ActiveModel::Serializer
185
137
  belongs_to :writer
186
138
  has_many :articles
187
139
  end
188
-
189
- class PaginatedSerializer < ActiveModel::Serializer::CollectionSerializer
190
- def json_key
191
- 'paginated'
192
- end
193
- end
194
-
195
140
  class AlternateBlogSerializer < ActiveModel::Serializer
196
141
  attribute :id
197
142
  attribute :name, key: :title
198
143
  end
199
-
200
144
  class CustomBlogSerializer < ActiveModel::Serializer
201
145
  attribute :id
202
146
  attribute :special_attribute
203
-
204
147
  has_many :articles
205
148
  end
206
149
 
207
- class CommentPreviewSerializer < ActiveModel::Serializer
208
- attributes :id
209
-
210
- belongs_to :post
150
+ class Role < Model
151
+ attributes :name, :description, :special_attribute
152
+ associations :author
211
153
  end
154
+ class RoleSerializer < ActiveModel::Serializer
155
+ cache only: [:name, :slug], skip_digest: true
156
+ attributes :id, :name, :description
157
+ attribute :friendly_id, key: :slug
158
+ belongs_to :author
212
159
 
213
- class AuthorPreviewSerializer < ActiveModel::Serializer
214
- attributes :id
160
+ def friendly_id
161
+ "#{object.name}-#{object.id}"
162
+ end
163
+ end
215
164
 
216
- has_many :posts
165
+ class Location < Model
166
+ attributes :lat, :lng
167
+ associations :place
217
168
  end
169
+ class LocationSerializer < ActiveModel::Serializer
170
+ cache only: [:address], skip_digest: true
171
+ attributes :id, :lat, :lng
218
172
 
219
- class PostPreviewSerializer < ActiveModel::Serializer
220
- attributes :title, :body, :id
173
+ belongs_to :place, key: :address
221
174
 
222
- has_many :comments, serializer: CommentPreviewSerializer
223
- belongs_to :author, serializer: AuthorPreviewSerializer
175
+ def place
176
+ 'Nowhere'
177
+ end
224
178
  end
225
179
 
226
- class PostWithTagsSerializer < ActiveModel::Serializer
227
- attributes :id
228
-
229
- has_many :tags
180
+ class Place < Model
181
+ attributes :name
182
+ associations :locations
183
+ end
184
+ class PlaceSerializer < ActiveModel::Serializer
185
+ attributes :id, :name
186
+ has_many :locations
230
187
  end
231
188
 
232
- class PostWithCustomKeysSerializer < ActiveModel::Serializer
233
- attributes :id
189
+ class Like < Model
190
+ attributes :time
191
+ associations :likeable
192
+ end
193
+ class LikeSerializer < ActiveModel::Serializer
194
+ attributes :id, :time
195
+ belongs_to :likeable
196
+ end
234
197
 
235
- has_many :comments, key: :reviews
236
- belongs_to :author, key: :writer
237
- has_one :blog, key: :site
198
+ module Spam
199
+ class UnrelatedLink < Model
200
+ end
201
+ class UnrelatedLinkSerializer < ActiveModel::Serializer
202
+ cache only: [:id]
203
+ attributes :id
204
+ end
238
205
  end
239
206
 
207
+ class VirtualValue < Model; end
240
208
  class VirtualValueSerializer < ActiveModel::Serializer
241
209
  attributes :id
242
-
243
210
  has_many :reviews, virtual_value: [{ type: 'reviews', id: '1' },
244
211
  { type: 'reviews', id: '2' }]
245
212
  has_one :maker, virtual_value: { type: 'makers', id: '1' }
@@ -251,36 +218,8 @@ class VirtualValueSerializer < ActiveModel::Serializer
251
218
  end
252
219
  end
253
220
 
254
- class PolymorphicHasManySerializer < ActiveModel::Serializer
255
- attributes :id, :name
256
- end
257
-
258
- class PolymorphicBelongsToSerializer < ActiveModel::Serializer
259
- attributes :id, :title
260
-
261
- has_one :imageable, serializer: PolymorphicHasManySerializer, polymorphic: true
262
- end
263
-
264
- class PolymorphicSimpleSerializer < ActiveModel::Serializer
265
- attributes :id
266
- end
267
-
268
- class PolymorphicObjectTagSerializer < ActiveModel::Serializer
269
- attributes :id
270
-
271
- has_many :taggable, serializer: PolymorphicSimpleSerializer, polymorphic: true
272
- end
273
-
274
- class PolymorphicTagSerializer < ActiveModel::Serializer
275
- attributes :id, :phrase
276
-
277
- has_many :object_tags, serializer: PolymorphicObjectTagSerializer
278
- end
279
-
280
- module Spam
281
- class UnrelatedLinkSerializer < ActiveModel::Serializer
282
- cache only: [:id]
283
- attributes :id
221
+ class PaginatedSerializer < ActiveModel::Serializer::CollectionSerializer
222
+ def json_key
223
+ 'paginated'
284
224
  end
285
225
  end
286
- $VERBOSE = verbose