active_model_serializers 0.8.3 → 0.10.0.rc1
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/.gitignore +3 -0
- data/.travis.yml +19 -20
- data/CHANGELOG.md +8 -67
- data/CONTRIBUTING.md +31 -0
- data/Gemfile +14 -1
- data/{MIT-LICENSE.txt → LICENSE.txt} +3 -2
- data/README.md +166 -495
- data/Rakefile +6 -12
- data/active_model_serializers.gemspec +21 -19
- data/lib/action_controller/serialization.rb +28 -27
- data/lib/active_model/serializer/adapter/fragment_cache.rb +78 -0
- data/lib/active_model/serializer/adapter/json/fragment_cache.rb +15 -0
- data/lib/active_model/serializer/adapter/json.rb +52 -0
- data/lib/active_model/serializer/adapter/json_api/fragment_cache.rb +22 -0
- data/lib/active_model/serializer/adapter/json_api.rb +152 -0
- data/lib/active_model/serializer/adapter/null.rb +11 -0
- data/lib/active_model/serializer/adapter.rb +87 -0
- data/lib/active_model/serializer/array_serializer.rb +32 -0
- data/lib/active_model/serializer/configuration.rb +13 -0
- data/lib/active_model/serializer/fieldset.rb +40 -0
- data/lib/active_model/{serializers → serializer}/version.rb +1 -1
- data/lib/active_model/serializer.rb +179 -436
- data/lib/active_model_serializers.rb +9 -86
- data/lib/generators/serializer/USAGE +0 -3
- data/lib/generators/serializer/serializer_generator.rb +1 -6
- data/lib/generators/serializer/templates/serializer.rb +2 -13
- data/test/action_controller/adapter_selector_test.rb +51 -0
- data/test/action_controller/explicit_serializer_test.rb +110 -0
- data/test/action_controller/json_api_linked_test.rb +173 -0
- data/test/{serialization_scope_name_test.rb → action_controller/serialization_scope_name_test.rb} +7 -11
- data/test/action_controller/serialization_test.rb +365 -0
- data/test/adapter/fragment_cache_test.rb +27 -0
- data/test/adapter/json/belongs_to_test.rb +41 -0
- data/test/adapter/json/collection_test.rb +59 -0
- data/test/adapter/json/has_many_test.rb +36 -0
- data/test/adapter/json_api/belongs_to_test.rb +147 -0
- data/test/adapter/json_api/collection_test.rb +89 -0
- data/test/adapter/json_api/has_many_embed_ids_test.rb +45 -0
- data/test/adapter/json_api/has_many_explicit_serializer_test.rb +98 -0
- data/test/adapter/json_api/has_many_test.rb +106 -0
- data/test/adapter/json_api/has_one_test.rb +59 -0
- data/test/adapter/json_api/linked_test.rb +257 -0
- data/test/adapter/json_test.rb +34 -0
- data/test/adapter/null_test.rb +25 -0
- data/test/adapter_test.rb +43 -0
- data/test/array_serializer_test.rb +21 -67
- data/test/fixtures/poro.rb +206 -0
- data/test/serializers/adapter_for_test.rb +50 -0
- data/test/serializers/associations_test.rb +106 -0
- data/test/serializers/attribute_test.rb +23 -0
- data/test/serializers/attributes_test.rb +28 -0
- data/test/serializers/cache_test.rb +128 -0
- data/test/serializers/configuration_test.rb +15 -0
- data/test/serializers/fieldset_test.rb +26 -0
- data/test/{generators_test.rb → serializers/generators_test.rb} +1 -27
- data/test/serializers/meta_test.rb +78 -0
- data/test/serializers/options_test.rb +21 -0
- data/test/serializers/serializer_for_test.rb +56 -0
- data/test/serializers/urls_test.rb +26 -0
- data/test/test_helper.rb +22 -13
- metadata +101 -42
- data/DESIGN.textile +0 -586
- data/Gemfile.edge +0 -9
- data/bench/perf.rb +0 -43
- data/cruft.md +0 -19
- data/lib/active_model/array_serializer.rb +0 -104
- 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/test/association_test.rb +0 -592
- data/test/caching_test.rb +0 -96
- data/test/no_serialization_scope_test.rb +0 -34
- 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,67 +1,79 @@
|
|
1
|
-
|
1
|
+
# ActiveModel::Serializers
|
2
2
|
|
3
|
-
|
3
|
+
[](https://travis-ci.org/rails-api/active_model_serializers)
|
4
4
|
|
5
|
-
|
6
|
-
encapsulate serialization of `ActiveModel` objects, including `ActiveRecord`
|
7
|
-
objects.
|
5
|
+
ActiveModel::Serializers brings convention over configuration to your JSON generation.
|
8
6
|
|
9
|
-
|
10
|
-
|
11
|
-
|
7
|
+
AMS does this through two components: **serializers** and **adapters**.
|
8
|
+
Serializers describe _which_ attributes and relationships should be serialized.
|
9
|
+
Adapters describe _how_ attributes and relationships should be serialized.
|
12
10
|
|
13
|
-
|
14
|
-
development.**
|
11
|
+
# RELEASE CANDIDATE, PLEASE READ
|
15
12
|
|
16
|
-
|
13
|
+
This is the master branch of AMS. It will become the `0.10.0` release when it's
|
14
|
+
ready. Currently this is a release candidate. This is **not** backward
|
15
|
+
compatible with `0.9.0` or `0.8.0`.
|
17
16
|
|
18
|
-
|
19
|
-
|
17
|
+
`0.10.x` will be based on the `0.8.0` code, but with a more flexible
|
18
|
+
architecture. We'd love your help. [Learn how you can help here.](https://github.com/rails-api/active_model_serializers/blob/master/CONTRIBUTING.md)
|
19
|
+
|
20
|
+
## Example
|
21
|
+
|
22
|
+
Given two models, a `Post(title: string, body: text)` and a
|
23
|
+
`Comment(name:string, body:text, post_id:integer)`, you will have two
|
24
|
+
serializers:
|
20
25
|
|
21
26
|
```ruby
|
22
|
-
|
23
|
-
|
27
|
+
class PostSerializer < ActiveModel::Serializer
|
28
|
+
cache key: 'posts', expires_in: 3.hours
|
29
|
+
attributes :title, :body
|
24
30
|
|
25
|
-
|
31
|
+
has_many :comments
|
26
32
|
|
27
|
-
|
28
|
-
|
33
|
+
url :post
|
34
|
+
end
|
29
35
|
```
|
30
36
|
|
31
|
-
|
37
|
+
and
|
32
38
|
|
33
|
-
|
34
|
-
|
39
|
+
```ruby
|
40
|
+
class CommentSerializer < ActiveModel::Serializer
|
41
|
+
attributes :name, :body
|
35
42
|
|
36
|
-
|
37
|
-
|
43
|
+
belongs_to :post
|
44
|
+
|
45
|
+
url [:post, :comment]
|
46
|
+
end
|
38
47
|
```
|
39
48
|
|
40
|
-
|
41
|
-
|
42
|
-
|
49
|
+
Generally speaking, you as a user of AMS will write (or generate) these
|
50
|
+
serializer classes. If you want to use a different adapter, such as a JsonApi, you can
|
51
|
+
change this in an initializer:
|
43
52
|
|
44
|
-
```
|
45
|
-
|
53
|
+
```ruby
|
54
|
+
ActiveModel::Serializer.config.adapter = ActiveModel::Serializer::Adapter::JsonApi
|
46
55
|
```
|
47
56
|
|
48
|
-
|
57
|
+
or
|
49
58
|
|
50
|
-
|
51
|
-
|
52
|
-
|
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.
|
59
|
+
```ruby
|
60
|
+
ActiveModel::Serializer.config.adapter = :json_api
|
61
|
+
```
|
59
62
|
|
60
|
-
|
63
|
+
You won't need to implement an adapter unless you wish to use a new format or
|
64
|
+
media type with AMS.
|
61
65
|
|
62
|
-
|
66
|
+
If you would like the key in the outputted JSON to be different from its name in ActiveRecord, you can use the :key option to customize it:
|
63
67
|
|
64
|
-
|
68
|
+
```ruby
|
69
|
+
class PostSerializer < ActiveModel::Serializer
|
70
|
+
attributes :id, :body
|
71
|
+
|
72
|
+
# look up :subject on the model, but use +title+ in the JSON
|
73
|
+
attribute :subject, :key => :title
|
74
|
+
has_many :comments
|
75
|
+
end
|
76
|
+
```
|
65
77
|
|
66
78
|
In your controllers, when you use `render :json`, Rails will now first search
|
67
79
|
for a serializer for the object and use it if available.
|
@@ -70,7 +82,8 @@ for a serializer for the object and use it if available.
|
|
70
82
|
class PostsController < ApplicationController
|
71
83
|
def show
|
72
84
|
@post = Post.find(params[:id])
|
73
|
-
|
85
|
+
|
86
|
+
render json: @post
|
74
87
|
end
|
75
88
|
end
|
76
89
|
```
|
@@ -78,578 +91,236 @@ end
|
|
78
91
|
In this case, Rails will look for a serializer named `PostSerializer`, and if
|
79
92
|
it exists, use it to serialize the `Post`.
|
80
93
|
|
81
|
-
|
82
|
-
note that any options passed to `render :json` will be passed to your
|
83
|
-
serializer and available as `@options` inside.
|
94
|
+
### Specify a serializer
|
84
95
|
|
85
|
-
|
96
|
+
If you wish to use a serializer other than the default, you can explicitly pass it to the renderer.
|
86
97
|
|
87
|
-
#### 1.
|
98
|
+
#### 1. For a resource:
|
88
99
|
|
89
100
|
```ruby
|
90
|
-
|
91
|
-
def active_model_serializer
|
92
|
-
FancyPostSerializer
|
93
|
-
end
|
94
|
-
end
|
101
|
+
render json: @post, serializer: PostPreviewSerializer
|
95
102
|
```
|
96
103
|
|
97
|
-
#### 2.
|
104
|
+
#### 2. For an array resource:
|
98
105
|
|
99
106
|
```ruby
|
100
|
-
|
101
|
-
|
107
|
+
# Use the default `ArraySerializer`, which will use `each_serializer` to
|
108
|
+
# serialize each element
|
109
|
+
render json: @posts, each_serializer: PostPreviewSerializer
|
102
110
|
|
103
|
-
|
104
|
-
|
105
|
-
In your controllers, when you use `render :json` for an array of objects, AMS will
|
106
|
-
use `ActiveModel::ArraySerializer` (included in this project) as the base serializer,
|
107
|
-
and the individual `Serializer` for the objects contained in that array.
|
108
|
-
|
109
|
-
```ruby
|
110
|
-
class PostSerializer < ActiveModel::Serializer
|
111
|
-
attributes :title, :body
|
112
|
-
end
|
113
|
-
|
114
|
-
class PostsController < ApplicationController
|
115
|
-
def index
|
116
|
-
@posts = Post.all
|
117
|
-
render :json => @posts
|
118
|
-
end
|
119
|
-
end
|
111
|
+
# Or, you can explicitly provide the collection serializer as well
|
112
|
+
render json: @posts, serializer: PaginatedSerializer, each_serializer: PostPreviewSerializer
|
120
113
|
```
|
121
114
|
|
122
|
-
|
115
|
+
### Meta
|
123
116
|
|
124
|
-
|
125
|
-
|
126
|
-
"posts":
|
127
|
-
[
|
128
|
-
{ "title": "Post 1", "body": "Hello!" },
|
129
|
-
{ "title": "Post 2", "body": "Goodbye!" }
|
130
|
-
]
|
131
|
-
}
|
132
|
-
```
|
133
|
-
|
134
|
-
By default, the root element is the name of the controller. For example, `PostsController`
|
135
|
-
generates a root element "posts". To change it:
|
117
|
+
If you want a `meta` attribute in your response, specify it in the `render`
|
118
|
+
call:
|
136
119
|
|
137
120
|
```ruby
|
138
|
-
render :
|
139
|
-
```
|
140
|
-
|
141
|
-
You may disable the root element for arrays at the top level, which will result in
|
142
|
-
more concise json. See the next section for ways on how to do this. Disabling the
|
143
|
-
root element of the array with any of those methods will produce
|
144
|
-
|
145
|
-
```json
|
146
|
-
[
|
147
|
-
{ "title": "Post 1", "body": "Hello!" },
|
148
|
-
{ "title": "Post 2", "body": "Goodbye!" }
|
149
|
-
]
|
121
|
+
render json: @post, meta: { total: 10 }
|
150
122
|
```
|
151
123
|
|
152
|
-
|
124
|
+
The key can be customized using `meta_key` option.
|
153
125
|
|
154
126
|
```ruby
|
155
|
-
render :
|
127
|
+
render json: @post, meta: { total: 10 }, meta_key: "custom_meta"
|
156
128
|
```
|
157
129
|
|
158
|
-
|
159
|
-
|
160
|
-
You have 4 options to disable the root element, each with a slightly different scope:
|
130
|
+
`meta` will only be included in your response if there's a root. For instance,
|
131
|
+
it won't be included in array responses.
|
161
132
|
|
162
|
-
|
133
|
+
### Root key
|
163
134
|
|
164
|
-
|
135
|
+
If you want to define a custom root for your response, specify it in the `render`
|
136
|
+
call:
|
165
137
|
|
166
138
|
```ruby
|
167
|
-
|
168
|
-
# Disable for all serializers (except ArraySerializer)
|
169
|
-
ActiveModel::Serializer.root = false
|
170
|
-
|
171
|
-
# Disable for ArraySerializer
|
172
|
-
ActiveModel::ArraySerializer.root = false
|
173
|
-
end
|
139
|
+
render json: @post, root: "articles"
|
174
140
|
```
|
175
141
|
|
176
|
-
|
142
|
+
### Overriding association methods
|
177
143
|
|
178
|
-
|
179
|
-
render :json => @posts, :root => false
|
180
|
-
```
|
181
|
-
|
182
|
-
#### 3. Subclass the serializer, and specify using it
|
144
|
+
If you want to override any association, you can use:
|
183
145
|
|
184
146
|
```ruby
|
185
|
-
class
|
186
|
-
|
187
|
-
end
|
188
|
-
|
189
|
-
# controller:
|
190
|
-
render :json => @posts, :serializer => CustomArraySerializer
|
191
|
-
```
|
192
|
-
|
193
|
-
#### 4. Define default_serializer_options in your controller
|
147
|
+
class PostSerializer < ActiveModel::Serializer
|
148
|
+
attributes :id, :body
|
194
149
|
|
195
|
-
|
196
|
-
all serializers in actions of this controller and it's children will use them.
|
197
|
-
One of the options may be `root: false`
|
150
|
+
has_many :comments
|
198
151
|
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
root: false
|
203
|
-
}
|
152
|
+
def comments
|
153
|
+
object.comments.active
|
154
|
+
end
|
204
155
|
end
|
205
156
|
```
|
206
157
|
|
207
|
-
|
208
|
-
|
209
|
-
If you find that your project is already relying on the old rails to_json
|
210
|
-
change `render :json` to `render :json => @your_object.to_json`.
|
211
|
-
|
212
|
-
# Attributes and Associations
|
158
|
+
### Overriding attribute methods
|
213
159
|
|
214
|
-
|
215
|
-
you would like to include in the serialized form.
|
160
|
+
If you want to override any attribute, you can use:
|
216
161
|
|
217
162
|
```ruby
|
218
163
|
class PostSerializer < ActiveModel::Serializer
|
219
|
-
attributes :id, :
|
220
|
-
has_many :comments
|
221
|
-
end
|
222
|
-
```
|
223
|
-
|
224
|
-
## Attributes
|
225
|
-
|
226
|
-
For specified attributes, a serializer will look up the attribute on the
|
227
|
-
object you passed to `render :json`. It uses
|
228
|
-
`read_attribute_for_serialization`, which `ActiveRecord` objects implement as a
|
229
|
-
regular attribute lookup.
|
230
|
-
|
231
|
-
Before looking up the attribute on the object, a serializer will check for the
|
232
|
-
presence of a method with the name of the attribute. This allows serializers to
|
233
|
-
include properties beyond the simple attributes of the model. For example:
|
164
|
+
attributes :id, :body
|
234
165
|
|
235
|
-
|
236
|
-
class PersonSerializer < ActiveModel::Serializer
|
237
|
-
attributes :first_name, :last_name, :full_name
|
166
|
+
has_many :comments
|
238
167
|
|
239
|
-
def
|
240
|
-
|
168
|
+
def body
|
169
|
+
object.body.downcase
|
241
170
|
end
|
242
171
|
end
|
243
172
|
```
|
244
173
|
|
245
|
-
|
246
|
-
serialized as `object`.
|
174
|
+
### Built in Adapters
|
247
175
|
|
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).
|
176
|
+
#### JSONAPI
|
252
177
|
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
178
|
+
This adapter follows RC3 of the format specified in
|
179
|
+
[jsonapi.org/format](http://jsonapi.org/format). It will include the associated
|
180
|
+
resources in the `"included"` member when the resource names are included in the
|
181
|
+
`include` option.
|
257
182
|
|
258
183
|
```ruby
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
def include_author?
|
263
|
-
current_user.admin?
|
264
|
-
end
|
265
|
-
end
|
184
|
+
render @posts, include: ['authors', 'comments']
|
185
|
+
# or
|
186
|
+
render @posts, include: 'authors,comments'
|
266
187
|
```
|
267
188
|
|
268
|
-
|
269
|
-
calculated without some sophisticated static code analysis. To specify the
|
270
|
-
type of a computed attribute:
|
189
|
+
## Installation
|
271
190
|
|
272
|
-
|
273
|
-
class PersonSerializer < ActiveModel::Serializer
|
274
|
-
attributes :first_name, :last_name, {:full_name => :string}
|
191
|
+
Add this line to your application's Gemfile:
|
275
192
|
|
276
|
-
def full_name
|
277
|
-
"#{object.first_name} #{object.last_name}"
|
278
|
-
end
|
279
|
-
end
|
280
193
|
```
|
281
|
-
|
282
|
-
If you would like the key in the outputted JSON to be different from its name
|
283
|
-
in ActiveRecord, you can use the `:key` option to customize it:
|
284
|
-
|
285
|
-
```ruby
|
286
|
-
class PostSerializer < ActiveModel::Serializer
|
287
|
-
attributes :id, :body
|
288
|
-
|
289
|
-
# look up :subject on the model, but use +title+ in the JSON
|
290
|
-
attribute :subject, :key => :title
|
291
|
-
has_many :comments
|
292
|
-
end
|
194
|
+
gem 'active_model_serializers'
|
293
195
|
```
|
294
196
|
|
295
|
-
|
296
|
-
option:
|
197
|
+
And then execute:
|
297
198
|
|
298
|
-
```ruby
|
299
|
-
render :json => @posts, :serializer => CustomArraySerializer, :meta => {:total => 10}
|
300
199
|
```
|
301
|
-
|
302
|
-
The above usage of `:meta` will produce the following:
|
303
|
-
|
304
|
-
```json
|
305
|
-
{
|
306
|
-
"meta": { "total": 10 },
|
307
|
-
"posts": [
|
308
|
-
{ "title": "Post 1", "body": "Hello!" },
|
309
|
-
{ "title": "Post 2", "body": "Goodbye!" }
|
310
|
-
]
|
311
|
-
}
|
200
|
+
$ bundle
|
312
201
|
```
|
313
202
|
|
314
|
-
|
315
|
-
|
316
|
-
```ruby
|
317
|
-
render :json => @posts, :serializer => CustomArraySerializer, :meta => {:total => 10}, :meta_key => 'meta_object'
|
318
|
-
```
|
203
|
+
## Creating a Serializer
|
319
204
|
|
320
|
-
The
|
205
|
+
The easiest way to create a new serializer is to generate a new resource, which
|
206
|
+
will generate a serializer at the same time:
|
321
207
|
|
322
|
-
```json
|
323
|
-
{
|
324
|
-
"meta_object": { "total": 10 },
|
325
|
-
"posts": [
|
326
|
-
{ "title": "Post 1", "body": "Hello!" },
|
327
|
-
{ "title": "Post 2", "body": "Goodbye!" }
|
328
|
-
]
|
329
|
-
}
|
330
208
|
```
|
331
|
-
|
332
|
-
If you would like direct, low-level control of attribute serialization, you can
|
333
|
-
completely override the `attributes` method to return the hash you need:
|
334
|
-
|
335
|
-
```ruby
|
336
|
-
class PersonSerializer < ActiveModel::Serializer
|
337
|
-
attributes :first_name, :last_name
|
338
|
-
|
339
|
-
def attributes
|
340
|
-
hash = super
|
341
|
-
if current_user.admin?
|
342
|
-
hash["ssn"] = object.ssn
|
343
|
-
hash["secret"] = object.mothers_maiden_name
|
344
|
-
end
|
345
|
-
hash
|
346
|
-
end
|
347
|
-
end
|
209
|
+
$ rails g resource post title:string body:string
|
348
210
|
```
|
349
211
|
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
then serialize each element of the association. For instance, a `has_many
|
354
|
-
:comments` association will create a new `CommentSerializer` for each comment
|
355
|
-
and use it to serialize the comment.
|
356
|
-
|
357
|
-
By default, serializers simply look up the association on the original object.
|
358
|
-
You can customize this behavior by implementing a method with the name of the
|
359
|
-
association and returning a different Array. Often, you will do this to
|
360
|
-
customize the objects returned based on the current user.
|
361
|
-
|
362
|
-
```ruby
|
363
|
-
class PostSerializer < ActiveModel::Serializer
|
364
|
-
attributes :id, :title, :body
|
365
|
-
has_many :comments
|
212
|
+
This will generate a serializer in `app/serializers/post_serializer.rb` for
|
213
|
+
your new model. You can also generate a serializer for an existing model with
|
214
|
+
the serializer generator:
|
366
215
|
|
367
|
-
# only let the user see comments he created.
|
368
|
-
def comments
|
369
|
-
object.comments.where(:created_by => current_user)
|
370
|
-
end
|
371
|
-
end
|
372
216
|
```
|
373
|
-
|
374
|
-
As with attributes, you can change the JSON key that the serializer should
|
375
|
-
use for a particular association.
|
376
|
-
|
377
|
-
```ruby
|
378
|
-
class PostSerializer < ActiveModel::Serializer
|
379
|
-
attributes :id, :title, :body
|
380
|
-
|
381
|
-
# look up comments, but use +my_comments+ as the key in JSON
|
382
|
-
has_many :comments, :key => :my_comments
|
383
|
-
end
|
217
|
+
$ rails g serializer post
|
384
218
|
```
|
385
219
|
|
386
|
-
|
387
|
-
|
388
|
-
should be included in the output. For example:
|
220
|
+
The generated seralizer will contain basic `attributes` and
|
221
|
+
`has_many`/`has_one`/`belongs_to` declarations, based on the model. For example:
|
389
222
|
|
390
223
|
```ruby
|
391
224
|
class PostSerializer < ActiveModel::Serializer
|
392
|
-
attributes :
|
225
|
+
attributes :title, :body
|
226
|
+
|
393
227
|
has_many :comments
|
228
|
+
has_one :author
|
394
229
|
|
395
|
-
|
396
|
-
!object.comments_disabled?
|
397
|
-
end
|
230
|
+
url :post
|
398
231
|
end
|
399
232
|
```
|
400
233
|
|
401
|
-
|
402
|
-
override `include_associations!` to specify which associations should be included:
|
234
|
+
and
|
403
235
|
|
404
236
|
```ruby
|
405
|
-
class
|
406
|
-
attributes :
|
407
|
-
has_one :author
|
408
|
-
has_many :comments
|
237
|
+
class CommentSerializer < ActiveModel::Serializer
|
238
|
+
attributes :name, :body
|
409
239
|
|
410
|
-
|
411
|
-
include! :author if current_user.admin?
|
412
|
-
include! :comments unless object.comments_disabled?
|
413
|
-
end
|
414
|
-
end
|
415
|
-
```
|
416
|
-
|
417
|
-
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.:
|
240
|
+
belongs_to :post_id
|
418
241
|
|
419
|
-
|
420
|
-
|
421
|
-
has_one :reviewer, :polymorphic => true
|
242
|
+
url [:post, :comment]
|
243
|
+
end
|
422
244
|
```
|
423
245
|
|
424
|
-
|
246
|
+
The attribute names are a **whitelist** of attributes to be serialized.
|
425
247
|
|
426
|
-
|
248
|
+
The `has_many`, `has_one`, and `belongs_to` declarations describe relationships between
|
249
|
+
resources. By default, when you serialize a `Post`, you will get its `Comment`s
|
250
|
+
as well.
|
427
251
|
|
428
|
-
|
429
|
-
you have a post, the outputted JSON will look like:
|
252
|
+
You may also use the `:serializer` option to specify a custom serializer class, for example:
|
430
253
|
|
431
|
-
```
|
432
|
-
|
433
|
-
"post": {
|
434
|
-
"id": 1,
|
435
|
-
"title": "New post",
|
436
|
-
"body": "A body!",
|
437
|
-
"comments": [
|
438
|
-
{ "id": 1, "body": "what a dumb post" }
|
439
|
-
]
|
440
|
-
}
|
441
|
-
}
|
254
|
+
```ruby
|
255
|
+
has_many :comments, serializer: CommentPreviewSerializer
|
442
256
|
```
|
443
257
|
|
444
|
-
|
445
|
-
|
446
|
-
flexible from a performance standpoint and avoids wasteful duplication.
|
258
|
+
The `url` declaration describes which named routes to use while generating URLs
|
259
|
+
for your JSON. Not every adapter will require URLs.
|
447
260
|
|
448
|
-
|
261
|
+
## Caching
|
449
262
|
|
450
|
-
```
|
451
|
-
|
452
|
-
|
263
|
+
To cache a serializer, call ```cache``` and pass its options.
|
264
|
+
The options are the same options of ```ActiveSupport::Cache::Store```, plus
|
265
|
+
a ```key``` option that will be the prefix of the object cache
|
266
|
+
on a pattern ```"#{key}/#{object.id}-#{object.updated_at}"```.
|
453
267
|
|
454
|
-
|
455
|
-
has_many :comments
|
456
|
-
end
|
457
|
-
```
|
268
|
+
The cache support is optimized to use the cached object in multiple request. An object cached on an ```show``` request will be reused at the ```index```. If there is a relationship with another cached serializer it will also be created and reused automatically.
|
458
269
|
|
459
|
-
|
460
|
-
|
461
|
-
```json
|
462
|
-
{
|
463
|
-
"post": {
|
464
|
-
"id": 1,
|
465
|
-
"title": "New post",
|
466
|
-
"body": "A body!",
|
467
|
-
"comment_ids": [ 1, 2, 3 ]
|
468
|
-
}
|
469
|
-
}
|
470
|
-
```
|
270
|
+
**[NOTE] Every object is individually cached.**
|
471
271
|
|
472
|
-
|
272
|
+
**[NOTE] The cache is automatically expired after update an object but it's not deleted.**
|
473
273
|
|
474
274
|
```ruby
|
475
|
-
|
476
|
-
attributes :id, :title, :body
|
477
|
-
|
478
|
-
has_many :comments, embed: :objects
|
479
|
-
has_many :tags, embed: :ids
|
480
|
-
end
|
481
|
-
```
|
482
|
-
|
483
|
-
The JSON will look like this:
|
484
|
-
|
485
|
-
```json
|
486
|
-
{
|
487
|
-
"post": {
|
488
|
-
"id": 1,
|
489
|
-
"title": "New post",
|
490
|
-
"body": "A body!",
|
491
|
-
"comments": [
|
492
|
-
{ "id": 1, "body": "what a dumb post" }
|
493
|
-
],
|
494
|
-
"tag_ids": [ 1, 2, 3 ]
|
495
|
-
}
|
496
|
-
}
|
275
|
+
cache(options = nil) # options: ```{key, expires_in, compress, force, race_condition_ttl}```
|
497
276
|
```
|
498
277
|
|
499
|
-
|
500
|
-
alongside the main object. This makes it easier to process the entire package
|
501
|
-
of data without having to recursively scan the tree looking for embedded
|
502
|
-
information. It also ensures that associations that are shared between several
|
503
|
-
objects (like tags), are only delivered once for the entire payload.
|
504
|
-
|
505
|
-
You can specify that the data be included like this:
|
278
|
+
Take the example bellow:
|
506
279
|
|
507
280
|
```ruby
|
508
281
|
class PostSerializer < ActiveModel::Serializer
|
509
|
-
|
282
|
+
cache key: 'post', expires_in: 3.hours
|
283
|
+
attributes :title, :body
|
510
284
|
|
511
|
-
attributes :id, :title, :body
|
512
285
|
has_many :comments
|
513
|
-
end
|
514
|
-
```
|
515
|
-
|
516
|
-
Assuming that the comments also `has_many :tags`, you will get a JSON like
|
517
|
-
this:
|
518
|
-
|
519
|
-
```json
|
520
|
-
{
|
521
|
-
"post": {
|
522
|
-
"id": 1,
|
523
|
-
"title": "New post",
|
524
|
-
"body": "A body!",
|
525
|
-
"comment_ids": [ 1, 2 ]
|
526
|
-
},
|
527
|
-
"comments": [
|
528
|
-
{ "id": 1, "body": "what a dumb post", "tag_ids": [ 1, 2 ] },
|
529
|
-
{ "id": 2, "body": "i liked it", "tag_ids": [ 1, 3 ] },
|
530
|
-
],
|
531
|
-
"tags": [
|
532
|
-
{ "id": 1, "name": "short" },
|
533
|
-
{ "id": 2, "name": "whiny" },
|
534
|
-
{ "id": 3, "name": "happy" }
|
535
|
-
]
|
536
|
-
}
|
537
|
-
```
|
538
286
|
|
539
|
-
|
540
|
-
used to reference them:
|
541
|
-
|
542
|
-
```ruby
|
543
|
-
class PostSerializer < ActiveModel::Serializer
|
544
|
-
embed :ids, :include => true
|
545
|
-
|
546
|
-
attributes :id, :title, :body
|
547
|
-
has_many :comments, :key => :comment_ids, :root => :comment_objects
|
287
|
+
url :post
|
548
288
|
end
|
549
289
|
```
|
550
290
|
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
{
|
555
|
-
"post": {
|
556
|
-
"id": 1,
|
557
|
-
"title": "New post",
|
558
|
-
"body": "A body!",
|
559
|
-
"comment_ids": [ 1 ]
|
560
|
-
},
|
561
|
-
"comment_objects": [
|
562
|
-
{ "id": 1, "body": "what a dumb post" }
|
563
|
-
]
|
564
|
-
}
|
565
|
-
```
|
566
|
-
|
567
|
-
You can also specify a different attribute to use rather than the ID of the
|
568
|
-
objects:
|
569
|
-
|
570
|
-
```ruby
|
571
|
-
class PostSerializer < ActiveModel::Serializer
|
572
|
-
embed :ids, :include => true
|
573
|
-
|
574
|
-
attributes :id, :title, :body
|
575
|
-
has_many :comments, :embed_key => :external_id
|
576
|
-
end
|
577
|
-
```
|
291
|
+
On this example every ```Post``` object will be cached with
|
292
|
+
the key ```"post/#{post.id}-#{post.updated_at}"```. You can use this key to expire it as you want,
|
293
|
+
but in this case it will be automatically expired after 3 hours.
|
578
294
|
|
579
|
-
|
580
|
-
|
581
|
-
```json
|
582
|
-
{
|
583
|
-
"post": {
|
584
|
-
"id": 1,
|
585
|
-
"title": "New post",
|
586
|
-
"body": "A body!",
|
587
|
-
"comment_ids": [ "COMM001" ]
|
588
|
-
},
|
589
|
-
"comments": [
|
590
|
-
{ "id": 1, "external_id": "COMM001", "body": "what a dumb post" }
|
591
|
-
]
|
592
|
-
}
|
593
|
-
```
|
295
|
+
### Fragmenting Caching
|
594
296
|
|
595
|
-
|
596
|
-
data in bulk and load it into a local store. For these clients, the ability to
|
597
|
-
easily see all of the data per type, rather than having to recursively scan the
|
598
|
-
data looking for information, is extremely useful.
|
297
|
+
If there is some API endpoint that shouldn't be fully cached, you can still optmise it, using Fragment Cache on the attributes and relationships that you want to cache.
|
599
298
|
|
600
|
-
|
601
|
-
Ajax requests, you probably just want to use the default embedded behavior.
|
299
|
+
You can define the attribute by using ```only``` or ```except``` option on cache method.
|
602
300
|
|
603
|
-
|
301
|
+
**[NOTE] Cache serializers will be used at their relationships**
|
604
302
|
|
605
|
-
|
606
|
-
provides to the serializer when you call `render :json`. By default, this is
|
607
|
-
`current_user`, but can be customized in your controller by calling
|
608
|
-
`serialization_scope`:
|
303
|
+
Example:
|
609
304
|
|
610
305
|
```ruby
|
611
|
-
class
|
612
|
-
|
613
|
-
|
614
|
-
```
|
615
|
-
|
616
|
-
The above example will also change the scope name from `current_user` to
|
617
|
-
`current_admin`.
|
618
|
-
|
619
|
-
Please note that, until now, `serialization_scope` doesn't accept a second
|
620
|
-
object with options for specifying which actions should or should not take a
|
621
|
-
given scope in consideration.
|
306
|
+
class PostSerializer < ActiveModel::Serializer
|
307
|
+
cache key: 'post', expires_in: 3.hours, only: [:title]
|
308
|
+
attributes :title, :body
|
622
309
|
|
623
|
-
|
310
|
+
has_many :comments
|
624
311
|
|
625
|
-
|
626
|
-
class SomeController < ApplicationController
|
627
|
-
serialization_scope :current_admin, :except => [:index, :show]
|
312
|
+
url :post
|
628
313
|
end
|
629
314
|
```
|
630
315
|
|
631
|
-
|
632
|
-
consideration for its scope, you may use something like this:
|
316
|
+
## Getting Help
|
633
317
|
|
634
|
-
|
635
|
-
class CitiesController < ApplicationController
|
636
|
-
serialization_scope nil
|
318
|
+
If you find a bug, please report an [Issue](https://github.com/rails-api/active_model_serializers/issues/new).
|
637
319
|
|
638
|
-
|
639
|
-
@cities = City.all
|
320
|
+
If you have a question, please [post to Stack Overflow](http://stackoverflow.com/questions/tagged/active-model-serializers).
|
640
321
|
|
641
|
-
|
642
|
-
end
|
322
|
+
Thanks!
|
643
323
|
|
644
|
-
|
645
|
-
@city = City.find(params[:id])
|
646
|
-
|
647
|
-
render :json => @city, :scope => current_admin, :scope_name => :current_admin
|
648
|
-
end
|
649
|
-
end
|
650
|
-
```
|
324
|
+
# Contributing
|
651
325
|
|
652
|
-
|
653
|
-
for the current user, the advantage of this approach is that, by setting
|
654
|
-
`serialization_scope` to `nil`, the `index` action no longer will need to make
|
655
|
-
that query, only the `show` action will.
|
326
|
+
See [CONTRIBUTING.md](https://github.com/rails-api/active_model_serializers/blob/master/CONTRIBUTING.md)
|