active_model_serializers 0.6.0 → 0.7.0

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