active_model_serializers 0.5.2 → 0.6.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,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