active_model_serializers 0.5.2 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,25 @@
1
+ module ActiveModel
2
+ class OrderedSet
3
+ def initialize(array)
4
+ @array = array
5
+ @hash = {}
6
+
7
+ array.each do |item|
8
+ @hash[item] = true
9
+ end
10
+ end
11
+
12
+ def merge!(other)
13
+ other.each do |item|
14
+ next if @hash.key?(item)
15
+
16
+ @hash[item] = true
17
+ @array.push item
18
+ end
19
+ end
20
+
21
+ def to_a
22
+ @array
23
+ end
24
+ end
25
+ end
@@ -3,78 +3,11 @@ require "active_support/core_ext/module/anonymous"
3
3
  require "set"
4
4
 
5
5
  module ActiveModel
6
- class OrderedSet
7
- def initialize(array)
8
- @array = array
9
- @hash = {}
10
-
11
- array.each do |item|
12
- @hash[item] = true
13
- end
14
- end
15
-
16
- def merge!(other)
17
- other.each do |item|
18
- next if @hash.key?(item)
19
-
20
- @hash[item] = true
21
- @array.push item
22
- end
23
- end
24
-
25
- def to_a
26
- @array
27
- end
28
- end
29
-
30
- # Active Model Array Serializer
31
- #
32
- # It serializes an array checking if each element that implements
33
- # the +active_model_serializer+ method.
34
- class ArraySerializer
35
- attr_reader :object, :options
36
-
37
- def initialize(object, options={})
38
- @object, @options = object, options
39
- end
40
-
41
- def serializable_array
42
- @object.map do |item|
43
- if item.respond_to?(:active_model_serializer) && (serializer = item.active_model_serializer)
44
- serializer.new(item, @options)
45
- else
46
- item
47
- end
48
- end
49
- end
50
-
51
- def as_json(*args)
52
- @options[:hash] = hash = {}
53
- @options[:unique_values] = {}
54
-
55
- array = serializable_array.map do |item|
56
- if item.is_a?(Hash)
57
- item
58
- elsif item.respond_to?(:serializable_hash)
59
- item.serializable_hash
60
- else
61
- item.as_json
62
- end
63
- end
64
-
65
- if root = @options[:root]
66
- hash.merge!(root => array)
67
- else
68
- array
69
- end
70
- end
71
- end
72
-
73
6
  # Active Model Serializer
74
7
  #
75
8
  # Provides a basic serializer implementation that allows you to easily
76
9
  # control how a given object is going to be serialized. On initialization,
77
- # it expects to object as arguments, a resource and options. For example,
10
+ # it expects two objects as arguments, a resource and options. For example,
78
11
  # one may do in a controller:
79
12
  #
80
13
  # PostSerializer.new(@post, :scope => current_user).to_json
@@ -83,7 +16,7 @@ module ActiveModel
83
16
  # in for authorization purposes.
84
17
  #
85
18
  # We use the scope to check if a given attribute should be serialized or not.
86
- # For example, some attributes maybe only be returned if +current_user+ is the
19
+ # For example, some attributes may only be returned if +current_user+ is the
87
20
  # author of the post:
88
21
  #
89
22
  # class PostSerializer < ActiveModel::Serializer
@@ -99,11 +32,26 @@ module ActiveModel
99
32
  # end
100
33
  #
101
34
  # def author?
102
- # post.author == options[:scope]
35
+ # post.author == scope
103
36
  # end
104
37
  # end
105
38
  #
106
39
  class Serializer
40
+ INCLUDE_METHODS = {}
41
+ INSTRUMENT = { :serialize => :"serialize.serializer", :associations => :"associations.serializer" }
42
+
43
+ class IncludeError < StandardError
44
+ attr_reader :source, :association
45
+
46
+ def initialize(source, association)
47
+ @source, @association = source, association
48
+ end
49
+
50
+ def to_s
51
+ "Cannot serialize #{association} when #{source} does not have a root!"
52
+ end
53
+ end
54
+
107
55
  module Associations #:nodoc:
108
56
  class Config #:nodoc:
109
57
  class_attribute :options
@@ -178,6 +126,10 @@ module ActiveModel
178
126
  option(:include, source_serializer._root_embed)
179
127
  end
180
128
 
129
+ def embeddable?
130
+ !associated_object.nil?
131
+ end
132
+
181
133
  protected
182
134
 
183
135
  def find_serializable(object)
@@ -212,13 +164,41 @@ module ActiveModel
212
164
  end
213
165
 
214
166
  class HasOne < Config #:nodoc:
167
+ def embeddable?
168
+ if polymorphic? && associated_object.nil?
169
+ false
170
+ else
171
+ true
172
+ end
173
+ end
174
+
175
+ def polymorphic?
176
+ option :polymorphic
177
+ end
178
+
179
+ def polymorphic_key
180
+ associated_object.class.to_s.demodulize.underscore.to_sym
181
+ end
182
+
215
183
  def plural_key
216
- key.to_s.pluralize.to_sym
184
+ if polymorphic?
185
+ associated_object.class.to_s.pluralize.demodulize.underscore.to_sym
186
+ else
187
+ key.to_s.pluralize.to_sym
188
+ end
217
189
  end
218
190
 
219
191
  def serialize
220
192
  object = associated_object
221
- object && find_serializable(object).serializable_hash
193
+
194
+ if object && polymorphic?
195
+ {
196
+ :type => polymorphic_key,
197
+ polymorphic_key => find_serializable(object).serializable_hash
198
+ }
199
+ elsif object
200
+ find_serializable(object).serializable_hash
201
+ end
222
202
  end
223
203
 
224
204
  def serialize_many
@@ -228,7 +208,14 @@ module ActiveModel
228
208
  end
229
209
 
230
210
  def serialize_ids
231
- if object = associated_object
211
+ object = associated_object
212
+
213
+ if object && polymorphic?
214
+ {
215
+ :type => polymorphic_key,
216
+ :id => object.read_attribute_for_serialization(:id)
217
+ }
218
+ elsif object
232
219
  object.read_attribute_for_serialization(:id)
233
220
  else
234
221
  nil
@@ -259,11 +246,15 @@ module ActiveModel
259
246
  end
260
247
 
261
248
  def attribute(attr, options={})
262
- self._attributes = _attributes.merge(attr => options[:key] || attr)
249
+ self._attributes = _attributes.merge(attr => options[:key] || attr.to_s.gsub(/\?$/, '').to_sym)
263
250
 
264
251
  unless method_defined?(attr)
265
- class_eval "def #{attr}() object.read_attribute_for_serialization(:#{attr}) end", __FILE__, __LINE__
252
+ define_method attr do
253
+ object.read_attribute_for_serialization(attr.to_sym)
254
+ end
266
255
  end
256
+
257
+ define_include_method attr
267
258
  end
268
259
 
269
260
  def associate(klass, attrs) #:nodoc:
@@ -272,13 +263,29 @@ module ActiveModel
272
263
 
273
264
  attrs.each do |attr|
274
265
  unless method_defined?(attr)
275
- class_eval "def #{attr}() object.#{attr} end", __FILE__, __LINE__
266
+ define_method attr do
267
+ object.send attr
268
+ end
276
269
  end
277
270
 
271
+ define_include_method attr
272
+
278
273
  self._associations[attr] = klass.refine(attr, options)
279
274
  end
280
275
  end
281
276
 
277
+ def define_include_method(name)
278
+ method = "include_#{name}?".to_sym
279
+
280
+ INCLUDE_METHODS[name] = method
281
+
282
+ unless method_defined?(method)
283
+ define_method method do
284
+ true
285
+ end
286
+ end
287
+ end
288
+
282
289
  # Defines an association in the object should be rendered.
283
290
  #
284
291
  # The serializer object should implement the association name
@@ -389,13 +396,12 @@ module ActiveModel
389
396
  end
390
397
 
391
398
  def url_options
392
- @options[:url_options]
399
+ @options[:url_options] || {}
393
400
  end
394
401
 
395
402
  # Returns a json representation of the serializable
396
403
  # object including the root.
397
- def as_json(options=nil)
398
- options ||= {}
404
+ def as_json(options={})
399
405
  if root = options.fetch(:root, @options.fetch(:root, _root))
400
406
  @options[:hash] = hash = {}
401
407
  @options[:unique_values] = {}
@@ -411,34 +417,22 @@ module ActiveModel
411
417
  # object without the root.
412
418
  def serializable_hash
413
419
  instrument(:serialize, :serializer => self.class.name) do
414
- node = attributes
420
+ @node = attributes
415
421
  instrument :associations do
416
- include_associations!(node) if _embed
422
+ include_associations! if _embed
417
423
  end
418
- node
424
+ @node
419
425
  end
420
426
  end
421
427
 
422
- def include_associations!(node)
423
- _associations.each do |attr, klass|
424
- opts = { :node => node }
425
-
426
- if options.include?(:include) || options.include?(:exclude)
427
- opts[:include] = included_association?(attr)
428
- end
429
-
430
- include! attr, opts
428
+ def include_associations!
429
+ _associations.each_key do |name|
430
+ include!(name) if include?(name)
431
431
  end
432
432
  end
433
433
 
434
- def included_association?(name)
435
- if options.key?(:include)
436
- options[:include].include?(name)
437
- elsif options.key?(:exclude)
438
- !options[:exclude].include?(name)
439
- else
440
- true
441
- end
434
+ def include?(name)
435
+ send INCLUDE_METHODS[name]
442
436
  end
443
437
 
444
438
  def include!(name, options={})
@@ -460,9 +454,17 @@ module ActiveModel
460
454
  @options[:unique_values] ||= {}
461
455
  end
462
456
 
463
- node = options[:node]
457
+ node = options[:node] ||= @node
464
458
  value = options[:value]
465
459
 
460
+ if options[:include] == nil
461
+ if @options.key?(:include)
462
+ options[:include] = @options[:include].include?(name)
463
+ elsif @options.include?(:exclude)
464
+ options[:include] = !@options[:exclude].include?(name)
465
+ end
466
+ end
467
+
466
468
  association_class =
467
469
  if klass = _associations[name]
468
470
  klass
@@ -477,7 +479,9 @@ module ActiveModel
477
479
  if association.embed_ids?
478
480
  node[association.key] = association.serialize_ids
479
481
 
480
- if association.embed_in_root?
482
+ if association.embed_in_root? && hash.nil?
483
+ raise IncludeError.new(self.class, association.name)
484
+ elsif association.embed_in_root? && association.embeddable?
481
485
  merge_association hash, association.root, association.serialize_many, unique_values
482
486
  end
483
487
  elsif association.embed_objects?
@@ -510,25 +514,24 @@ module ActiveModel
510
514
  hash = {}
511
515
 
512
516
  _attributes.each do |name,key|
513
- hash[key] = read_attribute_for_serialization(name)
517
+ hash[key] = read_attribute_for_serialization(name) if include?(name)
514
518
  end
515
519
 
516
520
  hash
517
521
  end
518
522
 
523
+ # Returns options[:scope]
524
+ def scope
525
+ @options[:scope]
526
+ end
527
+
519
528
  alias :read_attribute_for_serialization :send
520
529
 
521
530
  # Use ActiveSupport::Notifications to send events to external systems.
522
531
  # The event name is: name.class_name.serializer
523
532
  def instrument(name, payload = {}, &block)
524
- ActiveSupport::Notifications.instrument("#{name}.serializer", payload, &block)
533
+ event_name = INSTRUMENT[name]
534
+ ActiveSupport::Notifications.instrument(event_name, payload, &block)
525
535
  end
526
536
  end
527
537
  end
528
-
529
- class Array
530
- # Array uses ActiveModel::ArraySerializer.
531
- def active_model_serializer
532
- ActiveModel::ArraySerializer
533
- end
534
- end
@@ -1,5 +1,5 @@
1
1
  module ActiveModel
2
2
  class Serializer
3
- VERSION = "0.5.2"
3
+ VERSION = "0.6.0"
4
4
  end
5
5
  end
@@ -2,7 +2,10 @@ 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
+ require "active_model/array_serializer"
5
7
  require "active_model/serializer"
8
+ require "set"
6
9
 
7
10
  if defined?(Rails)
8
11
  module ActiveModel
@@ -11,6 +14,7 @@ if defined?(Rails)
11
14
  app ||= Rails.application # Rails 3.0.x does not yield `app`
12
15
 
13
16
  Rails::Generators.configure!(app.config.generators)
17
+ Rails::Generators.hidden_namespaces.uniq!
14
18
  require "generators/resource_override"
15
19
  end
16
20
 
@@ -56,6 +60,19 @@ ActiveSupport.on_load(:active_record) do
56
60
  include ActiveModel::SerializerSupport
57
61
  end
58
62
 
63
+ module ActiveModel::ArraySerializerSupport
64
+ def active_model_serializer
65
+ ActiveModel::ArraySerializer
66
+ end
67
+ end
68
+
69
+ Array.send(:include, ActiveModel::ArraySerializerSupport)
70
+ Set.send(:include, ActiveModel::ArraySerializerSupport)
71
+
72
+ ActiveSupport.on_load(:active_record) do
73
+ ActiveRecord::Relation.send(:include, ActiveModel::ArraySerializerSupport)
74
+ end
75
+
59
76
  begin
60
77
  require 'action_controller'
61
78
  require 'action_controller/serialization'
@@ -0,0 +1,34 @@
1
+ require "test_helper"
2
+
3
+ class NoSerializationScopeTest < ActionController::TestCase
4
+ class ScopeSerializer
5
+ def initialize(object, options)
6
+ @object, @options = object, options
7
+ end
8
+
9
+ def as_json(*)
10
+ { :scope => @options[:scope].as_json }
11
+ end
12
+ end
13
+
14
+ class ScopeSerializable
15
+ def active_model_serializer
16
+ ScopeSerializer
17
+ end
18
+ end
19
+
20
+ class NoSerializationScopeController < ActionController::Base
21
+ serialization_scope nil
22
+
23
+ def index
24
+ render :json => ScopeSerializable.new
25
+ end
26
+ end
27
+
28
+ tests NoSerializationScopeController
29
+
30
+ def test_disabled_serialization_scope
31
+ get :index
32
+ assert_equal '{"scope":null}', @response.body
33
+ end
34
+ end