active_model_serializers 0.9.0 → 0.9.3
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.
- 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 +44 -27
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
|
-
[](https://travis-ci.org/rails-api/active_model_serializers)
|
2
|
-
[](https://codeclimate.com/github/rails-api/active_model_serializers)
|
1
|
+
[](https://travis-ci.org/rails-api/active_model_serializers)
|
2
|
+
[](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
|