active_model_serializers 0.6.0 → 0.7.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.
- checksums.yaml +7 -0
- data/.travis.yml +13 -13
- data/{gemfiles/Gemfile.edge-rails → Gemfile.edge} +1 -1
- data/README.md +141 -16
- data/RELEASE_NOTES.md +1 -1
- data/lib/action_controller/serialization.rb +8 -1
- data/lib/active_model/array_serializer.rb +21 -13
- data/lib/active_model/serializer.rb +60 -199
- data/lib/active_model/serializer/associations.rb +226 -0
- data/lib/active_model/serializers/version.rb +1 -1
- data/lib/active_model_serializers.rb +3 -5
- data/test/array_serializer_test.rb +55 -0
- data/test/association_test.rb +151 -36
- data/test/generators_test.rb +4 -0
- data/test/serialization_test.rb +80 -3
- data/test/serializer_support_test.rb +11 -0
- data/test/serializer_test.rb +153 -231
- data/test/test_fakes.rb +182 -0
- metadata +41 -28
- data/lib/active_model/ordered_set.rb +0 -25
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e52d190b9a08bf39665a61da10e5c1103b553016
|
4
|
+
data.tar.gz: 5ca66e11bde1221cee716a40b48b71f3f3600a09
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 95648244da95ed7a5d69aa91079a89098864d1fe7d4b7d827caeb3b45e019f4e07ddf563c34ee154eb43e9501ac485ba624e6abef6c131cb8fbf79a9bfefc4b8
|
7
|
+
data.tar.gz: 727fb1fa750281605e22c8830212304eb83ac209c2ab2743e7f8695d763eee5635c97d2db6994678b77620385c50899583fd552eb94ffe821c29d1ba10c5d9cb
|
data/.travis.yml
CHANGED
@@ -5,25 +5,25 @@ rvm:
|
|
5
5
|
- 1.8.7
|
6
6
|
- 1.9.2
|
7
7
|
- 1.9.3
|
8
|
+
- ruby-head
|
8
9
|
- ree
|
9
|
-
- jruby
|
10
|
-
-
|
10
|
+
- jruby-18mode
|
11
|
+
- jruby-19mode
|
12
|
+
- rbx-18mode
|
13
|
+
- rbx-19mode
|
11
14
|
gemfile:
|
12
15
|
- Gemfile
|
13
|
-
-
|
16
|
+
- Gemfile.edge
|
14
17
|
matrix:
|
15
18
|
exclude:
|
16
19
|
# Edge Rails is only compatible with 1.9.3
|
17
|
-
- gemfile:
|
20
|
+
- gemfile: Gemfile.edge
|
18
21
|
rvm: 1.8.7
|
19
|
-
- gemfile:
|
22
|
+
- gemfile: Gemfile.edge
|
20
23
|
rvm: 1.9.2
|
21
|
-
- gemfile:
|
24
|
+
- gemfile: Gemfile.edge
|
22
25
|
rvm: ree
|
23
|
-
- gemfile:
|
24
|
-
rvm: jruby
|
25
|
-
- gemfile:
|
26
|
-
rvm: rbx
|
27
|
-
allow_failures:
|
28
|
-
- gemfile: gemfiles/Gemfile.edge-rails
|
29
|
-
rvm: 1.9.3
|
26
|
+
- gemfile: Gemfile.edge
|
27
|
+
rvm: jruby-18mode
|
28
|
+
- gemfile: Gemfile.edge
|
29
|
+
rvm: rbx-18mode
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
[](http://travis-ci.org/rails-api/active_model_serializers)
|
2
2
|
|
3
3
|
# Purpose
|
4
4
|
|
@@ -15,11 +15,11 @@ development.**
|
|
15
15
|
|
16
16
|
# Installing Serializers
|
17
17
|
|
18
|
-
For now, the easiest way to install `ActiveModel::Serializers` is to add
|
18
|
+
For now, the easiest way to install `ActiveModel::Serializers` is to add it
|
19
19
|
to your `Gemfile`:
|
20
20
|
|
21
21
|
```ruby
|
22
|
-
gem "active_model_serializers",
|
22
|
+
gem "active_model_serializers", "~> 0.6.0"
|
23
23
|
```
|
24
24
|
|
25
25
|
Then, install it on the command line:
|
@@ -34,7 +34,7 @@ The easiest way to create a new serializer is to generate a new resource, which
|
|
34
34
|
will generate a serializer at the same time:
|
35
35
|
|
36
36
|
```
|
37
|
-
$ rails g resource post title:string body:string
|
37
|
+
$ rails g resource post title:string body:string
|
38
38
|
```
|
39
39
|
|
40
40
|
This will generate a serializer in `app/serializers/post_serializer.rb` for
|
@@ -45,6 +45,14 @@ the serializer generator:
|
|
45
45
|
$ rails g serializer post
|
46
46
|
```
|
47
47
|
|
48
|
+
### Support for PORO's and other ORM's.
|
49
|
+
|
50
|
+
Currently `ActiveModel::Serializers` adds serialization support to all models
|
51
|
+
that descend from `ActiveRecord`. If you are using another ORM or if you are
|
52
|
+
using objects that are `ActiveModel` compliant, but do not descend from
|
53
|
+
`ActiveRecord`. You must add an include statement for
|
54
|
+
`ActiveModel::SerializerSupport`.
|
55
|
+
|
48
56
|
# ActiveModel::Serializer
|
49
57
|
|
50
58
|
All new serializers descend from ActiveModel::Serializer
|
@@ -64,7 +72,7 @@ end
|
|
64
72
|
```
|
65
73
|
|
66
74
|
In this case, Rails will look for a serializer named `PostSerializer`, and if
|
67
|
-
it exists, use it to serialize the `Post`.
|
75
|
+
it exists, use it to serialize the `Post`.
|
68
76
|
|
69
77
|
This also works with `respond_with`, which uses `to_json` under the hood. Also
|
70
78
|
note that any options passed to `render :json` will be passed to your
|
@@ -132,7 +140,9 @@ more concise json. To disable the root element for arrays, you have 3 options:
|
|
132
140
|
#### 1. Disable root globally for in `ArraySerializer`. In an initializer:
|
133
141
|
|
134
142
|
```ruby
|
135
|
-
|
143
|
+
ActiveSupport.on_load(:active_model_serializers) do
|
144
|
+
ActiveModel::ArraySerializer.root = false
|
145
|
+
end
|
136
146
|
```
|
137
147
|
|
138
148
|
#### 2. Disable root per render call in your controller:
|
@@ -167,6 +177,19 @@ To specify a custom serializer for the items within an array:
|
|
167
177
|
```ruby
|
168
178
|
render :json => @posts, :each_serializer => FancyPostSerializer
|
169
179
|
```
|
180
|
+
#### 4. Define default_serializer_options in your controller
|
181
|
+
|
182
|
+
If you define `default_serializer_options` method in your controller,
|
183
|
+
all serializers in actions of this controller and it's children will use them.
|
184
|
+
One of options may be `root: false`
|
185
|
+
|
186
|
+
```ruby
|
187
|
+
def default_serializer_options
|
188
|
+
{
|
189
|
+
root: false
|
190
|
+
}
|
191
|
+
end
|
192
|
+
```
|
170
193
|
|
171
194
|
## Getting the old version
|
172
195
|
|
@@ -207,8 +230,7 @@ end
|
|
207
230
|
```
|
208
231
|
|
209
232
|
Within a serializer's methods, you can access the object being
|
210
|
-
serialized as
|
211
|
-
(e.g. `admin_comment` for the `AdminCommentSerializer`).
|
233
|
+
serialized as `object`.
|
212
234
|
|
213
235
|
You can also access the `scope` method, which provides an
|
214
236
|
authorization context to your serializer. By default, scope
|
@@ -243,6 +265,43 @@ class PostSerializer < ActiveModel::Serializer
|
|
243
265
|
end
|
244
266
|
```
|
245
267
|
|
268
|
+
If you would like to add meta information to the outputted JSON, use the `:meta`
|
269
|
+
option:
|
270
|
+
|
271
|
+
```ruby
|
272
|
+
render :json => @posts, :serializer => CustomArraySerializer, :meta => {:total => 10}
|
273
|
+
```
|
274
|
+
|
275
|
+
The above usage of `:meta` will produce the following:
|
276
|
+
|
277
|
+
```json
|
278
|
+
{
|
279
|
+
"meta": { "total": 10 },
|
280
|
+
"posts": [
|
281
|
+
{ "title": "Post 1", "body": "Hello!" },
|
282
|
+
{ "title": "Post 2", "body": "Goodbye!" }
|
283
|
+
]
|
284
|
+
}
|
285
|
+
```
|
286
|
+
|
287
|
+
If you would like to change the meta key name you can use the `:meta_key` option:
|
288
|
+
|
289
|
+
```ruby
|
290
|
+
render :json => @posts, :serializer => CustomArraySerializer, :meta => {:total => 10}, :meta_key => 'meta_object'
|
291
|
+
```
|
292
|
+
|
293
|
+
The above usage of `:meta_key` will produce the following:
|
294
|
+
|
295
|
+
```json
|
296
|
+
{
|
297
|
+
"meta_object": { "total": 10 },
|
298
|
+
"posts": [
|
299
|
+
{ "title": "Post 1", "body": "Hello!" },
|
300
|
+
{ "title": "Post 2", "body": "Goodbye!" }
|
301
|
+
]
|
302
|
+
}
|
303
|
+
```
|
304
|
+
|
246
305
|
If you would like direct, low-level control of attribute serialization, you can
|
247
306
|
completely override the `attributes` method to return the hash you need:
|
248
307
|
|
@@ -280,7 +339,7 @@ class PostSerializer < ActiveModel::Serializer
|
|
280
339
|
|
281
340
|
# only let the user see comments he created.
|
282
341
|
def comments
|
283
|
-
|
342
|
+
object.comments.where(:created_by => scope)
|
284
343
|
end
|
285
344
|
end
|
286
345
|
```
|
@@ -307,7 +366,7 @@ class PostSerializer < ActiveModel::Serializer
|
|
307
366
|
has_many :comments
|
308
367
|
|
309
368
|
def include_comments?
|
310
|
-
!
|
369
|
+
!object.comments_disabled?
|
311
370
|
end
|
312
371
|
end
|
313
372
|
```
|
@@ -376,7 +435,7 @@ Now, any associations will be supplied as an Array of IDs:
|
|
376
435
|
"id": 1,
|
377
436
|
"title": "New post",
|
378
437
|
"body": "A body!",
|
379
|
-
"
|
438
|
+
"comment_ids": [ 1, 2, 3 ]
|
380
439
|
}
|
381
440
|
}
|
382
441
|
```
|
@@ -386,7 +445,7 @@ Alternatively, you can choose to embed only the ids or the associated objects pe
|
|
386
445
|
```ruby
|
387
446
|
class PostSerializer < ActiveModel::Serializer
|
388
447
|
attributes :id, :title, :body
|
389
|
-
|
448
|
+
|
390
449
|
has_many :comments, embed: :objects
|
391
450
|
has_many :tags, embed: :ids
|
392
451
|
end
|
@@ -403,7 +462,7 @@ The JSON will look like this:
|
|
403
462
|
"comments": [
|
404
463
|
{ "id": 1, "body": "what a dumb post" }
|
405
464
|
],
|
406
|
-
"
|
465
|
+
"tag_ids": [ 1, 2, 3 ]
|
407
466
|
}
|
408
467
|
}
|
409
468
|
```
|
@@ -434,11 +493,11 @@ this:
|
|
434
493
|
"id": 1,
|
435
494
|
"title": "New post",
|
436
495
|
"body": "A body!",
|
437
|
-
"
|
496
|
+
"comment_ids": [ 1, 2 ]
|
438
497
|
},
|
439
498
|
"comments": [
|
440
|
-
{ "id": 1, "body": "what a dumb post", "
|
441
|
-
{ "id": 2, "body": "i liked it", "
|
499
|
+
{ "id": 1, "body": "what a dumb post", "tag_ids": [ 1, 2 ] },
|
500
|
+
{ "id": 2, "body": "i liked it", "tag_ids": [ 1, 3 ] },
|
442
501
|
],
|
443
502
|
"tags": [
|
444
503
|
{ "id": 1, "name": "short" },
|
@@ -476,6 +535,34 @@ This would generate JSON that would look like this:
|
|
476
535
|
}
|
477
536
|
```
|
478
537
|
|
538
|
+
You can also specify a different attribute to use rather than the ID of the
|
539
|
+
objects:
|
540
|
+
|
541
|
+
```ruby
|
542
|
+
class PostSerializer < ActiveModel::Serializer
|
543
|
+
embed :ids, :include => true
|
544
|
+
|
545
|
+
attributes :id, :title, :body
|
546
|
+
has_many :comments, :embed_key => :external_id
|
547
|
+
end
|
548
|
+
```
|
549
|
+
|
550
|
+
This would generate JSON that would look like this:
|
551
|
+
|
552
|
+
```json
|
553
|
+
{
|
554
|
+
"post": {
|
555
|
+
"id": 1,
|
556
|
+
"title": "New post",
|
557
|
+
"body": "A body!",
|
558
|
+
"comment_ids": [ "COMM001" ]
|
559
|
+
},
|
560
|
+
"comments": [
|
561
|
+
{ "id": 1, "external_id": "COMM001", "body": "what a dumb post" }
|
562
|
+
]
|
563
|
+
}
|
564
|
+
```
|
565
|
+
|
479
566
|
**NOTE**: The `embed :ids` mechanism is primary useful for clients that process
|
480
567
|
data in bulk and load it into a local store. For these clients, the ability to
|
481
568
|
easily see all of the data per type, rather than having to recursively scan the
|
@@ -496,3 +583,41 @@ class ApplicationController < ActionController::Base
|
|
496
583
|
serialization_scope :current_admin
|
497
584
|
end
|
498
585
|
```
|
586
|
+
|
587
|
+
Please note that, until now, `serialization_scope` doesn't accept a second
|
588
|
+
object with options for specifying which actions should or should not take a
|
589
|
+
given scope in consideration.
|
590
|
+
|
591
|
+
To be clear, it's not possible, yet, to do something like this:
|
592
|
+
|
593
|
+
```ruby
|
594
|
+
class SomeController < ApplicationController
|
595
|
+
serialization_scope :current_admin, :except => [:index, :show]
|
596
|
+
end
|
597
|
+
```
|
598
|
+
|
599
|
+
So, in order to have a fine grained control of what each action should take in
|
600
|
+
consideration for its scope, you may use something like this:
|
601
|
+
|
602
|
+
```ruby
|
603
|
+
class CitiesController < ApplicationController
|
604
|
+
serialization_scope nil
|
605
|
+
|
606
|
+
def index
|
607
|
+
@cities = City.all
|
608
|
+
|
609
|
+
render :json => @cities, :each_serializer => CitySerializer
|
610
|
+
end
|
611
|
+
|
612
|
+
def show
|
613
|
+
@city = City.find(params[:id])
|
614
|
+
|
615
|
+
render :json => @city, :scope => current_admin?
|
616
|
+
end
|
617
|
+
end
|
618
|
+
```
|
619
|
+
|
620
|
+
Assuming that the `current_admin?` method needs to make a query in the database
|
621
|
+
for the current user, the advantage of this approach is that, by setting
|
622
|
+
`serialization_scope` to `nil`, the `index` action no longer will need to make
|
623
|
+
that query, only the `show` action will.
|
data/RELEASE_NOTES.md
CHANGED
@@ -40,10 +40,17 @@ module ActionController
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def _render_option_json(json, options)
|
43
|
+
options = default_serializer_options.merge(options) if default_serializer_options
|
44
|
+
|
43
45
|
serializer = options.delete(:serializer) ||
|
44
46
|
(json.respond_to?(:active_model_serializer) && json.active_model_serializer)
|
45
47
|
|
46
48
|
if json.respond_to?(:to_ary)
|
49
|
+
unless serializer <= ActiveModel::ArraySerializer
|
50
|
+
raise ArgumentError.new("#{serializer.name} is not an ArraySerializer. " +
|
51
|
+
"You may want to use the :each_serializer option instead.")
|
52
|
+
end
|
53
|
+
|
47
54
|
if options[:root] != false && serializer.root != false
|
48
55
|
# default root element for arrays is serializer's root or the controller name
|
49
56
|
# the serializer for an Array is ActiveModel::ArraySerializer
|
@@ -54,7 +61,7 @@ module ActionController
|
|
54
61
|
if serializer
|
55
62
|
options[:scope] = serialization_scope unless options.has_key?(:scope)
|
56
63
|
options[:url_options] = url_options
|
57
|
-
json = serializer.new(json, options
|
64
|
+
json = serializer.new(json, options)
|
58
65
|
end
|
59
66
|
super
|
60
67
|
end
|
@@ -1,4 +1,6 @@
|
|
1
1
|
require "active_support/core_ext/class/attribute"
|
2
|
+
require 'active_support/dependencies'
|
3
|
+
require 'active_support/descendants_tracker'
|
2
4
|
|
3
5
|
module ActiveModel
|
4
6
|
# Active Model Array Serializer
|
@@ -11,6 +13,8 @@ module ActiveModel
|
|
11
13
|
# ActiveModel::ArraySerializer.root = false
|
12
14
|
#
|
13
15
|
class ArraySerializer
|
16
|
+
extend ActiveSupport::DescendantsTracker
|
17
|
+
|
14
18
|
attr_reader :object, :options
|
15
19
|
|
16
20
|
class_attribute :root
|
@@ -27,30 +31,34 @@ module ActiveModel
|
|
27
31
|
serializer = item.active_model_serializer
|
28
32
|
end
|
29
33
|
|
30
|
-
|
31
|
-
|
34
|
+
serializable = serializer ? serializer.new(item, @options) : item
|
35
|
+
|
36
|
+
if serializable.respond_to?(:serializable_hash)
|
37
|
+
serializable.serializable_hash
|
32
38
|
else
|
33
|
-
|
39
|
+
serializable.as_json
|
34
40
|
end
|
35
41
|
end
|
36
42
|
end
|
37
43
|
|
44
|
+
def meta_key
|
45
|
+
@options[:meta_key].try(:to_sym) || :meta
|
46
|
+
end
|
47
|
+
|
48
|
+
def include_meta(hash)
|
49
|
+
hash[meta_key] = @options[:meta] if @options.has_key?(:meta)
|
50
|
+
end
|
51
|
+
|
38
52
|
def as_json(*args)
|
39
53
|
@options[:hash] = hash = {}
|
40
54
|
@options[:unique_values] = {}
|
41
55
|
|
42
|
-
array = serializable_array.map do |item|
|
43
|
-
if item.respond_to?(:serializable_hash)
|
44
|
-
item.serializable_hash
|
45
|
-
else
|
46
|
-
item.as_json
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
56
|
if root = @options[:root]
|
51
|
-
hash.merge!(root =>
|
57
|
+
hash.merge!(root => serializable_array)
|
58
|
+
include_meta hash
|
59
|
+
hash
|
52
60
|
else
|
53
|
-
|
61
|
+
serializable_array
|
54
62
|
end
|
55
63
|
end
|
56
64
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require "active_support/core_ext/class/attribute"
|
2
2
|
require "active_support/core_ext/module/anonymous"
|
3
|
-
require
|
3
|
+
require 'active_support/dependencies'
|
4
|
+
require 'active_support/descendants_tracker'
|
4
5
|
|
5
6
|
module ActiveModel
|
6
7
|
# Active Model Serializer
|
@@ -37,6 +38,8 @@ module ActiveModel
|
|
37
38
|
# end
|
38
39
|
#
|
39
40
|
class Serializer
|
41
|
+
extend ActiveSupport::DescendantsTracker
|
42
|
+
|
40
43
|
INCLUDE_METHODS = {}
|
41
44
|
INSTRUMENT = { :serialize => :"serialize.serializer", :associations => :"associations.serializer" }
|
42
45
|
|
@@ -52,178 +55,6 @@ module ActiveModel
|
|
52
55
|
end
|
53
56
|
end
|
54
57
|
|
55
|
-
module Associations #:nodoc:
|
56
|
-
class Config #:nodoc:
|
57
|
-
class_attribute :options
|
58
|
-
|
59
|
-
def self.refine(name, class_options)
|
60
|
-
current_class = self
|
61
|
-
|
62
|
-
Class.new(self) do
|
63
|
-
singleton_class.class_eval do
|
64
|
-
define_method(:to_s) do
|
65
|
-
"(subclass of #{current_class.name})"
|
66
|
-
end
|
67
|
-
|
68
|
-
alias inspect to_s
|
69
|
-
end
|
70
|
-
|
71
|
-
self.options = class_options
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
self.options = {}
|
76
|
-
|
77
|
-
def initialize(name, source, options={})
|
78
|
-
@name = name
|
79
|
-
@source = source
|
80
|
-
@options = options
|
81
|
-
end
|
82
|
-
|
83
|
-
def option(key, default=nil)
|
84
|
-
if @options.key?(key)
|
85
|
-
@options[key]
|
86
|
-
elsif self.class.options.key?(key)
|
87
|
-
self.class.options[key]
|
88
|
-
else
|
89
|
-
default
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
def target_serializer
|
94
|
-
option(:serializer)
|
95
|
-
end
|
96
|
-
|
97
|
-
def source_serializer
|
98
|
-
@source
|
99
|
-
end
|
100
|
-
|
101
|
-
def key
|
102
|
-
option(:key) || @name
|
103
|
-
end
|
104
|
-
|
105
|
-
def root
|
106
|
-
option(:root) || plural_key
|
107
|
-
end
|
108
|
-
|
109
|
-
def name
|
110
|
-
option(:name) || @name
|
111
|
-
end
|
112
|
-
|
113
|
-
def associated_object
|
114
|
-
option(:value) || source_serializer.send(name)
|
115
|
-
end
|
116
|
-
|
117
|
-
def embed_ids?
|
118
|
-
option(:embed, source_serializer._embed) == :ids
|
119
|
-
end
|
120
|
-
|
121
|
-
def embed_objects?
|
122
|
-
option(:embed, source_serializer._embed) == :objects
|
123
|
-
end
|
124
|
-
|
125
|
-
def embed_in_root?
|
126
|
-
option(:include, source_serializer._root_embed)
|
127
|
-
end
|
128
|
-
|
129
|
-
def embeddable?
|
130
|
-
!associated_object.nil?
|
131
|
-
end
|
132
|
-
|
133
|
-
protected
|
134
|
-
|
135
|
-
def find_serializable(object)
|
136
|
-
if target_serializer
|
137
|
-
target_serializer.new(object, source_serializer.options)
|
138
|
-
elsif object.respond_to?(:active_model_serializer) && (ams = object.active_model_serializer)
|
139
|
-
ams.new(object, source_serializer.options)
|
140
|
-
else
|
141
|
-
object
|
142
|
-
end
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
class HasMany < Config #:nodoc:
|
147
|
-
alias plural_key key
|
148
|
-
|
149
|
-
def serialize
|
150
|
-
associated_object.map do |item|
|
151
|
-
find_serializable(item).serializable_hash
|
152
|
-
end
|
153
|
-
end
|
154
|
-
alias serialize_many serialize
|
155
|
-
|
156
|
-
def serialize_ids
|
157
|
-
# Use pluck or select_columns if available
|
158
|
-
# return collection.ids if collection.respond_to?(:ids)
|
159
|
-
|
160
|
-
associated_object.map do |item|
|
161
|
-
item.read_attribute_for_serialization(:id)
|
162
|
-
end
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
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
|
-
|
183
|
-
def plural_key
|
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
|
189
|
-
end
|
190
|
-
|
191
|
-
def serialize
|
192
|
-
object = associated_object
|
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
|
202
|
-
end
|
203
|
-
|
204
|
-
def serialize_many
|
205
|
-
object = associated_object
|
206
|
-
value = object && find_serializable(object).serializable_hash
|
207
|
-
value ? [value] : []
|
208
|
-
end
|
209
|
-
|
210
|
-
def serialize_ids
|
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
|
219
|
-
object.read_attribute_for_serialization(:id)
|
220
|
-
else
|
221
|
-
nil
|
222
|
-
end
|
223
|
-
end
|
224
|
-
end
|
225
|
-
end
|
226
|
-
|
227
58
|
class_attribute :_attributes
|
228
59
|
self._attributes = {}
|
229
60
|
|
@@ -238,10 +69,15 @@ module ActiveModel
|
|
238
69
|
class << self
|
239
70
|
# Define attributes to be used in the serialization.
|
240
71
|
def attributes(*attrs)
|
72
|
+
|
241
73
|
self._attributes = _attributes.dup
|
242
74
|
|
243
75
|
attrs.each do |attr|
|
244
|
-
|
76
|
+
if Hash === attr
|
77
|
+
attr.each {|attr_real, key| attribute attr_real, :key => key }
|
78
|
+
else
|
79
|
+
attribute attr
|
80
|
+
end
|
245
81
|
end
|
246
82
|
end
|
247
83
|
|
@@ -342,16 +178,31 @@ module ActiveModel
|
|
342
178
|
klass = model_class
|
343
179
|
columns = klass.columns_hash
|
344
180
|
|
345
|
-
attrs =
|
346
|
-
|
347
|
-
|
181
|
+
attrs = {}
|
182
|
+
_attributes.each do |name, key|
|
183
|
+
if column = columns[name.to_s]
|
184
|
+
attrs[key] = column.type
|
185
|
+
else
|
186
|
+
# Computed attribute (method on serializer or model). We cannot
|
187
|
+
# infer the type, so we put nil.
|
188
|
+
attrs[key] = nil
|
189
|
+
end
|
348
190
|
end
|
349
191
|
|
350
|
-
associations =
|
192
|
+
associations = {}
|
193
|
+
_associations.each do |attr, association_class|
|
351
194
|
association = association_class.new(attr, self)
|
352
195
|
|
353
|
-
model_association = klass.reflect_on_association(association.name)
|
354
|
-
|
196
|
+
if model_association = klass.reflect_on_association(association.name)
|
197
|
+
# Real association.
|
198
|
+
associations[association.key] = { model_association.macro => model_association.name }
|
199
|
+
else
|
200
|
+
# Computed association. We could infer has_many vs. has_one from
|
201
|
+
# the association class, but that would make it different from
|
202
|
+
# real associations, which read has_one vs. belongs_to from the
|
203
|
+
# model.
|
204
|
+
associations[association.key] = nil
|
205
|
+
end
|
355
206
|
end
|
356
207
|
|
357
208
|
{ :attributes => attrs, :associations => associations }
|
@@ -377,16 +228,7 @@ module ActiveModel
|
|
377
228
|
def root(name)
|
378
229
|
self._root = name
|
379
230
|
end
|
380
|
-
|
381
|
-
def inherited(klass) #:nodoc:
|
382
|
-
return if klass.anonymous?
|
383
|
-
name = klass.name.demodulize.underscore.sub(/_serializer$/, '')
|
384
|
-
|
385
|
-
klass.class_eval do
|
386
|
-
alias_method name.to_sym, :object
|
387
|
-
root name.to_sym unless self._root == false
|
388
|
-
end
|
389
|
-
end
|
231
|
+
alias_method :root=, :root
|
390
232
|
end
|
391
233
|
|
392
234
|
attr_reader :object, :options
|
@@ -395,18 +237,34 @@ module ActiveModel
|
|
395
237
|
@object, @options = object, options
|
396
238
|
end
|
397
239
|
|
240
|
+
def root_name
|
241
|
+
return false if self._root == false
|
242
|
+
|
243
|
+
class_name = self.class.name.demodulize.underscore.sub(/_serializer$/, '').to_sym unless self.class.name.blank?
|
244
|
+
self._root || class_name
|
245
|
+
end
|
246
|
+
|
398
247
|
def url_options
|
399
248
|
@options[:url_options] || {}
|
400
249
|
end
|
401
250
|
|
251
|
+
def meta_key
|
252
|
+
@options[:meta_key].try(:to_sym) || :meta
|
253
|
+
end
|
254
|
+
|
255
|
+
def include_meta(hash)
|
256
|
+
hash[meta_key] = @options[:meta] if @options.has_key?(:meta)
|
257
|
+
end
|
258
|
+
|
402
259
|
# Returns a json representation of the serializable
|
403
260
|
# object including the root.
|
404
261
|
def as_json(options={})
|
405
|
-
if root = options.fetch(:root, @options.fetch(:root,
|
262
|
+
if root = options.fetch(:root, @options.fetch(:root, root_name))
|
406
263
|
@options[:hash] = hash = {}
|
407
264
|
@options[:unique_values] = {}
|
408
265
|
|
409
266
|
hash.merge!(root => serializable_hash)
|
267
|
+
include_meta hash
|
410
268
|
hash
|
411
269
|
else
|
412
270
|
serializable_hash
|
@@ -416,6 +274,7 @@ module ActiveModel
|
|
416
274
|
# Returns a hash representation of the serializable
|
417
275
|
# object without the root.
|
418
276
|
def serializable_hash
|
277
|
+
return nil if @object.nil?
|
419
278
|
instrument(:serialize, :serializer => self.class.name) do
|
420
279
|
@node = attributes
|
421
280
|
instrument :associations do
|
@@ -482,7 +341,7 @@ module ActiveModel
|
|
482
341
|
if association.embed_in_root? && hash.nil?
|
483
342
|
raise IncludeError.new(self.class, association.name)
|
484
343
|
elsif association.embed_in_root? && association.embeddable?
|
485
|
-
merge_association hash, association.root, association.
|
344
|
+
merge_association hash, association.root, association.serializables, unique_values
|
486
345
|
end
|
487
346
|
elsif association.embed_objects?
|
488
347
|
node[association.key] = association.serialize
|
@@ -498,13 +357,15 @@ module ActiveModel
|
|
498
357
|
# a unique list of all of the objects that are already in the Array. This
|
499
358
|
# avoids the need to scan through the Array looking for entries every time
|
500
359
|
# we want to merge a new list of values.
|
501
|
-
def merge_association(hash, key,
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
360
|
+
def merge_association(hash, key, serializables, unique_values)
|
361
|
+
already_serialized = (unique_values[key] ||= {})
|
362
|
+
serializable_hashes = (hash[key] ||= [])
|
363
|
+
|
364
|
+
serializables.each do |serializable|
|
365
|
+
unless already_serialized.include? serializable.object
|
366
|
+
already_serialized[serializable.object] = true
|
367
|
+
serializable_hashes << serializable.serializable_hash
|
368
|
+
end
|
508
369
|
end
|
509
370
|
end
|
510
371
|
|