active_model_serializers 0.9.2 → 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/README.md +109 -17
  4. data/lib/action_controller/serialization.rb +27 -8
  5. data/lib/action_controller/serialization_test_case.rb +4 -4
  6. data/lib/active_model/array_serializer.rb +12 -5
  7. data/lib/active_model/serializable.rb +24 -2
  8. data/lib/active_model/serializable/utils.rb +16 -0
  9. data/lib/active_model/serializer.rb +70 -36
  10. data/lib/active_model/serializer/{associations.rb → association.rb} +8 -52
  11. data/lib/active_model/serializer/association/has_many.rb +39 -0
  12. data/lib/active_model/serializer/association/has_one.rb +25 -0
  13. data/lib/active_model/serializer/railtie.rb +8 -0
  14. data/lib/active_model/serializer/version.rb +1 -1
  15. data/lib/active_model_serializers.rb +1 -1
  16. data/test/fixtures/poro.rb +106 -1
  17. data/test/fixtures/template.html.erb +1 -0
  18. data/test/integration/action_controller/namespaced_serialization_test.rb +96 -0
  19. data/test/integration/action_controller/serialization_test_case_test.rb +10 -0
  20. data/test/integration/active_record/active_record_test.rb +2 -2
  21. data/test/serializers/tmp/app/serializers/account_serializer.rb +3 -0
  22. data/test/test_app.rb +3 -0
  23. data/test/unit/active_model/array_serializer/serialization_test.rb +1 -1
  24. data/test/unit/active_model/serializer/associations/build_serializer_test.rb +15 -0
  25. data/test/unit/active_model/serializer/attributes_test.rb +16 -0
  26. data/test/unit/active_model/serializer/config_test.rb +3 -0
  27. data/test/unit/active_model/serializer/has_many_polymorphic_test.rb +189 -0
  28. data/test/unit/active_model/serializer/has_many_test.rb +51 -16
  29. data/test/unit/active_model/serializer/has_one_and_has_many_test.rb +27 -0
  30. data/test/unit/active_model/serializer/has_one_polymorphic_test.rb +196 -0
  31. data/test/unit/active_model/serializer/has_one_test.rb +32 -0
  32. data/test/unit/active_model/serializer/options_test.rb +19 -0
  33. data/test/unit/active_model/serializer/url_helpers_test.rb +35 -0
  34. metadata +38 -23
  35. data/test/tmp/app/serializers/account_serializer.rb +0 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d9a74f3f7f7116d91ae865c6f1ebff4a803181fc
4
- data.tar.gz: 660cbc5ba7cd25125bdd8e50875b03c726b3c75a
3
+ metadata.gz: 78c340a2e3f13f2c481239a485597b288d1bad45
4
+ data.tar.gz: 41820649a307a90c8bcae4739e3dc32a77a45711
5
5
  SHA512:
6
- metadata.gz: ef6c3b8ae2e948ffc77ffbf16823e6a13cb9d94410423be2a2469cbb75fd936b6b116d51bac5b4ce509603ec2021b3d494c9b675ce4a002ad596c8b55d2251c7
7
- data.tar.gz: a54bd156271f4434d949dbf225775d9e9883a3362acda92227630d9045e639d321f188a3de23a5fad6390855f42929256d76808927e8a30051354ad7840f94b4
6
+ metadata.gz: ef0646d4c09ba21a4a8182c9fa01931ba619faeeefbf0dfb1492fa5f8769b6ce5c6ac2962d13b09224943191cf70e79ab8e360da5923fadc1f5eaf8213c24ea1
7
+ data.tar.gz: ab87b438037b55acf490d9ac6327abc01fed66cb06638bd12e235e0d3e3f8471a09144315762b866bba13958b60fa38dd02f9e9173d1dfff069ad310e02cec7b
@@ -20,6 +20,12 @@
20
20
 
21
21
  * Require rails >= 3.2.
22
22
 
23
+ * Serializers for associations are being looked up in a parent serializer's namespace first. Same with controllers' namespaces.
24
+
25
+ * Added a "prefix" option in case you want to use a different version of serializer.
26
+
27
+ * Serializers default namespace can be set in `default_serializer_options` and inherited by associations.
28
+
23
29
  # VERSION 0.8.1
24
30
 
25
31
  * Fix bug whereby a serializer using 'options' would blow up.
data/README.md CHANGED
@@ -1,12 +1,12 @@
1
- [![Build Status](https://api.travis-ci.org/rails-api/active_model_serializers.png?branch=0-9-stable)](https://travis-ci.org/rails-api/active_model_serializers)
2
- [![Code Climate](https://codeclimate.com/github/rails-api/active_model_serializers.png)](https://codeclimate.com/github/rails-api/active_model_serializers)
1
+ [![Build Status](https://api.travis-ci.org/rails-api/active_model_serializers.png?branch=0-9-stable)](https://travis-ci.org/rails-api/active_model_serializers)
2
+ [![Code Climate](https://codeclimate.com/github/rails-api/active_model_serializers.png)](https://codeclimate.com/github/rails-api/active_model_serializers)
3
3
 
4
4
  # ActiveModel::Serializers
5
5
 
6
6
  ## Purpose
7
7
 
8
- `ActiveModel::Serializers` encapsulates the JSON serialization of objects.
9
- Objects that respond to read\_attribute\_for\_serialization
8
+ `ActiveModel::Serializers` encapsulates the JSON serialization of objects.
9
+ Objects that respond to read\_attribute\_for\_serialization
10
10
  (including `ActiveModel` and `ActiveRecord` objects) are supported.
11
11
 
12
12
  Serializers know about both a model and the `current_user`, so you can
@@ -94,6 +94,17 @@ serializer when you render the object:
94
94
  render json: @post, serializer: FancyPostSerializer
95
95
  ```
96
96
 
97
+ ### Use serialization outside of ActionController::Base
98
+
99
+ When controller does not inherit from ActionController::Base,
100
+ include Serialization module manually:
101
+
102
+ ```ruby
103
+ class ApplicationController < ActionController::API
104
+ include ActionController::Serialization
105
+ end
106
+ ```
107
+
97
108
  ## Arrays
98
109
 
99
110
  In your controllers, when you use `render :json` for an array of objects, AMS will
@@ -229,13 +240,25 @@ ActiveModel::Serializer.setup do |config|
229
240
  config.key_format = :lower_camel
230
241
  end
231
242
 
232
- class BlogLowerCamelSerializer < ActiveModel::Serializer
243
+ class BlogLowerCamelSerializer < ActiveModel::Serializer
233
244
  format_keys :lower_camel
234
245
  end
235
246
 
236
247
  BlogSerializer.new(object, key_format: :lower_camel)
237
248
  ```
238
249
 
250
+ ## Changing the default association key type
251
+
252
+ You can specify that serializers use unsuffixed names as association keys by default.
253
+
254
+ `````ruby
255
+ ActiveModel::Serializer.setup do |config|
256
+ config.default_key_type = :name
257
+ end
258
+ ````
259
+
260
+ This will build association keys like `comments` or `author` instead of `comment_ids` or `author_id`.
261
+
239
262
  ## Getting the old version
240
263
 
241
264
  If you find that your project is already relying on the old rails to_json
@@ -281,7 +304,7 @@ Since this shadows any attribute named `object`, you can include them through `o
281
304
 
282
305
  ```ruby
283
306
  class VersionSerializer < ActiveModel::Serializer
284
- attribute :version_object, key: :object
307
+ attributes :version_object
285
308
 
286
309
  def version_object
287
310
  object.object
@@ -354,7 +377,7 @@ The above usage of `:meta` will produce the following:
354
377
  If you would like to change the meta key name you can use the `:meta_key` option:
355
378
 
356
379
  ```ruby
357
- render json: @posts, serializer: CustomArraySerializer, meta: {total: 10}, meta_key: 'meta_object'
380
+ render json: @posts, serializer: CustomArraySerializer, meta_object: {total: 10}, meta_key: 'meta_object'
358
381
  ```
359
382
 
360
383
  The above usage of `:meta_key` will produce the following:
@@ -467,9 +490,6 @@ You may also use the `:serializer` option to specify a custom serializer class a
467
490
 
468
491
  Serializers are only concerned with multiplicity, and not ownership. `belongs_to` ActiveRecord associations can be included using `has_one` in your serializer.
469
492
 
470
- NOTE: polymorphic was removed because was only supported for has\_one
471
- associations and is in the TODO list of the project.
472
-
473
493
  ## Embedding Associations
474
494
 
475
495
  By default, associations will be embedded inside the serialized object. So if
@@ -516,15 +536,15 @@ Now, any associations will be supplied as an Array of IDs:
516
536
  }
517
537
  ```
518
538
 
519
- You may also choose to embed the IDs by the association's name underneath an
520
- `embed_key` for the resource. For example, say we want to change `comment_ids`
539
+ You may also choose to embed the IDs by the association's name underneath a
540
+ `key` for the resource. For example, say we want to change `comment_ids`
521
541
  to `comments` underneath a `links` key:
522
542
 
523
543
  ```ruby
524
544
  class PostSerializer < ActiveModel::Serializer
525
545
  attributes :id, :title, :body
526
546
 
527
- has_many :comments, embed: ids, embed_namespace: :links
547
+ has_many :comments, embed: :ids, key: :comments, embed_namespace: :links
528
548
  end
529
549
  ```
530
550
 
@@ -615,7 +635,7 @@ the root document (say, `linked`), you can specify an `embed_in_root_key`:
615
635
 
616
636
  ```ruby
617
637
  class PostSerializer < ActiveModel::Serializer
618
- embed: ids, include: true, embed_in_root_key: :linked
638
+ embed :ids, include: true, embed_in_root_key: :linked
619
639
 
620
640
  attributes: :id, :title, :body
621
641
  has_many :comments, :tags
@@ -646,8 +666,8 @@ The above would yield the following JSON document:
646
666
  }
647
667
  ```
648
668
 
649
- When side-loading data, your serializer cannot have the `{ root: false }` option,
650
- as this would lead to invalid JSON. If you do not have a root key, the `include`
669
+ When side-loading data, your serializer cannot have the `{ root: false }` option,
670
+ as this would lead to invalid JSON. If you do not have a root key, the `include`
651
671
  instruction will be ignored
652
672
 
653
673
  You can also specify a different root for the embedded objects than the key
@@ -686,7 +706,7 @@ class PostSerializer < ActiveModel::Serializer
686
706
  embed :ids, include: true
687
707
 
688
708
  attributes :id, :title, :body
689
- has_many :comments, embed_key: :external_id
709
+ has_many :comments, key: :external_id
690
710
  end
691
711
  ```
692
712
 
@@ -714,6 +734,78 @@ data looking for information, is extremely useful.
714
734
  If you are mostly working with the data in simple scenarios and manually making
715
735
  Ajax requests, you probably just want to use the default embedded behavior.
716
736
 
737
+
738
+ ## Embedding Polymorphic Associations
739
+
740
+ Because we need both the id and the type to be able to identify a polymorphic associated model, these are serialized in a slightly different format than common ones.
741
+
742
+ When embedding entire objects:
743
+
744
+ ```ruby
745
+ class PostSerializer < ActiveModel::Serializer
746
+ attributes :id, :title
747
+ has_many :attachments, polymorphic: true
748
+ end
749
+ ```
750
+
751
+ ```json
752
+ {
753
+ "post": {
754
+ "id": 1,
755
+ "title": "New post",
756
+ "attachments": [
757
+ {
758
+ "type": "image"
759
+ "image": {
760
+ "id": 3
761
+ "name": "logo"
762
+ "url": "http://images.com/logo.jpg"
763
+ }
764
+ },
765
+ {
766
+ "type": "video"
767
+ "video": {
768
+ "id": 12
769
+ "uid": "XCSSMDFWW"
770
+ "source": "youtube"
771
+ }
772
+ }
773
+ ]
774
+ }
775
+ }
776
+ ```
777
+
778
+ When embedding ids:
779
+
780
+ ```ruby
781
+ class PostSerializer < ActiveModel::Serializer
782
+ embed :ids
783
+
784
+ attributes :id, :title
785
+ has_many :attachments, polymorphic: true
786
+ end
787
+ ```
788
+
789
+ ```json
790
+ {
791
+ "post": {
792
+ "id": 1,
793
+ "title": "New post",
794
+ "attachment_ids": [
795
+ {
796
+ "type": "image"
797
+ "id": 12
798
+ },
799
+ {
800
+ "type": "video"
801
+ "id": 3
802
+ }
803
+ ]
804
+ }
805
+ }
806
+ ```
807
+
808
+
717
809
  ## Customizing Scope
718
810
 
719
811
  In a serializer, `current_user` is the current authorization scope which the controller
@@ -45,18 +45,32 @@ module ActionController
45
45
  end
46
46
  end
47
47
 
48
- def _render_option_json(resource, options)
49
- serializer = build_json_serializer(resource, options)
48
+ [:_render_option_json, :_render_with_renderer_json].each do |renderer_method|
49
+ define_method renderer_method do |resource, options|
50
+ serializer = build_json_serializer(resource, options)
50
51
 
51
- if serializer
52
- super(serializer, options)
53
- else
54
- super
52
+ if serializer
53
+ super(serializer, options)
54
+ else
55
+ super(resource, options)
56
+ end
55
57
  end
56
58
  end
57
59
 
58
60
  private
59
61
 
62
+ def namespace_for_serializer
63
+ @namespace_for_serializer ||= self.class.parent unless self.class.parent == Object
64
+ end
65
+
66
+ def default_serializer(resource)
67
+ options = {}.tap do |o|
68
+ o[:namespace] = namespace_for_serializer if namespace_for_serializer
69
+ end
70
+
71
+ ActiveModel::Serializer.serializer_for(resource, options)
72
+ end
73
+
60
74
  def default_serializer_options
61
75
  {}
62
76
  end
@@ -68,10 +82,15 @@ module ActionController
68
82
 
69
83
  def build_json_serializer(resource, options = {})
70
84
  options = default_serializer_options.merge(options)
85
+ @namespace_for_serializer = options.fetch(:namespace, nil)
71
86
 
72
- if serializer = options.fetch(:serializer, ActiveModel::Serializer.serializer_for(resource))
87
+ if serializer = options.fetch(:serializer, default_serializer(resource))
73
88
  options[:scope] = serialization_scope unless options.has_key?(:scope)
74
- options[:resource_name] = controller_name if resource.respond_to?(:to_ary)
89
+
90
+ if resource.respond_to?(:to_ary)
91
+ options[:resource_name] = controller_name
92
+ options[:namespace] = namespace_for_serializer if namespace_for_serializer
93
+ end
75
94
 
76
95
  serializer.new(resource, options)
77
96
  end
@@ -3,11 +3,11 @@ module ActionController
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  included do
6
- setup :setup_subscriptions
7
- teardown :teardown_subscriptions
6
+ setup :setup_serialization_subscriptions
7
+ teardown :teardown_serialization_subscriptions
8
8
  end
9
9
 
10
- def setup_subscriptions
10
+ def setup_serialization_subscriptions
11
11
  @serializers = Hash.new(0)
12
12
 
13
13
  ActiveSupport::Notifications.subscribe("!serialize.active_model_serializers") do |name, start, finish, id, payload|
@@ -16,7 +16,7 @@ module ActionController
16
16
  end
17
17
  end
18
18
 
19
- def teardown_subscriptions
19
+ def teardown_serialization_subscriptions
20
20
  ActiveSupport::Notifications.unsubscribe("!serialize.active_model_serializers")
21
21
  end
22
22
 
@@ -15,12 +15,14 @@ module ActiveModel
15
15
  @object = object
16
16
  @scope = options[:scope]
17
17
  @root = options.fetch(:root, self.class._root)
18
+ @polymorphic = options.fetch(:polymorphic, false)
18
19
  @meta_key = options[:meta_key] || :meta
19
20
  @meta = options[@meta_key]
20
21
  @each_serializer = options[:each_serializer]
21
22
  @resource_name = options[:resource_name]
22
23
  @only = options[:only] ? Array(options[:only]) : nil
23
24
  @except = options[:except] ? Array(options[:except]) : nil
25
+ @namespace = options[:namespace]
24
26
  @key_format = options[:key_format] || options[:each_serializer].try(:key_format)
25
27
  end
26
28
  attr_accessor :object, :scope, :root, :meta_key, :meta, :key_format
@@ -32,13 +34,13 @@ module ActiveModel
32
34
  end
33
35
 
34
36
  def serializer_for(item)
35
- serializer_class = @each_serializer || Serializer.serializer_for(item) || DefaultSerializer
36
- serializer_class.new(item, scope: scope, key_format: key_format, only: @only, except: @except)
37
+ serializer_class = @each_serializer || Serializer.serializer_for(item, namespace: @namespace) || DefaultSerializer
38
+ serializer_class.new(item, scope: scope, key_format: key_format, only: @only, except: @except, polymorphic: @polymorphic, namespace: @namespace)
37
39
  end
38
40
 
39
- def serializable_object
41
+ def serializable_object(options={})
40
42
  @object.map do |item|
41
- serializer_for(item).serializable_object
43
+ serializer_for(item).serializable_object_with_notification(options)
42
44
  end
43
45
  end
44
46
  alias_method :serializable_array, :serializable_object
@@ -49,7 +51,11 @@ module ActiveModel
49
51
  next if !objects || objects.flatten.empty?
50
52
 
51
53
  if hash.has_key?(type)
52
- hash[type].concat(objects).uniq!
54
+ case hash[type] when Hash
55
+ hash[type].deep_merge!(objects){ |key, old, new| (Array(old) + Array(new)).uniq }
56
+ else
57
+ hash[type].concat(objects).uniq!
58
+ end
53
59
  else
54
60
  hash[type] = objects
55
61
  end
@@ -58,6 +64,7 @@ module ActiveModel
58
64
  end
59
65
 
60
66
  private
67
+
61
68
  def instrumentation_keys
62
69
  [:object, :scope, :root, :meta_key, :meta, :each_serializer, :resource_name, :key_format]
63
70
  end
@@ -1,17 +1,29 @@
1
+ require 'active_model/serializable/utils'
2
+
1
3
  module ActiveModel
2
4
  module Serializable
5
+ def self.included(base)
6
+ base.extend Utils
7
+ end
8
+
3
9
  def as_json(options={})
4
10
  instrument('!serialize') do
5
11
  if root = options.fetch(:root, json_key)
6
- hash = { root => serializable_object }
12
+ hash = { root => serializable_object(options) }
7
13
  hash.merge!(serializable_data)
8
14
  hash
9
15
  else
10
- serializable_object
16
+ serializable_object(options)
11
17
  end
12
18
  end
13
19
  end
14
20
 
21
+ def serializable_object_with_notification(options={})
22
+ instrument('!serialize') do
23
+ serializable_object(options)
24
+ end
25
+ end
26
+
15
27
  def serializable_data
16
28
  embedded_in_root_associations.tap do |hash|
17
29
  if respond_to?(:meta) && meta
@@ -20,11 +32,21 @@ module ActiveModel
20
32
  end
21
33
  end
22
34
 
35
+ def namespace
36
+ get_namespace && Utils._const_get(get_namespace)
37
+ end
38
+
23
39
  def embedded_in_root_associations
24
40
  {}
25
41
  end
26
42
 
27
43
  private
44
+
45
+ def get_namespace
46
+ modules = self.class.name.split('::')
47
+ modules[0..-2].join('::') if modules.size > 1
48
+ end
49
+
28
50
  def instrument(action, &block)
29
51
  payload = instrumentation_keys.inject({ serializer: self.class.name }) do |payload, key|
30
52
  payload[:payload] = self.instance_variable_get(:"@#{key}")
@@ -0,0 +1,16 @@
1
+ module ActiveModel
2
+ module Serializable
3
+ module Utils
4
+ extend self
5
+
6
+ def _const_get(const)
7
+ begin
8
+ method = RUBY_VERSION >= '2.0' ? :const_get : :qualified_const_get
9
+ Object.send method, const
10
+ rescue NameError
11
+ const.safe_constantize
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -1,6 +1,6 @@
1
1
  require 'active_model/array_serializer'
2
2
  require 'active_model/serializable'
3
- require 'active_model/serializer/associations'
3
+ require 'active_model/serializer/association'
4
4
  require 'active_model/serializer/config'
5
5
 
6
6
  require 'thread'
@@ -55,33 +55,15 @@ end
55
55
  end
56
56
  attr_reader :key_format
57
57
 
58
- if RUBY_VERSION >= '2.0'
59
- def serializer_for(resource)
60
- if resource.respond_to?(:to_ary)
61
- if Object.constants.include?(:ArraySerializer)
62
- ::ArraySerializer
63
- else
64
- ArraySerializer
65
- end
66
- else
67
- begin
68
- Object.const_get "#{resource.class.name}Serializer"
69
- rescue NameError
70
- nil
71
- end
72
- end
73
- end
74
- else
75
- def serializer_for(resource)
76
- if resource.respond_to?(:to_ary)
77
- if Object.constants.include?(:ArraySerializer)
78
- ::ArraySerializer
79
- else
80
- ArraySerializer
81
- end
58
+ def serializer_for(resource, options = {})
59
+ if resource.respond_to?(:to_ary)
60
+ if Object.constants.include?(:ArraySerializer)
61
+ ::ArraySerializer
82
62
  else
83
- "#{resource.class.name}Serializer".safe_constantize
63
+ ArraySerializer
84
64
  end
65
+ else
66
+ _const_get build_serializer_class(resource, options)
85
67
  end
86
68
  end
87
69
 
@@ -90,14 +72,19 @@ end
90
72
  alias root= _root=
91
73
 
92
74
  def root_name
93
- name.demodulize.underscore.sub(/_serializer$/, '') if name
75
+ if name
76
+ root_name = name.demodulize.underscore.sub(/_serializer$/, '')
77
+ CONFIG.plural_default_root ? root_name.pluralize : root_name
78
+ end
94
79
  end
95
80
 
96
81
  def attributes(*attrs)
97
- @_attributes.concat attrs
98
-
99
82
  attrs.each do |attr|
100
- define_method attr do
83
+ striped_attr = strip_attribute attr
84
+
85
+ @_attributes << striped_attr
86
+
87
+ define_method striped_attr do
101
88
  object.read_attribute_for_serialization attr
102
89
  end unless method_defined?(attr)
103
90
  end
@@ -113,6 +100,22 @@ end
113
100
 
114
101
  private
115
102
 
103
+ def strip_attribute(attr)
104
+ symbolized = attr.is_a?(Symbol)
105
+
106
+ attr = attr.to_s.gsub(/\?\Z/, '')
107
+ attr = attr.to_sym if symbolized
108
+ attr
109
+ end
110
+
111
+ def build_serializer_class(resource, options)
112
+ "".tap do |klass_name|
113
+ klass_name << "#{options[:namespace]}::" if options[:namespace]
114
+ klass_name << options[:prefix].to_s.classify if options[:prefix]
115
+ klass_name << "#{resource.class.name}Serializer"
116
+ end
117
+ end
118
+
116
119
  def associate(klass, *attrs)
117
120
  options = attrs.extract_options!
118
121
 
@@ -130,6 +133,7 @@ end
130
133
  @object = object
131
134
  @scope = options[:scope]
132
135
  @root = options.fetch(:root, self.class._root)
136
+ @polymorphic = options.fetch(:polymorphic, false)
133
137
  @meta_key = options[:meta_key] || :meta
134
138
  @meta = options[@meta_key]
135
139
  @wrap_in_array = options[:_wrap_in_array]
@@ -137,8 +141,9 @@ end
137
141
  @except = options[:except] ? Array(options[:except]) : nil
138
142
  @key_format = options[:key_format]
139
143
  @context = options[:context]
144
+ @namespace = options[:namespace]
140
145
  end
141
- attr_accessor :object, :scope, :root, :meta_key, :meta, :key_format, :context
146
+ attr_accessor :object, :scope, :root, :meta_key, :meta, :key_format, :context, :polymorphic
142
147
 
143
148
  def json_key
144
149
  key = if root == true || root.nil?
@@ -194,12 +199,15 @@ end
194
199
  included_associations = filter(associations.keys)
195
200
  associations.each_with_object({}) do |(name, association), hash|
196
201
  if included_associations.include? name
202
+ association_serializer = build_serializer(association)
203
+ # we must do this always because even if the current association is not
204
+ # embeded in root, it might have its own associations that are embeded in root
205
+ hash.merge!(association_serializer.embedded_in_root_associations) {|key, oldval, newval| [newval, oldval].flatten }
206
+
197
207
  if association.embed_in_root?
198
208
  if association.embed_in_root_key?
199
209
  hash = hash[association.embed_in_root_key] ||= {}
200
210
  end
201
- association_serializer = build_serializer(association)
202
- hash.merge!(association_serializer.embedded_in_root_associations) {|key, oldval, newval| [newval, oldval].flatten }
203
211
 
204
212
  serialized_data = association_serializer.serializable_object
205
213
  key = association.root_key
@@ -215,7 +223,17 @@ end
215
223
 
216
224
  def build_serializer(association)
217
225
  object = send(association.name)
218
- association.build_serializer(object, scope: scope)
226
+ association.build_serializer(object, association_options_for_serializer(association))
227
+ end
228
+
229
+ def association_options_for_serializer(association)
230
+ prefix = association.options[:prefix]
231
+ namespace = association.options[:namespace] || @namespace || self.namespace
232
+
233
+ { scope: scope }.tap do |opts|
234
+ opts[:namespace] = namespace if namespace
235
+ opts[:prefix] = prefix if prefix
236
+ end
219
237
  end
220
238
 
221
239
  def serialize(association)
@@ -225,9 +243,9 @@ end
225
243
  def serialize_ids(association)
226
244
  associated_data = send(association.name)
227
245
  if associated_data.respond_to?(:to_ary)
228
- associated_data.map { |elem| elem.read_attribute_for_serialization(association.embed_key) }
246
+ associated_data.map { |elem| serialize_id(elem, association) }
229
247
  else
230
- associated_data.read_attribute_for_serialization(association.embed_key) if associated_data
248
+ serialize_id(associated_data, association) if associated_data
231
249
  end
232
250
  end
233
251
 
@@ -255,14 +273,30 @@ end
255
273
  end]
256
274
  end
257
275
 
276
+ attr_writer :serialization_options
277
+ def serialization_options
278
+ @serialization_options || {}
279
+ end
280
+
258
281
  def serializable_object(options={})
282
+ self.serialization_options = options
259
283
  return @wrap_in_array ? [] : nil if @object.nil?
260
284
  hash = attributes
261
285
  hash.merge! associations
262
286
  hash = convert_keys(hash) if key_format.present?
287
+ hash = { :type => type_name(@object), type_name(@object) => hash } if @polymorphic
263
288
  @wrap_in_array ? [hash] : hash
264
289
  end
265
290
  alias_method :serializable_hash, :serializable_object
291
+
292
+ def serialize_id(elem, association)
293
+ id = elem.read_attribute_for_serialization(association.embed_key)
294
+ association.polymorphic? ? { id: id, type: type_name(elem) } : id
295
+ end
296
+
297
+ def type_name(elem)
298
+ elem.class.to_s.demodulize.underscore.to_sym
299
+ end
266
300
  end
267
301
 
268
302
  end