active_model_serializers 0.10.4 → 0.10.5

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