active_model_serializers 0.8.3 → 0.9.4
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 +122 -5
- data/CONTRIBUTING.md +20 -0
- data/DESIGN.textile +4 -4
- data/{MIT-LICENSE.txt → MIT-LICENSE} +0 -0
- data/README.md +396 -95
- data/lib/action_controller/serialization.rb +50 -12
- data/lib/action_controller/serialization_test_case.rb +79 -0
- data/lib/active_model/array_serializer.rb +47 -78
- data/lib/active_model/default_serializer.rb +32 -0
- data/lib/active_model/serializable/utils.rb +16 -0
- data/lib/active_model/serializable.rb +62 -0
- 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/association.rb +58 -0
- data/lib/active_model/serializer/config.rb +31 -0
- data/lib/active_model/serializer/generators/resource_override.rb +13 -0
- data/lib/{generators → active_model/serializer/generators}/serializer/USAGE +0 -0
- data/lib/active_model/serializer/generators/serializer/scaffold_controller_generator.rb +14 -0
- data/lib/active_model/serializer/generators/serializer/serializer_generator.rb +37 -0
- data/lib/active_model/serializer/generators/serializer/templates/controller.rb +93 -0
- data/lib/active_model/serializer/generators/serializer/templates/serializer.rb +8 -0
- data/lib/active_model/serializer/railtie.rb +18 -0
- data/lib/active_model/{serializers → serializer}/version.rb +1 -1
- data/lib/active_model/serializer.rb +214 -423
- data/lib/active_model/serializer_support.rb +5 -0
- data/lib/active_model_serializers/mime_types.rb +14 -0
- data/lib/active_model_serializers.rb +12 -87
- data/test/fixtures/active_record.rb +96 -0
- data/test/fixtures/poro.rb +187 -0
- data/test/fixtures/template.html.erb +1 -0
- data/test/integration/action_controller/namespaced_serialization_test.rb +105 -0
- data/test/integration/action_controller/serialization_test.rb +287 -0
- data/test/integration/action_controller/serialization_test_case_test.rb +71 -0
- data/test/integration/active_record/active_record_test.rb +94 -0
- data/test/integration/generators/resource_generator_test.rb +26 -0
- data/test/integration/generators/scaffold_controller_generator_test.rb +64 -0
- data/test/integration/generators/serializer_generator_test.rb +41 -0
- data/test/test_app.rb +14 -0
- data/test/test_helper.rb +10 -18
- data/test/unit/active_model/array_serializer/except_test.rb +18 -0
- data/test/unit/active_model/array_serializer/key_format_test.rb +18 -0
- data/test/unit/active_model/array_serializer/meta_test.rb +53 -0
- data/test/unit/active_model/array_serializer/only_test.rb +18 -0
- data/test/unit/active_model/array_serializer/options_test.rb +16 -0
- data/test/unit/active_model/array_serializer/root_test.rb +102 -0
- data/test/unit/active_model/array_serializer/scope_test.rb +24 -0
- data/test/unit/active_model/array_serializer/serialization_test.rb +216 -0
- data/test/unit/active_model/default_serializer_test.rb +13 -0
- data/test/unit/active_model/serializer/associations/build_serializer_test.rb +36 -0
- data/test/unit/active_model/serializer/associations_test.rb +19 -0
- data/test/unit/active_model/serializer/attributes_test.rb +57 -0
- data/test/unit/active_model/serializer/config_test.rb +91 -0
- data/test/unit/active_model/serializer/filter_test.rb +69 -0
- data/test/unit/active_model/serializer/has_many_polymorphic_test.rb +189 -0
- data/test/unit/active_model/serializer/has_many_test.rb +265 -0
- 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 +253 -0
- data/test/unit/active_model/serializer/key_format_test.rb +25 -0
- data/test/unit/active_model/serializer/meta_test.rb +39 -0
- data/test/unit/active_model/serializer/options_test.rb +42 -0
- data/test/unit/active_model/serializer/root_test.rb +117 -0
- data/test/unit/active_model/serializer/scope_test.rb +49 -0
- data/test/unit/active_model/serializer/url_helpers_test.rb +35 -0
- metadata +107 -64
- data/.gitignore +0 -18
- data/.travis.yml +0 -28
- data/Gemfile +0 -4
- data/Gemfile.edge +0 -9
- data/Rakefile +0 -18
- data/active_model_serializers.gemspec +0 -24
- data/bench/perf.rb +0 -43
- data/cruft.md +0 -19
- data/lib/active_model/serializer/associations.rb +0 -233
- data/lib/active_record/serializer_override.rb +0 -16
- data/lib/generators/resource_override.rb +0 -13
- data/lib/generators/serializer/serializer_generator.rb +0 -42
- data/lib/generators/serializer/templates/serializer.rb +0 -19
- data/test/array_serializer_test.rb +0 -75
- data/test/association_test.rb +0 -592
- data/test/caching_test.rb +0 -96
- data/test/generators_test.rb +0 -85
- data/test/no_serialization_scope_test.rb +0 -34
- data/test/serialization_scope_name_test.rb +0 -67
- data/test/serialization_test.rb +0 -392
- data/test/serializer_support_test.rb +0 -51
- data/test/serializer_test.rb +0 -1465
- data/test/test_fakes.rb +0 -217
data/README.md
CHANGED
@@ -1,10 +1,13 @@
|
|
1
|
-
[](https://travis-ci.org/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)
|
2
3
|
|
3
|
-
#
|
4
|
+
# ActiveModel::Serializers
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
objects.
|
6
|
+
## Purpose
|
7
|
+
|
8
|
+
`ActiveModel::Serializers` encapsulates the JSON serialization of objects.
|
9
|
+
Objects that respond to read\_attribute\_for\_serialization
|
10
|
+
(including `ActiveModel` and `ActiveRecord` objects) are supported.
|
8
11
|
|
9
12
|
Serializers know about both a model and the `current_user`, so you can
|
10
13
|
customize serialization based upon whether a user is authorized to see the
|
@@ -13,13 +16,13 @@ content.
|
|
13
16
|
In short, **serializers replace hash-driven development with object-oriented
|
14
17
|
development.**
|
15
18
|
|
16
|
-
# Installing
|
19
|
+
# Installing
|
17
20
|
|
18
21
|
The easiest way to install `ActiveModel::Serializers` is to add it to your
|
19
22
|
`Gemfile`:
|
20
23
|
|
21
24
|
```ruby
|
22
|
-
gem "active_model_serializers"
|
25
|
+
gem "active_model_serializers"
|
23
26
|
```
|
24
27
|
|
25
28
|
Then, install it on the command line:
|
@@ -28,6 +31,16 @@ Then, install it on the command line:
|
|
28
31
|
$ bundle install
|
29
32
|
```
|
30
33
|
|
34
|
+
#### Ruby 1.8 is no longer supported!
|
35
|
+
|
36
|
+
If you must use a ruby 1.8 version (MRI 1.8.7, REE, Rubinius 1.8, or JRuby 1.8), you need to use version 0.8.x.
|
37
|
+
Versions after 0.9.0 do not support ruby 1.8. To specify version 0.8, include this in your Gemfile:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
gem "active_model_serializers", "~> 0.8.0"
|
41
|
+
```
|
42
|
+
|
43
|
+
|
31
44
|
# Creating a Serializer
|
32
45
|
|
33
46
|
The easiest way to create a new serializer is to generate a new resource, which
|
@@ -45,21 +58,13 @@ the serializer generator:
|
|
45
58
|
$ rails g serializer post
|
46
59
|
```
|
47
60
|
|
48
|
-
### Support for
|
49
|
-
|
50
|
-
Currently `ActiveModel::Serializers` adds serialization support to all models
|
51
|
-
that descend from `ActiveRecord` or include `Mongoid::Document`. If you are
|
52
|
-
using another ORM, or if you are using objects that are `ActiveModel`
|
53
|
-
compliant but do not descend from `ActiveRecord` or include
|
54
|
-
`Mongoid::Document`, you must add an include statement for
|
55
|
-
`ActiveModel::SerializerSupport` to make models serializable. If you
|
56
|
-
also want to make collections serializable, you should include
|
57
|
-
`ActiveModel::ArraySerializerSupport` into your ORM's
|
58
|
-
relation/criteria class.
|
61
|
+
### Support for POROs
|
59
62
|
|
60
|
-
|
63
|
+
The PORO should include ActiveModel::SerializerSupport. That's all you need to
|
64
|
+
do to have your POROs supported.
|
61
65
|
|
62
|
-
|
66
|
+
For Rails versions before Rails 4 ActiveModel::Serializers expects objects to
|
67
|
+
implement `read_attribute_for_serialization`.
|
63
68
|
|
64
69
|
# render :json
|
65
70
|
|
@@ -70,7 +75,7 @@ for a serializer for the object and use it if available.
|
|
70
75
|
class PostsController < ApplicationController
|
71
76
|
def show
|
72
77
|
@post = Post.find(params[:id])
|
73
|
-
render :
|
78
|
+
render json: @post
|
74
79
|
end
|
75
80
|
end
|
76
81
|
```
|
@@ -82,22 +87,22 @@ This also works with `respond_with`, which uses `to_json` under the hood. Also
|
|
82
87
|
note that any options passed to `render :json` will be passed to your
|
83
88
|
serializer and available as `@options` inside.
|
84
89
|
|
85
|
-
To specify a custom serializer for an object,
|
86
|
-
|
87
|
-
#### 1. Specify the serializer in your model:
|
90
|
+
To specify a custom serializer for an object, you can specify the
|
91
|
+
serializer when you render the object:
|
88
92
|
|
89
93
|
```ruby
|
90
|
-
|
91
|
-
def active_model_serializer
|
92
|
-
FancyPostSerializer
|
93
|
-
end
|
94
|
-
end
|
94
|
+
render json: @post, serializer: FancyPostSerializer
|
95
95
|
```
|
96
96
|
|
97
|
-
|
97
|
+
### Use serialization outside of ActionController::Base
|
98
|
+
|
99
|
+
When controller does not inherit from ActionController::Base,
|
100
|
+
include Serialization module manually:
|
98
101
|
|
99
102
|
```ruby
|
100
|
-
|
103
|
+
class ApplicationController < ActionController::API
|
104
|
+
include ActionController::Serialization
|
105
|
+
end
|
101
106
|
```
|
102
107
|
|
103
108
|
## Arrays
|
@@ -114,7 +119,7 @@ end
|
|
114
119
|
class PostsController < ApplicationController
|
115
120
|
def index
|
116
121
|
@posts = Post.all
|
117
|
-
render :
|
122
|
+
render json: @posts
|
118
123
|
end
|
119
124
|
end
|
120
125
|
```
|
@@ -135,7 +140,7 @@ By default, the root element is the name of the controller. For example, `PostsC
|
|
135
140
|
generates a root element "posts". To change it:
|
136
141
|
|
137
142
|
```ruby
|
138
|
-
render :
|
143
|
+
render json: @posts, root: "some_posts"
|
139
144
|
```
|
140
145
|
|
141
146
|
You may disable the root element for arrays at the top level, which will result in
|
@@ -152,9 +157,32 @@ root element of the array with any of those methods will produce
|
|
152
157
|
To specify a custom serializer for the items within an array:
|
153
158
|
|
154
159
|
```ruby
|
155
|
-
render :
|
160
|
+
render json: @posts, each_serializer: FancyPostSerializer
|
161
|
+
```
|
162
|
+
|
163
|
+
## Render independently
|
164
|
+
|
165
|
+
By default the setting of serializer is in controller as described above which is the
|
166
|
+
recommended way. However, there may be cases you need to render the json object elsewhere
|
167
|
+
say in a helper or a view when controller is only for main object.
|
168
|
+
|
169
|
+
Then you can render the serialized JSON independently.
|
170
|
+
|
171
|
+
```ruby
|
172
|
+
def current_user_as_json_helper
|
173
|
+
CurrentUserSerializer.new(current_user).to_json
|
174
|
+
end
|
156
175
|
```
|
157
176
|
|
177
|
+
You can also render an array of objects using ArraySerializer.
|
178
|
+
|
179
|
+
```ruby
|
180
|
+
def users_array_as_json_helper(users)
|
181
|
+
ActiveModel::ArraySerializer.new(users, each_serializer: UserSerializer).to_json
|
182
|
+
end
|
183
|
+
```
|
184
|
+
|
185
|
+
|
158
186
|
## Disabling the root element
|
159
187
|
|
160
188
|
You have 4 options to disable the root element, each with a slightly different scope:
|
@@ -164,19 +192,17 @@ You have 4 options to disable the root element, each with a slightly different s
|
|
164
192
|
In an initializer:
|
165
193
|
|
166
194
|
```ruby
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
ActiveModel::ArraySerializer.root = false
|
173
|
-
end
|
195
|
+
# Disable for all serializers (except ArraySerializer)
|
196
|
+
ActiveModel::Serializer.root = false
|
197
|
+
|
198
|
+
# Disable for ArraySerializer
|
199
|
+
ActiveModel::ArraySerializer.root = false
|
174
200
|
```
|
175
201
|
|
176
202
|
#### 2. Disable root per render call in your controller
|
177
203
|
|
178
204
|
```ruby
|
179
|
-
render :
|
205
|
+
render json: @posts, root: false
|
180
206
|
```
|
181
207
|
|
182
208
|
#### 3. Subclass the serializer, and specify using it
|
@@ -187,7 +213,7 @@ class CustomArraySerializer < ActiveModel::ArraySerializer
|
|
187
213
|
end
|
188
214
|
|
189
215
|
# controller:
|
190
|
-
render :
|
216
|
+
render json: @posts, serializer: CustomArraySerializer
|
191
217
|
```
|
192
218
|
|
193
219
|
#### 4. Define default_serializer_options in your controller
|
@@ -204,10 +230,39 @@ def default_serializer_options
|
|
204
230
|
end
|
205
231
|
```
|
206
232
|
|
233
|
+
## Changing the Key Format
|
234
|
+
|
235
|
+
You can specify that serializers use the lower-camel key format at the config, class or instance level.
|
236
|
+
|
237
|
+
```ruby
|
238
|
+
|
239
|
+
ActiveModel::Serializer.setup do |config|
|
240
|
+
config.key_format = :lower_camel
|
241
|
+
end
|
242
|
+
|
243
|
+
class BlogLowerCamelSerializer < ActiveModel::Serializer
|
244
|
+
format_keys :lower_camel
|
245
|
+
end
|
246
|
+
|
247
|
+
BlogSerializer.new(object, key_format: :lower_camel)
|
248
|
+
```
|
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
|
+
|
207
262
|
## Getting the old version
|
208
263
|
|
209
264
|
If you find that your project is already relying on the old rails to_json
|
210
|
-
change `render :json` to `render :
|
265
|
+
change `render :json` to `render json: @your_object.to_json`.
|
211
266
|
|
212
267
|
# Attributes and Associations
|
213
268
|
|
@@ -245,49 +300,58 @@ end
|
|
245
300
|
Within a serializer's methods, you can access the object being
|
246
301
|
serialized as `object`.
|
247
302
|
|
248
|
-
|
249
|
-
authorization context to your serializer. By default, the context
|
250
|
-
is the current user of your application, but this
|
251
|
-
[can be customized](#customizing-scope).
|
252
|
-
|
253
|
-
Serializers will check for the presence of a method named
|
254
|
-
`include_[ATTRIBUTE]?` to determine whether a particular attribute should be
|
255
|
-
included in the output. This is typically used to customize output
|
256
|
-
based on `current_user`. For example:
|
303
|
+
Since this shadows any attribute named `object`, you can include them through `object.object`. For example:
|
257
304
|
|
258
305
|
```ruby
|
259
|
-
class
|
260
|
-
attributes :
|
306
|
+
class VersionSerializer < ActiveModel::Serializer
|
307
|
+
attributes :version_object
|
261
308
|
|
262
|
-
def
|
263
|
-
|
309
|
+
def version_object
|
310
|
+
object.object
|
264
311
|
end
|
265
312
|
end
|
266
313
|
```
|
267
314
|
|
268
|
-
|
269
|
-
|
270
|
-
|
315
|
+
You can also access the `scope` method, which provides an
|
316
|
+
authorization context to your serializer. By default, the context
|
317
|
+
is the current user of your application, but this
|
318
|
+
[can be customized](#customizing-scope).
|
319
|
+
|
320
|
+
Serializers provide a method named `filter`, which should return an array
|
321
|
+
used to determine what attributes and associations should be included in the output.
|
322
|
+
This is typically used to customize output based on `current_user`. For example:
|
271
323
|
|
272
324
|
```ruby
|
273
|
-
class
|
274
|
-
attributes :
|
325
|
+
class PostSerializer < ActiveModel::Serializer
|
326
|
+
attributes :id, :title, :body, :author
|
275
327
|
|
276
|
-
def
|
277
|
-
|
328
|
+
def filter(keys)
|
329
|
+
if scope.admin?
|
330
|
+
keys
|
331
|
+
else
|
332
|
+
keys - [:author]
|
333
|
+
end
|
278
334
|
end
|
279
335
|
end
|
280
336
|
```
|
281
337
|
|
338
|
+
And it's also safe to mutate keys argument by doing keys.delete(:author)
|
339
|
+
in case you want to avoid creating two extra arrays. Note that if you do an
|
340
|
+
in-place modification, you still need to return the modified array.
|
341
|
+
|
342
|
+
### Alias Attribute
|
282
343
|
If you would like the key in the outputted JSON to be different from its name
|
283
|
-
in ActiveRecord, you can
|
344
|
+
in ActiveRecord, you can declare the attribute with the different name
|
345
|
+
and redefine that method:
|
284
346
|
|
285
347
|
```ruby
|
286
348
|
class PostSerializer < ActiveModel::Serializer
|
287
|
-
|
349
|
+
# look up subject on the model, but use title in the JSON
|
350
|
+
def title
|
351
|
+
object.subject
|
352
|
+
end
|
288
353
|
|
289
|
-
|
290
|
-
attribute :subject, :key => :title
|
354
|
+
attributes :id, :body, :title
|
291
355
|
has_many :comments
|
292
356
|
end
|
293
357
|
```
|
@@ -296,7 +360,7 @@ If you would like to add meta information to the outputted JSON, use the `:meta`
|
|
296
360
|
option:
|
297
361
|
|
298
362
|
```ruby
|
299
|
-
render :
|
363
|
+
render json: @posts, serializer: CustomArraySerializer, meta: {total: 10}
|
300
364
|
```
|
301
365
|
|
302
366
|
The above usage of `:meta` will produce the following:
|
@@ -314,7 +378,7 @@ The above usage of `:meta` will produce the following:
|
|
314
378
|
If you would like to change the meta key name you can use the `:meta_key` option:
|
315
379
|
|
316
380
|
```ruby
|
317
|
-
render :
|
381
|
+
render json: @posts, serializer: CustomArraySerializer, meta_object: {total: 10}, meta_key: 'meta_object'
|
318
382
|
```
|
319
383
|
|
320
384
|
The above usage of `:meta_key` will produce the following:
|
@@ -329,6 +393,9 @@ The above usage of `:meta_key` will produce the following:
|
|
329
393
|
}
|
330
394
|
```
|
331
395
|
|
396
|
+
When using meta information, your serializer cannot have the `{ root: false }` option, as this would lead to
|
397
|
+
invalid JSON. If you do not have a root key, the meta information will be ignored.
|
398
|
+
|
332
399
|
If you would like direct, low-level control of attribute serialization, you can
|
333
400
|
completely override the `attributes` method to return the hash you need:
|
334
401
|
|
@@ -338,7 +405,7 @@ class PersonSerializer < ActiveModel::Serializer
|
|
338
405
|
|
339
406
|
def attributes
|
340
407
|
hash = super
|
341
|
-
if
|
408
|
+
if scope.admin?
|
342
409
|
hash["ssn"] = object.ssn
|
343
410
|
hash["secret"] = object.mothers_maiden_name
|
344
411
|
end
|
@@ -357,7 +424,7 @@ and use it to serialize the comment.
|
|
357
424
|
By default, serializers simply look up the association on the original object.
|
358
425
|
You can customize this behavior by implementing a method with the name of the
|
359
426
|
association and returning a different Array. Often, you will do this to
|
360
|
-
customize the objects returned based on the current user.
|
427
|
+
customize the objects returned based on the current user (scope).
|
361
428
|
|
362
429
|
```ruby
|
363
430
|
class PostSerializer < ActiveModel::Serializer
|
@@ -366,7 +433,7 @@ class PostSerializer < ActiveModel::Serializer
|
|
366
433
|
|
367
434
|
# only let the user see comments he created.
|
368
435
|
def comments
|
369
|
-
object.comments.where(:
|
436
|
+
object.comments.where(created_by: scope)
|
370
437
|
end
|
371
438
|
end
|
372
439
|
```
|
@@ -379,27 +446,27 @@ class PostSerializer < ActiveModel::Serializer
|
|
379
446
|
attributes :id, :title, :body
|
380
447
|
|
381
448
|
# look up comments, but use +my_comments+ as the key in JSON
|
382
|
-
has_many :comments, :
|
449
|
+
has_many :comments, root: :my_comments
|
383
450
|
end
|
384
451
|
```
|
385
452
|
|
386
|
-
Also, as with attributes, serializers will
|
387
|
-
|
388
|
-
|
453
|
+
Also, as with attributes, serializers will execute a filter method to
|
454
|
+
determine which associations should be included in the output. For
|
455
|
+
example:
|
389
456
|
|
390
457
|
```ruby
|
391
458
|
class PostSerializer < ActiveModel::Serializer
|
392
459
|
attributes :id, :title, :body
|
393
460
|
has_many :comments
|
394
461
|
|
395
|
-
def
|
396
|
-
|
462
|
+
def filter(keys)
|
463
|
+
keys.delete :comments if object.comments_disabled?
|
464
|
+
keys
|
397
465
|
end
|
398
466
|
end
|
399
467
|
```
|
400
468
|
|
401
|
-
|
402
|
-
override `include_associations!` to specify which associations should be included:
|
469
|
+
Or ...
|
403
470
|
|
404
471
|
```ruby
|
405
472
|
class PostSerializer < ActiveModel::Serializer
|
@@ -407,9 +474,10 @@ class PostSerializer < ActiveModel::Serializer
|
|
407
474
|
has_one :author
|
408
475
|
has_many :comments
|
409
476
|
|
410
|
-
def
|
411
|
-
|
412
|
-
|
477
|
+
def filter(keys)
|
478
|
+
keys.delete :author unless scope.admin?
|
479
|
+
keys.delete :comments if object.comments_disabled?
|
480
|
+
keys
|
413
481
|
end
|
414
482
|
end
|
415
483
|
```
|
@@ -417,8 +485,8 @@ end
|
|
417
485
|
You may also use the `:serializer` option to specify a custom serializer class and the `:polymorphic` option to specify an association that is polymorphic (STI), e.g.:
|
418
486
|
|
419
487
|
```ruby
|
420
|
-
has_many :comments, :
|
421
|
-
has_one :reviewer, :
|
488
|
+
has_many :comments, serializer: CommentShortSerializer
|
489
|
+
has_one :reviewer, polymorphic: true
|
422
490
|
```
|
423
491
|
|
424
492
|
Serializers are only concerned with multiplicity, and not ownership. `belongs_to` ActiveRecord associations can be included using `has_one` in your serializer.
|
@@ -469,6 +537,33 @@ Now, any associations will be supplied as an Array of IDs:
|
|
469
537
|
}
|
470
538
|
```
|
471
539
|
|
540
|
+
You may also choose to embed the IDs by the association's name underneath a
|
541
|
+
`key` for the resource. For example, say we want to change `comment_ids`
|
542
|
+
to `comments` underneath a `links` key:
|
543
|
+
|
544
|
+
```ruby
|
545
|
+
class PostSerializer < ActiveModel::Serializer
|
546
|
+
attributes :id, :title, :body
|
547
|
+
|
548
|
+
has_many :comments, embed: :ids, key: :comments, embed_namespace: :links
|
549
|
+
end
|
550
|
+
```
|
551
|
+
|
552
|
+
The JSON will look like this:
|
553
|
+
|
554
|
+
```json
|
555
|
+
{
|
556
|
+
"post": {
|
557
|
+
"id": 1,
|
558
|
+
"title": "New post",
|
559
|
+
"body": "A body!",
|
560
|
+
"links": {
|
561
|
+
"comments": [ 1, 2, 3 ]
|
562
|
+
}
|
563
|
+
}
|
564
|
+
}
|
565
|
+
```
|
566
|
+
|
472
567
|
Alternatively, you can choose to embed only the ids or the associated objects per association:
|
473
568
|
|
474
569
|
```ruby
|
@@ -506,7 +601,7 @@ You can specify that the data be included like this:
|
|
506
601
|
|
507
602
|
```ruby
|
508
603
|
class PostSerializer < ActiveModel::Serializer
|
509
|
-
embed :ids, :
|
604
|
+
embed :ids, include: true
|
510
605
|
|
511
606
|
attributes :id, :title, :body
|
512
607
|
has_many :comments
|
@@ -536,15 +631,55 @@ this:
|
|
536
631
|
}
|
537
632
|
```
|
538
633
|
|
634
|
+
If you would like to namespace association JSON underneath a certain key in
|
635
|
+
the root document (say, `linked`), you can specify an `embed_in_root_key`:
|
636
|
+
|
637
|
+
```ruby
|
638
|
+
class PostSerializer < ActiveModel::Serializer
|
639
|
+
embed :ids, include: true, embed_in_root_key: :linked
|
640
|
+
|
641
|
+
attributes: :id, :title, :body
|
642
|
+
has_many :comments, :tags
|
643
|
+
end
|
644
|
+
```
|
645
|
+
|
646
|
+
The above would yield the following JSON document:
|
647
|
+
|
648
|
+
```json
|
649
|
+
{
|
650
|
+
"post": {
|
651
|
+
"id": 1,
|
652
|
+
"title": "New post",
|
653
|
+
"body": "A body!",
|
654
|
+
"comment_ids": [ 1, 2 ]
|
655
|
+
},
|
656
|
+
"linked": {
|
657
|
+
"comments": [
|
658
|
+
{ "id": 1, "body": "what a dumb post", "tag_ids": [ 1, 2 ] },
|
659
|
+
{ "id": 2, "body": "i liked it", "tag_ids": [ 1, 3 ] },
|
660
|
+
],
|
661
|
+
"tags": [
|
662
|
+
{ "id": 1, "name": "short" },
|
663
|
+
{ "id": 2, "name": "whiny" },
|
664
|
+
{ "id": 3, "name": "happy" }
|
665
|
+
]
|
666
|
+
}
|
667
|
+
}
|
668
|
+
```
|
669
|
+
|
670
|
+
When side-loading data, your serializer cannot have the `{ root: false }` option,
|
671
|
+
as this would lead to invalid JSON. If you do not have a root key, the `include`
|
672
|
+
instruction will be ignored
|
673
|
+
|
539
674
|
You can also specify a different root for the embedded objects than the key
|
540
675
|
used to reference them:
|
541
676
|
|
542
677
|
```ruby
|
543
678
|
class PostSerializer < ActiveModel::Serializer
|
544
|
-
embed :ids, :
|
679
|
+
embed :ids, include: true
|
545
680
|
|
546
681
|
attributes :id, :title, :body
|
547
|
-
has_many :comments, :
|
682
|
+
has_many :comments, key: :comment_ids, root: :comment_objects
|
548
683
|
end
|
549
684
|
```
|
550
685
|
|
@@ -569,10 +704,10 @@ objects:
|
|
569
704
|
|
570
705
|
```ruby
|
571
706
|
class PostSerializer < ActiveModel::Serializer
|
572
|
-
embed :ids, :
|
707
|
+
embed :ids, include: true
|
573
708
|
|
574
709
|
attributes :id, :title, :body
|
575
|
-
has_many :comments, :
|
710
|
+
has_many :comments, key: :external_id
|
576
711
|
end
|
577
712
|
```
|
578
713
|
|
@@ -600,6 +735,78 @@ data looking for information, is extremely useful.
|
|
600
735
|
If you are mostly working with the data in simple scenarios and manually making
|
601
736
|
Ajax requests, you probably just want to use the default embedded behavior.
|
602
737
|
|
738
|
+
|
739
|
+
## Embedding Polymorphic Associations
|
740
|
+
|
741
|
+
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.
|
742
|
+
|
743
|
+
When embedding entire objects:
|
744
|
+
|
745
|
+
```ruby
|
746
|
+
class PostSerializer < ActiveModel::Serializer
|
747
|
+
attributes :id, :title
|
748
|
+
has_many :attachments, polymorphic: true
|
749
|
+
end
|
750
|
+
```
|
751
|
+
|
752
|
+
```json
|
753
|
+
{
|
754
|
+
"post": {
|
755
|
+
"id": 1,
|
756
|
+
"title": "New post",
|
757
|
+
"attachments": [
|
758
|
+
{
|
759
|
+
"type": "image",
|
760
|
+
"image": {
|
761
|
+
"id": 3,
|
762
|
+
"name": "logo",
|
763
|
+
"url": "http://images.com/logo.jpg"
|
764
|
+
}
|
765
|
+
},
|
766
|
+
{
|
767
|
+
"type": "video",
|
768
|
+
"video": {
|
769
|
+
"id": 12,
|
770
|
+
"uid": "XCSSMDFWW",
|
771
|
+
"source": "youtube"
|
772
|
+
}
|
773
|
+
}
|
774
|
+
]
|
775
|
+
}
|
776
|
+
}
|
777
|
+
```
|
778
|
+
|
779
|
+
When embedding ids:
|
780
|
+
|
781
|
+
```ruby
|
782
|
+
class PostSerializer < ActiveModel::Serializer
|
783
|
+
embed :ids
|
784
|
+
|
785
|
+
attributes :id, :title
|
786
|
+
has_many :attachments, polymorphic: true
|
787
|
+
end
|
788
|
+
```
|
789
|
+
|
790
|
+
```json
|
791
|
+
{
|
792
|
+
"post": {
|
793
|
+
"id": 1,
|
794
|
+
"title": "New post",
|
795
|
+
"attachment_ids": [
|
796
|
+
{
|
797
|
+
"type": "image",
|
798
|
+
"id": 12
|
799
|
+
},
|
800
|
+
{
|
801
|
+
"type": "video",
|
802
|
+
"id": 3
|
803
|
+
}
|
804
|
+
]
|
805
|
+
}
|
806
|
+
}
|
807
|
+
```
|
808
|
+
|
809
|
+
|
603
810
|
## Customizing Scope
|
604
811
|
|
605
812
|
In a serializer, `current_user` is the current authorization scope which the controller
|
@@ -613,7 +820,7 @@ class ApplicationController < ActionController::Base
|
|
613
820
|
end
|
614
821
|
```
|
615
822
|
|
616
|
-
The above example will also change the scope
|
823
|
+
The above example will also change the scope from `current_user` to
|
617
824
|
`current_admin`.
|
618
825
|
|
619
826
|
Please note that, until now, `serialization_scope` doesn't accept a second
|
@@ -624,7 +831,7 @@ To be clear, it's not possible, yet, to do something like this:
|
|
624
831
|
|
625
832
|
```ruby
|
626
833
|
class SomeController < ApplicationController
|
627
|
-
serialization_scope :current_admin, :
|
834
|
+
serialization_scope :current_admin, except: [:index, :show]
|
628
835
|
end
|
629
836
|
```
|
630
837
|
|
@@ -638,13 +845,13 @@ class CitiesController < ApplicationController
|
|
638
845
|
def index
|
639
846
|
@cities = City.all
|
640
847
|
|
641
|
-
render :
|
848
|
+
render json: @cities, each_serializer: CitySerializer
|
642
849
|
end
|
643
850
|
|
644
851
|
def show
|
645
852
|
@city = City.find(params[:id])
|
646
853
|
|
647
|
-
render :
|
854
|
+
render json: @city, scope: current_admin
|
648
855
|
end
|
649
856
|
end
|
650
857
|
```
|
@@ -653,3 +860,97 @@ Assuming that the `current_admin` method needs to make a query in the database
|
|
653
860
|
for the current user, the advantage of this approach is that, by setting
|
654
861
|
`serialization_scope` to `nil`, the `index` action no longer will need to make
|
655
862
|
that query, only the `show` action will.
|
863
|
+
|
864
|
+
## Testing
|
865
|
+
|
866
|
+
In order to test a Serializer, you can just call `.new` on it, passing the object to serialize:
|
867
|
+
|
868
|
+
### MiniTest
|
869
|
+
|
870
|
+
```ruby
|
871
|
+
class TestPostSerializer < Minitest::Test
|
872
|
+
def setup
|
873
|
+
@serializer = PostSerializer.new Post.new(id: 123, title: 'some title', body: 'some text')
|
874
|
+
end
|
875
|
+
|
876
|
+
def test_special_json_for_api
|
877
|
+
assert_equal '{"post":{"id":123,"title":"some title","body":"some text"}}', @serializer.to_json
|
878
|
+
end
|
879
|
+
```
|
880
|
+
|
881
|
+
### RSpec
|
882
|
+
|
883
|
+
```ruby
|
884
|
+
describe PostSerializer do
|
885
|
+
it "creates special JSON for the API" do
|
886
|
+
serializer = PostSerializer.new Post.new(id: 123, title: 'some title', body: 'some text')
|
887
|
+
expect(serializer.to_json).to eql('{"post":{"id":123,"title":"some title","body":"some text"}}')
|
888
|
+
end
|
889
|
+
end
|
890
|
+
```
|
891
|
+
|
892
|
+
## Caching
|
893
|
+
|
894
|
+
NOTE: This functionality was removed from AMS and it's in the TODO list.
|
895
|
+
We need to re-think and re-design the caching strategy for the next
|
896
|
+
version of AMS.
|
897
|
+
|
898
|
+
To cache a serializer, call `cached` and define a `cache_key` method:
|
899
|
+
|
900
|
+
```ruby
|
901
|
+
class PostSerializer < ActiveModel::Serializer
|
902
|
+
cached # enables caching for this serializer
|
903
|
+
|
904
|
+
attributes :title, :body
|
905
|
+
|
906
|
+
def cache_key
|
907
|
+
[object, scope]
|
908
|
+
end
|
909
|
+
end
|
910
|
+
```
|
911
|
+
|
912
|
+
The caching interface uses `Rails.cache` under the hood.
|
913
|
+
|
914
|
+
# ApplicationSerializer
|
915
|
+
|
916
|
+
By default, new serializers descend from ActiveModel::Serializer. However, if you wish to share behaviour across your serializers you can create an ApplicationSerializer at ```app/serializers/application_serializer.rb```:
|
917
|
+
|
918
|
+
```ruby
|
919
|
+
class ApplicationSerializer < ActiveModel::Serializer
|
920
|
+
end
|
921
|
+
```
|
922
|
+
|
923
|
+
Any newly generated serializers will automatically descend from ApplicationSerializer.
|
924
|
+
|
925
|
+
```
|
926
|
+
$ rails g serializer post
|
927
|
+
```
|
928
|
+
|
929
|
+
now generates:
|
930
|
+
|
931
|
+
```ruby
|
932
|
+
class PostSerializer < ApplicationSerializer
|
933
|
+
attributes :id
|
934
|
+
end
|
935
|
+
````
|
936
|
+
|
937
|
+
# Design and Implementation Guidelines
|
938
|
+
|
939
|
+
## Keep it Simple
|
940
|
+
|
941
|
+
`ActiveModel::Serializers` is capable of producing complex JSON views/large object
|
942
|
+
trees, and it may be tempting to design in this way so that your client can make
|
943
|
+
fewer requests to get data and so that related querying can be optimized.
|
944
|
+
However, keeping things simple in your serializers and controllers may
|
945
|
+
significantly reduce complexity and maintenance over the long-term development
|
946
|
+
of your application. Please consider reducing the complexity of the JSON views
|
947
|
+
you provide via the serializers as you build out your application, so that
|
948
|
+
controllers/services can be more easily reused without a lot of complexity
|
949
|
+
later.
|
950
|
+
|
951
|
+
## Performance
|
952
|
+
|
953
|
+
As you develop your controllers or other code that utilizes serializers, try to
|
954
|
+
avoid n+1 queries by ensuring that data loads in an optimal fashion, e.g. if you
|
955
|
+
are using ActiveRecord, you might want to use query includes or joins as needed
|
956
|
+
to make the data available that the serializer(s) need.
|