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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +109 -17
- data/lib/action_controller/serialization.rb +27 -8
- data/lib/action_controller/serialization_test_case.rb +4 -4
- data/lib/active_model/array_serializer.rb +12 -5
- data/lib/active_model/serializable.rb +24 -2
- data/lib/active_model/serializable/utils.rb +16 -0
- data/lib/active_model/serializer.rb +70 -36
- data/lib/active_model/serializer/{associations.rb → association.rb} +8 -52
- data/lib/active_model/serializer/association/has_many.rb +39 -0
- data/lib/active_model/serializer/association/has_one.rb +25 -0
- data/lib/active_model/serializer/railtie.rb +8 -0
- data/lib/active_model/serializer/version.rb +1 -1
- data/lib/active_model_serializers.rb +1 -1
- data/test/fixtures/poro.rb +106 -1
- data/test/fixtures/template.html.erb +1 -0
- data/test/integration/action_controller/namespaced_serialization_test.rb +96 -0
- data/test/integration/action_controller/serialization_test_case_test.rb +10 -0
- data/test/integration/active_record/active_record_test.rb +2 -2
- data/test/serializers/tmp/app/serializers/account_serializer.rb +3 -0
- data/test/test_app.rb +3 -0
- data/test/unit/active_model/array_serializer/serialization_test.rb +1 -1
- data/test/unit/active_model/serializer/associations/build_serializer_test.rb +15 -0
- data/test/unit/active_model/serializer/attributes_test.rb +16 -0
- data/test/unit/active_model/serializer/config_test.rb +3 -0
- data/test/unit/active_model/serializer/has_many_polymorphic_test.rb +189 -0
- data/test/unit/active_model/serializer/has_many_test.rb +51 -16
- data/test/unit/active_model/serializer/has_one_and_has_many_test.rb +27 -0
- data/test/unit/active_model/serializer/has_one_polymorphic_test.rb +196 -0
- data/test/unit/active_model/serializer/has_one_test.rb +32 -0
- data/test/unit/active_model/serializer/options_test.rb +19 -0
- data/test/unit/active_model/serializer/url_helpers_test.rb +35 -0
- metadata +38 -23
- 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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 78c340a2e3f13f2c481239a485597b288d1bad45
|
4
|
+
data.tar.gz: 41820649a307a90c8bcae4739e3dc32a77a45711
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ef0646d4c09ba21a4a8182c9fa01931ba619faeeefbf0dfb1492fa5f8769b6ce5c6ac2962d13b09224943191cf70e79ab8e360da5923fadc1f5eaf8213c24ea1
|
7
|
+
data.tar.gz: ab87b438037b55acf490d9ac6327abc01fed66cb06638bd12e235e0d3e3f8471a09144315762b866bba13958b60fa38dd02f9e9173d1dfff069ad310e02cec7b
|
data/CHANGELOG.md
CHANGED
@@ -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
|
-
|
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,
|
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
|
520
|
-
`
|
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:
|
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,
|
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
|
-
|
49
|
-
|
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
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
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,
|
87
|
+
if serializer = options.fetch(:serializer, default_serializer(resource))
|
73
88
|
options[:scope] = serialization_scope unless options.has_key?(:scope)
|
74
|
-
|
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 :
|
7
|
-
teardown :
|
6
|
+
setup :setup_serialization_subscriptions
|
7
|
+
teardown :teardown_serialization_subscriptions
|
8
8
|
end
|
9
9
|
|
10
|
-
def
|
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
|
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).
|
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]
|
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/
|
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
|
-
|
59
|
-
|
60
|
-
if
|
61
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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,
|
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
|
246
|
+
associated_data.map { |elem| serialize_id(elem, association) }
|
229
247
|
else
|
230
|
-
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
|