active_model_serializers 0.6.0 → 0.7.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.
@@ -0,0 +1,226 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ module Associations #:nodoc:
4
+ class Config #:nodoc:
5
+ class_attribute :options
6
+
7
+ def self.refine(name, class_options)
8
+ current_class = self
9
+
10
+ Class.new(self) do
11
+ singleton_class.class_eval do
12
+ define_method(:to_s) do
13
+ "(subclass of #{current_class.name})"
14
+ end
15
+
16
+ alias inspect to_s
17
+ end
18
+
19
+ self.options = class_options
20
+ end
21
+ end
22
+
23
+ self.options = {}
24
+
25
+ def initialize(name, source, options={})
26
+ @name = name
27
+ @source = source
28
+ @options = options
29
+ end
30
+
31
+ def option(key, default=nil)
32
+ if @options.key?(key)
33
+ @options[key]
34
+ elsif self.class.options.key?(key)
35
+ self.class.options[key]
36
+ else
37
+ default
38
+ end
39
+ end
40
+
41
+ def target_serializer
42
+ serializer = option(:serializer)
43
+ serializer.is_a?(String) ? serializer.constantize : serializer
44
+ end
45
+
46
+ def source_serializer
47
+ @source
48
+ end
49
+
50
+ def key
51
+ option(:key) || @name
52
+ end
53
+
54
+ def root
55
+ option(:root) || @name
56
+ end
57
+
58
+ def name
59
+ option(:name) || @name
60
+ end
61
+
62
+ def associated_object
63
+ option(:value) || source_serializer.send(name)
64
+ end
65
+
66
+ def embed_ids?
67
+ option(:embed, source_serializer._embed) == :ids
68
+ end
69
+
70
+ def embed_objects?
71
+ option(:embed, source_serializer._embed) == :objects
72
+ end
73
+
74
+ def embed_in_root?
75
+ option(:include, source_serializer._root_embed)
76
+ end
77
+
78
+ def embeddable?
79
+ !associated_object.nil?
80
+ end
81
+
82
+ protected
83
+
84
+ def find_serializable(object)
85
+ if target_serializer
86
+ target_serializer.new(object, source_serializer.options)
87
+ elsif object.respond_to?(:active_model_serializer) && (ams = object.active_model_serializer)
88
+ ams.new(object, source_serializer.options)
89
+ else
90
+ object
91
+ end
92
+ end
93
+ end
94
+
95
+ class HasMany < Config #:nodoc:
96
+ def key
97
+ if key = option(:key)
98
+ key
99
+ elsif embed_ids?
100
+ "#{@name.to_s.singularize}_ids".to_sym
101
+ else
102
+ @name
103
+ end
104
+ end
105
+
106
+ def embed_key
107
+ if key = option(:embed_key)
108
+ key
109
+ else
110
+ :id
111
+ end
112
+ end
113
+
114
+ def serialize
115
+ associated_object.map do |item|
116
+ find_serializable(item).serializable_hash
117
+ end
118
+ end
119
+
120
+ def serializables
121
+ associated_object.map do |item|
122
+ find_serializable(item)
123
+ end
124
+ end
125
+
126
+ def serialize_ids
127
+ # Use pluck or select_columns if available
128
+ # return collection.ids if collection.respond_to?(:ids)
129
+ ids_key = "#{key.to_s.singularize}_ids"
130
+
131
+ if !option(:include) && !option(:embed_key) && source_serializer.object.respond_to?(ids_key)
132
+ source_serializer.object.send(ids_key)
133
+ else
134
+ associated_object.map do |item|
135
+ item.read_attribute_for_serialization(embed_key)
136
+ end
137
+ end
138
+ end
139
+ end
140
+
141
+ class HasOne < Config #:nodoc:
142
+ def embeddable?
143
+ if polymorphic? && associated_object.nil?
144
+ false
145
+ else
146
+ true
147
+ end
148
+ end
149
+
150
+ def polymorphic?
151
+ option :polymorphic
152
+ end
153
+
154
+ def root
155
+ if root = option(:root)
156
+ root
157
+ elsif polymorphic?
158
+ associated_object.class.to_s.pluralize.demodulize.underscore.to_sym
159
+ else
160
+ @name.to_s.pluralize.to_sym
161
+ end
162
+ end
163
+
164
+ def key
165
+ if key = option(:key)
166
+ key
167
+ elsif embed_ids? && !polymorphic?
168
+ "#{@name}_id".to_sym
169
+ else
170
+ @name
171
+ end
172
+ end
173
+
174
+ def embed_key
175
+ if key = option(:embed_key)
176
+ key
177
+ else
178
+ :id
179
+ end
180
+ end
181
+
182
+ def polymorphic_key
183
+ associated_object.class.to_s.demodulize.underscore.to_sym
184
+ end
185
+
186
+ def serialize
187
+ object = associated_object
188
+
189
+ if object && polymorphic?
190
+ {
191
+ :type => polymorphic_key,
192
+ polymorphic_key => find_serializable(object).serializable_hash
193
+ }
194
+ elsif object
195
+ find_serializable(object).serializable_hash
196
+ end
197
+ end
198
+
199
+ def serializables
200
+ object = associated_object
201
+ value = object && find_serializable(object)
202
+ value ? [value] : []
203
+ end
204
+
205
+ def serialize_ids
206
+ if polymorphic?
207
+ if associated_object
208
+ {
209
+ :type => polymorphic_key,
210
+ :id => associated_object.read_attribute_for_serialization(embed_key)
211
+ }
212
+ else
213
+ nil
214
+ end
215
+ elsif !option(:embed_key) && source_serializer.object.respond_to?("#{name}_id")
216
+ source_serializer.object.send("#{name}_id")
217
+ elsif associated_object
218
+ associated_object.read_attribute_for_serialization(embed_key)
219
+ else
220
+ nil
221
+ end
222
+ end
223
+ end
224
+ end
225
+ end
226
+ end
@@ -1,5 +1,5 @@
1
1
  module ActiveModel
2
2
  class Serializer
3
- VERSION = "0.6.0"
3
+ VERSION = "0.7.0"
4
4
  end
5
5
  end
@@ -2,9 +2,9 @@ require "active_support"
2
2
  require "active_support/core_ext/string/inflections"
3
3
  require "active_support/notifications"
4
4
  require "active_model"
5
- require "active_model/ordered_set"
6
5
  require "active_model/array_serializer"
7
6
  require "active_model/serializer"
7
+ require "active_model/serializer/associations"
8
8
  require "set"
9
9
 
10
10
  if defined?(Rails)
@@ -33,14 +33,12 @@ module ActiveModel::SerializerSupport
33
33
  module ClassMethods #:nodoc:
34
34
  if "".respond_to?(:safe_constantize)
35
35
  def active_model_serializer
36
- @active_model_serializer ||= "#{self.name}Serializer".safe_constantize
36
+ "#{self.name}Serializer".safe_constantize
37
37
  end
38
38
  else
39
39
  def active_model_serializer
40
- return @active_model_serializer if defined?(@active_model_serializer)
41
-
42
40
  begin
43
- @active_model_serializer = "#{self.name}Serializer".constantize
41
+ "#{self.name}Serializer".constantize
44
42
  rescue NameError => e
45
43
  raise unless e.message =~ /uninitialized constant/
46
44
  end
@@ -0,0 +1,55 @@
1
+ require "test_helper"
2
+ require "test_fakes"
3
+
4
+ class ArraySerializerTest < ActiveModel::TestCase
5
+ # serialize different typed objects
6
+ def test_array_serializer
7
+ model = Model.new
8
+ user = User.new
9
+ comments = Comment.new(:title => "Comment1", :id => 1)
10
+
11
+ array = [model, user, comments]
12
+ serializer = array.active_model_serializer.new(array, :scope => {:scope => true})
13
+ assert_equal([
14
+ { :model => "Model" },
15
+ { :last_name => "Valim", :ok => true, :first_name => "Jose", :scope => true },
16
+ { :title => "Comment1" }
17
+ ], serializer.as_json)
18
+ end
19
+
20
+ def test_array_serializer_with_root
21
+ comment1 = Comment.new(:title => "Comment1", :id => 1)
22
+ comment2 = Comment.new(:title => "Comment2", :id => 2)
23
+
24
+ array = [ comment1, comment2 ]
25
+
26
+ serializer = array.active_model_serializer.new(array, :root => :comments)
27
+
28
+ assert_equal({ :comments => [
29
+ { :title => "Comment1" },
30
+ { :title => "Comment2" }
31
+ ]}, serializer.as_json)
32
+ end
33
+
34
+ def test_array_serializer_with_hash
35
+ hash = {:value => "something"}
36
+ array = [hash]
37
+ serializer = array.active_model_serializer.new(array, :root => :items)
38
+ assert_equal({ :items => [ hash.as_json ]}, serializer.as_json)
39
+ end
40
+
41
+ def test_array_serializer_with_specified_seriailizer
42
+ post1 = Post.new(:title => "Post1", :author => "Author1", :id => 1)
43
+ post2 = Post.new(:title => "Post2", :author => "Author2", :id => 2)
44
+
45
+ array = [ post1, post2 ]
46
+
47
+ serializer = array.active_model_serializer.new array, :each_serializer => CustomPostSerializer
48
+
49
+ assert_equal([
50
+ { :title => "Post1" },
51
+ { :title => "Post2" }
52
+ ], serializer.as_json)
53
+ end
54
+
55
+ end
@@ -30,30 +30,29 @@ class AssociationTest < ActiveModel::TestCase
30
30
  end
31
31
 
32
32
  def setup
33
+ @hash = {}
34
+ @root_hash = {}
35
+
33
36
  @post = Model.new(:title => "New Post", :body => "Body")
34
- @comment = Model.new(:id => 1, :body => "ZOMG A COMMENT")
37
+ @comment = Model.new(:id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT")
35
38
  @post.comments = [ @comment ]
36
39
  @post.comment = @comment
37
40
 
38
41
  @comment_serializer_class = def_serializer do
39
- attributes :id, :body
42
+ attributes :id, :external_id, :body
40
43
  end
41
44
 
42
45
  @post_serializer_class = def_serializer do
43
46
  attributes :title, :body
44
47
  end
45
48
 
46
- @post_serializer = @post_serializer_class.new(@post)
47
-
48
- @hash = {}
49
- @root_hash = {}
49
+ @post_serializer = @post_serializer_class.new(@post, :hash => @root_hash)
50
50
  end
51
51
 
52
52
  def include!(key, options={})
53
53
  @post_serializer.include! key, {
54
54
  :embed => :ids,
55
55
  :include => true,
56
- :hash => @root_hash,
57
56
  :node => @hash,
58
57
  :serializer => @comment_serializer_class
59
58
  }.merge(options)
@@ -61,7 +60,6 @@ class AssociationTest < ActiveModel::TestCase
61
60
 
62
61
  def include_bare!(key, options={})
63
62
  @post_serializer.include! key, {
64
- :hash => @root_hash,
65
63
  :node => @hash,
66
64
  :serializer => @comment_serializer_class
67
65
  }.merge(options)
@@ -72,12 +70,12 @@ class AssociationTest < ActiveModel::TestCase
72
70
  include! :comments, :value => @post.comments
73
71
 
74
72
  assert_equal({
75
- :comments => [ 1 ]
73
+ :comment_ids => [ 1 ]
76
74
  }, @hash)
77
75
 
78
76
  assert_equal({
79
77
  :comments => [
80
- { :id => 1, :body => "ZOMG A COMMENT" }
78
+ { :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" }
81
79
  ]
82
80
  }, @root_hash)
83
81
  end
@@ -93,7 +91,7 @@ class AssociationTest < ActiveModel::TestCase
93
91
  include! :comments, :value => @post.comments, :embed => :ids, :include => false
94
92
 
95
93
  assert_equal({
96
- :comments => [ 1 ]
94
+ :comment_ids => [ 1 ]
97
95
  }, @hash)
98
96
 
99
97
  assert_equal({}, @root_hash)
@@ -103,11 +101,11 @@ class AssociationTest < ActiveModel::TestCase
103
101
  include! :comment, :value => @post.comment
104
102
 
105
103
  assert_equal({
106
- :comment => 1
104
+ :comment_id => 1
107
105
  }, @hash)
108
106
 
109
107
  assert_equal({
110
- :comments => [{ :id => 1, :body => "ZOMG A COMMENT" }]
108
+ :comments => [{ :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" }]
111
109
  }, @root_hash)
112
110
  end
113
111
  end
@@ -121,12 +119,12 @@ class AssociationTest < ActiveModel::TestCase
121
119
  include! :comments
122
120
 
123
121
  assert_equal({
124
- :comments => [ 1 ]
122
+ :comment_ids => [ 1 ]
125
123
  }, @hash)
126
124
 
127
125
  assert_equal({
128
126
  :comments => [
129
- { :id => 1, :body => "ZOMG A COMMENT" }
127
+ { :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" }
130
128
  ]
131
129
  }, @root_hash)
132
130
  end
@@ -139,12 +137,12 @@ class AssociationTest < ActiveModel::TestCase
139
137
  include! :comment
140
138
 
141
139
  assert_equal({
142
- :comment => 1
140
+ :comment_id => 1
143
141
  }, @hash)
144
142
 
145
143
  assert_equal({
146
144
  :comments => [
147
- { :id => 1, :body => "ZOMG A COMMENT" }
145
+ { :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" }
148
146
  ]
149
147
  }, @root_hash)
150
148
  end
@@ -161,26 +159,98 @@ class AssociationTest < ActiveModel::TestCase
161
159
  }, @hash)
162
160
 
163
161
  assert_equal({
164
- :custom_comments => [
165
- { :id => 1, :body => "ZOMG A COMMENT" }
162
+ :comments => [
163
+ { :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" }
166
164
  ]
167
165
  }, @root_hash)
168
166
  end
169
167
 
170
168
  def test_with_default_has_one_with_custom_key
171
169
  @post_serializer_class.class_eval do
172
- has_one :comment, :key => :custom_comment
170
+ has_one :comment, :key => :custom_comment_id
173
171
  end
174
172
 
175
173
  include! :comment
176
174
 
177
175
  assert_equal({
178
- :custom_comment => 1
176
+ :custom_comment_id => 1
179
177
  }, @hash)
180
178
 
181
179
  assert_equal({
182
- :custom_comments => [
183
- { :id => 1, :body => "ZOMG A COMMENT" }
180
+ :comments => [
181
+ { :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" }
182
+ ]
183
+ }, @root_hash)
184
+ end
185
+
186
+ def test_with_default_has_many_with_custom_embed_key
187
+ @post_serializer_class.class_eval do
188
+ has_many :comments, :embed_key => :external_id
189
+ end
190
+
191
+ include! :comments
192
+
193
+ assert_equal({
194
+ :comment_ids => [ "COMM001" ]
195
+ }, @hash)
196
+
197
+ assert_equal({
198
+ :comments => [
199
+ { :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" }
200
+ ]
201
+ }, @root_hash)
202
+ end
203
+
204
+ def test_with_default_has_one_with_custom_embed_key
205
+ @post_serializer_class.class_eval do
206
+ has_one :comment, :embed_key => :external_id
207
+ end
208
+
209
+ include! :comment
210
+
211
+ assert_equal({
212
+ :comment_id => "COMM001"
213
+ }, @hash)
214
+
215
+ assert_equal({
216
+ :comments => [
217
+ { :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" }
218
+ ]
219
+ }, @root_hash)
220
+ end
221
+
222
+ def test_with_default_has_many_with_custom_key_and_custom_embed_key
223
+ @post_serializer_class.class_eval do
224
+ has_many :comments, :key => :custom_comments, :embed_key => :external_id
225
+ end
226
+
227
+ include! :comments
228
+
229
+ assert_equal({
230
+ :custom_comments => [ "COMM001" ]
231
+ }, @hash)
232
+
233
+ assert_equal({
234
+ :comments => [
235
+ { :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" }
236
+ ]
237
+ }, @root_hash)
238
+ end
239
+
240
+ def test_with_default_has_one_with_custom_key_and_custom_embed_key
241
+ @post_serializer_class.class_eval do
242
+ has_one :comment, :key => :custom_comment, :embed_key => :external_id
243
+ end
244
+
245
+ include! :comment
246
+
247
+ assert_equal({
248
+ :custom_comment => "COMM001"
249
+ }, @hash)
250
+
251
+ assert_equal({
252
+ :comments => [
253
+ { :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" }
184
254
  ]
185
255
  }, @root_hash)
186
256
  end
@@ -194,7 +264,7 @@ class AssociationTest < ActiveModel::TestCase
194
264
 
195
265
  assert_equal({
196
266
  :comments => [
197
- { :id => 1, :body => "ZOMG A COMMENT" }
267
+ { :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" }
198
268
  ]
199
269
  }, @hash)
200
270
 
@@ -209,7 +279,7 @@ class AssociationTest < ActiveModel::TestCase
209
279
  include_bare! :comments
210
280
 
211
281
  assert_equal({
212
- :comments => [ 1 ]
282
+ :comment_ids => [ 1 ]
213
283
  }, @hash)
214
284
 
215
285
  assert_equal({}, @root_hash)
@@ -234,12 +304,12 @@ class AssociationTest < ActiveModel::TestCase
234
304
  include_bare! :comments
235
305
 
236
306
  assert_equal({
237
- :comments => [ 1 ]
307
+ :comment_ids => [ 1 ]
238
308
  }, @hash)
239
309
 
240
310
  assert_equal({
241
311
  :comments => [
242
- { :id => 1, :body => "ZOMG A COMMENT" }
312
+ { :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" }
243
313
  ]
244
314
  }, @root_hash)
245
315
  end
@@ -252,7 +322,7 @@ class AssociationTest < ActiveModel::TestCase
252
322
  include_bare! :comment
253
323
 
254
324
  assert_equal({
255
- :comment => 1
325
+ :comment_id => 1
256
326
  }, @hash)
257
327
 
258
328
  assert_equal({}, @root_hash)
@@ -277,15 +347,38 @@ class AssociationTest < ActiveModel::TestCase
277
347
  include_bare! :comment
278
348
 
279
349
  assert_equal({
280
- :comment => 1
350
+ :comment_id => 1
281
351
  }, @hash)
282
352
 
283
353
  assert_equal({
284
354
  :comments => [
285
- { :id => 1, :body => "ZOMG A COMMENT" }
355
+ { :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" }
286
356
  ]
287
357
  }, @root_hash)
288
358
  end
359
+
360
+ def test_embed_ids_include_true_does_not_serialize_multiple_times
361
+ @post.recent_comment = @comment
362
+
363
+ @post_serializer_class.class_eval do
364
+ has_one :comment, :embed => :ids, :include => true
365
+ has_one :recent_comment, :embed => :ids, :include => true, :root => :comments
366
+ end
367
+
368
+ # Count how often the @comment record is serialized.
369
+ serialized_times = 0
370
+ @comment.class_eval do
371
+ define_method :read_attribute_for_serialization, lambda { |name|
372
+ serialized_times += 1 if name == :body
373
+ super(name)
374
+ }
375
+ end
376
+
377
+ include_bare! :comment
378
+ include_bare! :recent_comment
379
+
380
+ assert_equal 1, serialized_times
381
+ end
289
382
  end
290
383
 
291
384
  class InclusionTest < AssociationTest
@@ -312,10 +405,10 @@ class AssociationTest < ActiveModel::TestCase
312
405
  :post => {
313
406
  :title => "New Post",
314
407
  :body => "Body",
315
- :comments => [ 1 ]
408
+ :comment_ids => [ 1 ]
316
409
  },
317
410
  :comments => [
318
- { :id => 1, :body => "ZOMG A COMMENT" }
411
+ { :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" }
319
412
  ]
320
413
  }, json)
321
414
  end
@@ -331,7 +424,7 @@ class AssociationTest < ActiveModel::TestCase
331
424
  :post => {
332
425
  :title => "New Post",
333
426
  :body => "Body",
334
- :comments => [ 1 ]
427
+ :comment_ids => [ 1 ]
335
428
  }
336
429
  }, json)
337
430
  end
@@ -347,7 +440,7 @@ class AssociationTest < ActiveModel::TestCase
347
440
  :post => {
348
441
  :title => "New Post",
349
442
  :body => "Body",
350
- :comments => [ 1 ]
443
+ :comment_ids => [ 1 ]
351
444
  }
352
445
  }, json)
353
446
  end
@@ -363,12 +456,34 @@ class AssociationTest < ActiveModel::TestCase
363
456
  :post => {
364
457
  :title => "New Post",
365
458
  :body => "Body",
366
- :comments => [ 1 ]
459
+ :comment_ids => [ 1 ]
367
460
  },
368
461
  :comments => [
369
- { :id => 1, :body => "ZOMG A COMMENT" }
462
+ { :id => 1, :external_id => "COMM001", :body => "ZOMG A COMMENT" }
370
463
  ]
371
464
  }, json)
372
465
  end
373
466
  end
467
+
468
+ class StringSerializerOption < AssociationTest
469
+ class StringSerializer < ActiveModel::Serializer
470
+ attributes :id, :body
471
+ end
472
+
473
+ def test_specifying_serializer_class_as_string
474
+ @post_serializer_class.class_eval do
475
+ has_many :comments, :embed => :objects
476
+ end
477
+
478
+ include_bare! :comments, :serializer => "AssociationTest::StringSerializerOption::StringSerializer"
479
+
480
+ assert_equal({
481
+ :comments => [
482
+ { :id => 1, :body => "ZOMG A COMMENT" }
483
+ ]
484
+ }, @hash)
485
+
486
+ assert_equal({}, @root_hash)
487
+ end
488
+ end
374
489
  end