active_model_serializers 0.8.0 → 0.10.12
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 +5 -5
- data/CHANGELOG.md +702 -6
- data/{MIT-LICENSE.txt → MIT-LICENSE} +3 -2
- data/README.md +195 -536
- data/lib/action_controller/serialization.rb +53 -35
- data/lib/active_model/serializable_resource.rb +13 -0
- data/lib/active_model/serializer/adapter/attributes.rb +17 -0
- data/lib/active_model/serializer/adapter/base.rb +20 -0
- data/lib/active_model/serializer/adapter/json.rb +17 -0
- data/lib/active_model/serializer/adapter/json_api.rb +17 -0
- data/lib/active_model/serializer/adapter/null.rb +17 -0
- data/lib/active_model/serializer/adapter.rb +26 -0
- data/lib/active_model/serializer/array_serializer.rb +14 -0
- data/lib/active_model/serializer/association.rb +73 -0
- data/lib/active_model/serializer/attribute.rb +27 -0
- data/lib/active_model/serializer/belongs_to_reflection.rb +13 -0
- data/lib/active_model/serializer/collection_serializer.rb +90 -0
- data/lib/active_model/serializer/concerns/caching.rb +305 -0
- data/lib/active_model/serializer/error_serializer.rb +16 -0
- data/lib/active_model/serializer/errors_serializer.rb +34 -0
- data/lib/active_model/serializer/field.rb +92 -0
- data/lib/active_model/serializer/fieldset.rb +33 -0
- data/lib/active_model/serializer/has_many_reflection.rb +12 -0
- data/lib/active_model/serializer/has_one_reflection.rb +9 -0
- data/lib/active_model/serializer/lazy_association.rb +99 -0
- data/lib/active_model/serializer/link.rb +23 -0
- data/lib/active_model/serializer/lint.rb +152 -0
- data/lib/active_model/serializer/null.rb +19 -0
- data/lib/active_model/serializer/reflection.rb +212 -0
- data/lib/active_model/serializer/version.rb +7 -0
- data/lib/active_model/serializer.rb +352 -441
- data/lib/active_model_serializers/adapter/attributes.rb +36 -0
- data/lib/active_model_serializers/adapter/base.rb +85 -0
- data/lib/active_model_serializers/adapter/json.rb +23 -0
- data/lib/active_model_serializers/adapter/json_api/deserialization.rb +215 -0
- data/lib/active_model_serializers/adapter/json_api/error.rb +98 -0
- data/lib/active_model_serializers/adapter/json_api/jsonapi.rb +51 -0
- data/lib/active_model_serializers/adapter/json_api/link.rb +85 -0
- data/lib/active_model_serializers/adapter/json_api/meta.rb +39 -0
- data/lib/active_model_serializers/adapter/json_api/pagination_links.rb +90 -0
- data/lib/active_model_serializers/adapter/json_api/relationship.rb +106 -0
- data/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +68 -0
- data/lib/active_model_serializers/adapter/json_api.rb +535 -0
- data/lib/active_model_serializers/adapter/null.rb +11 -0
- data/lib/active_model_serializers/adapter.rb +100 -0
- data/lib/active_model_serializers/callbacks.rb +57 -0
- data/lib/active_model_serializers/deprecate.rb +56 -0
- data/lib/active_model_serializers/deserialization.rb +17 -0
- data/lib/active_model_serializers/json_pointer.rb +16 -0
- data/lib/active_model_serializers/logging.rb +124 -0
- data/lib/active_model_serializers/lookup_chain.rb +82 -0
- data/lib/active_model_serializers/model/caching.rb +25 -0
- data/lib/active_model_serializers/model.rb +132 -0
- data/lib/active_model_serializers/railtie.rb +52 -0
- data/lib/active_model_serializers/register_jsonapi_renderer.rb +80 -0
- data/lib/active_model_serializers/serializable_resource.rb +84 -0
- data/lib/active_model_serializers/serialization_context.rb +41 -0
- data/lib/active_model_serializers/test/schema.rb +140 -0
- data/lib/active_model_serializers/test/serializer.rb +127 -0
- data/lib/active_model_serializers/test.rb +9 -0
- data/lib/active_model_serializers.rb +49 -83
- data/lib/generators/rails/USAGE +6 -0
- data/lib/generators/rails/resource_override.rb +12 -0
- data/lib/generators/rails/serializer_generator.rb +38 -0
- data/lib/generators/rails/templates/serializer.rb.erb +8 -0
- data/lib/grape/active_model_serializers.rb +18 -0
- data/lib/grape/formatters/active_model_serializers.rb +34 -0
- data/lib/grape/helpers/active_model_serializers.rb +19 -0
- data/lib/tasks/rubocop.rake +55 -0
- metadata +291 -77
- data/.gitignore +0 -18
- data/.travis.yml +0 -29
- data/DESIGN.textile +0 -586
- data/Gemfile +0 -6
- data/Gemfile.edge +0 -9
- data/Rakefile +0 -18
- data/active_model_serializers.gemspec +0 -25
- 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_model/serializers/version.rb +0 -5
- data/lib/active_record/serializer_override.rb +0 -16
- data/lib/generators/resource_override.rb +0 -13
- data/lib/generators/serializer/USAGE +0 -9
- 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 -54
- 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 -394
- data/test/serializer_support_test.rb +0 -51
- data/test/serializer_test.rb +0 -1452
- data/test/test_fakes.rb +0 -194
- data/test/test_helper.rb +0 -41
data/README.md
CHANGED
|
@@ -1,646 +1,305 @@
|
|
|
1
|
-
|
|
1
|
+
# ActiveModelSerializers
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<table>
|
|
4
|
+
<tr>
|
|
5
|
+
<td>Build Status</td>
|
|
6
|
+
<td>
|
|
7
|
+
<a href="https://travis-ci.org/rails-api/active_model_serializers"><img src="https://api.travis-ci.org/rails-api/active_model_serializers.svg?branch=0-10-stable" alt="Build Status" ></a>
|
|
8
|
+
<a href="https://ci.appveyor.com/project/bf4/active-model-serializers/branch/0-10-stable"><img src="https://ci.appveyor.com/api/projects/status/x6xdjydutm54gvyt/branch/master?svg=true" alt="Build status"></a>
|
|
9
|
+
</td>
|
|
10
|
+
</tr>
|
|
11
|
+
<tr>
|
|
12
|
+
<td>Code Quality</td>
|
|
13
|
+
<td>
|
|
14
|
+
<a href="https://codeclimate.com/github/rails-api/active_model_serializers"><img src="https://codeclimate.com/github/rails-api/active_model_serializers/badges/gpa.svg" alt="Code Quality"></a>
|
|
15
|
+
<a href="https://codebeat.co/projects/github-com-rails-api-active_model_serializers"><img src="https://codebeat.co/badges/a9ab35fa-8b5a-4680-9d4e-a81f9a55ebcd" alt="codebeat" ></a>
|
|
16
|
+
<a href="https://codeclimate.com/github/rails-api/active_model_serializers/coverage"><img src="https://codeclimate.com/github/rails-api/active_model_serializers/badges/coverage.svg" alt="Test Coverage"></a>
|
|
17
|
+
</td>
|
|
18
|
+
</tr>
|
|
19
|
+
<tr>
|
|
20
|
+
<td>Issue Stats</td>
|
|
21
|
+
<td>
|
|
22
|
+
<a href="https://github.com/rails-api/active_model_serializers/pulse/monthly">Pulse</a>
|
|
23
|
+
</td>
|
|
24
|
+
</tr>
|
|
25
|
+
</table>
|
|
4
26
|
|
|
5
|
-
|
|
6
|
-
encapsulate serialization of `ActiveModel` objects, including `ActiveRecord`
|
|
7
|
-
objects.
|
|
27
|
+
## About
|
|
8
28
|
|
|
9
|
-
|
|
10
|
-
customize serialization based upon whether a user is authorized to see the
|
|
11
|
-
content.
|
|
29
|
+
ActiveModelSerializers brings convention over configuration to your JSON generation.
|
|
12
30
|
|
|
13
|
-
|
|
14
|
-
development.**
|
|
31
|
+
ActiveModelSerializers works through two components: **serializers** and **adapters**.
|
|
15
32
|
|
|
16
|
-
|
|
33
|
+
Serializers describe _which_ attributes and relationships should be serialized.
|
|
17
34
|
|
|
18
|
-
|
|
19
|
-
`Gemfile`:
|
|
35
|
+
Adapters describe _how_ attributes and relationships should be serialized.
|
|
20
36
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
37
|
+
SerializableResource co-ordinates the resource, Adapter and Serializer to produce the
|
|
38
|
+
resource serialization. The serialization has the `#as_json`, `#to_json` and `#serializable_hash`
|
|
39
|
+
methods used by the Rails JSON Renderer. (SerializableResource actually delegates
|
|
40
|
+
these methods to the adapter.)
|
|
24
41
|
|
|
25
|
-
|
|
42
|
+
By default ActiveModelSerializers will use the **Attributes Adapter** (no JSON root).
|
|
43
|
+
But we strongly advise you to use **JsonApi Adapter**, which
|
|
44
|
+
follows 1.0 of the format specified in [jsonapi.org/format](https://jsonapi.org/format).
|
|
45
|
+
Check how to change the adapter in the sections below.
|
|
26
46
|
|
|
27
|
-
|
|
28
|
-
$ bundle install
|
|
29
|
-
```
|
|
47
|
+
`0.10.x` is **not** backward compatible with `0.9.x` nor `0.8.x`.
|
|
30
48
|
|
|
31
|
-
|
|
49
|
+
`0.10.x` is based on the `0.8.0` code, but with a more flexible
|
|
50
|
+
architecture. We'd love your help. [Learn how you can help here.](CONTRIBUTING.md)
|
|
32
51
|
|
|
33
|
-
|
|
34
|
-
will generate a serializer at the same time:
|
|
52
|
+
## Installation
|
|
35
53
|
|
|
36
|
-
|
|
37
|
-
$ rails g resource post title:string body:string
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
This will generate a serializer in `app/serializers/post_serializer.rb` for
|
|
41
|
-
your new model. You can also generate a serializer for an existing model with
|
|
42
|
-
the serializer generator:
|
|
54
|
+
Add this line to your application's Gemfile:
|
|
43
55
|
|
|
44
56
|
```
|
|
45
|
-
|
|
57
|
+
gem 'active_model_serializers', '~> 0.10.0'
|
|
46
58
|
```
|
|
47
59
|
|
|
48
|
-
|
|
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::ArraySerializationSupport` into your ORM's
|
|
58
|
-
relation/criteria class.
|
|
59
|
-
|
|
60
|
-
# ActiveModel::Serializer
|
|
60
|
+
And then execute:
|
|
61
61
|
|
|
62
|
-
All new serializers descend from ActiveModel::Serializer
|
|
63
|
-
|
|
64
|
-
# render :json
|
|
65
|
-
|
|
66
|
-
In your controllers, when you use `render :json`, Rails will now first search
|
|
67
|
-
for a serializer for the object and use it if available.
|
|
68
|
-
|
|
69
|
-
```ruby
|
|
70
|
-
class PostsController < ApplicationController
|
|
71
|
-
def show
|
|
72
|
-
@post = Post.find(params[:id])
|
|
73
|
-
render :json => @post
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
62
|
```
|
|
77
|
-
|
|
78
|
-
In this case, Rails will look for a serializer named `PostSerializer`, and if
|
|
79
|
-
it exists, use it to serialize the `Post`.
|
|
80
|
-
|
|
81
|
-
This also works with `respond_with`, which uses `to_json` under the hood. Also
|
|
82
|
-
note that any options passed to `render :json` will be passed to your
|
|
83
|
-
serializer and available as `@options` inside.
|
|
84
|
-
|
|
85
|
-
To specify a custom serializer for an object, there are 2 options:
|
|
86
|
-
|
|
87
|
-
#### 1. Specify the serializer in your model:
|
|
88
|
-
|
|
89
|
-
```ruby
|
|
90
|
-
class Post < ActiveRecord::Base
|
|
91
|
-
def active_model_serializer
|
|
92
|
-
FancyPostSerializer
|
|
93
|
-
end
|
|
94
|
-
end
|
|
63
|
+
$ bundle
|
|
95
64
|
```
|
|
96
65
|
|
|
97
|
-
|
|
66
|
+
## Getting Started
|
|
98
67
|
|
|
99
|
-
|
|
100
|
-
render :json => @post, :serializer => FancyPostSerializer
|
|
101
|
-
```
|
|
68
|
+
See [Getting Started](docs/general/getting_started.md) for the nuts and bolts.
|
|
102
69
|
|
|
103
|
-
|
|
70
|
+
More information is available in the [Guides](docs) and
|
|
71
|
+
[High-level behavior](README.md#high-level-behavior).
|
|
104
72
|
|
|
105
|
-
|
|
106
|
-
use `ActiveModel::ArraySerializer` (included in this project) as the base serializer,
|
|
107
|
-
and the individual `Serializer` for the objects contained in that array.
|
|
73
|
+
## Getting Help
|
|
108
74
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
attributes :title, :body
|
|
112
|
-
end
|
|
75
|
+
If you find a bug, please report an [Issue](https://github.com/rails-api/active_model_serializers/issues/new)
|
|
76
|
+
and see our [contributing guide](CONTRIBUTING.md).
|
|
113
77
|
|
|
114
|
-
|
|
115
|
-
def index
|
|
116
|
-
@posts = Post.all
|
|
117
|
-
render :json => @posts
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
|
-
```
|
|
78
|
+
If you have a question, please [post to Stack Overflow](https://stackoverflow.com/questions/tagged/active-model-serializers).
|
|
121
79
|
|
|
122
|
-
|
|
80
|
+
If you'd like to chat, we have a [community slack](https://amserializers.herokuapp.com).
|
|
123
81
|
|
|
124
|
-
|
|
125
|
-
{
|
|
126
|
-
"posts":
|
|
127
|
-
[
|
|
128
|
-
{ "title": "Post 1", "body": "Hello!" },
|
|
129
|
-
{ "title": "Post 2", "body": "Goodbye!" }
|
|
130
|
-
]
|
|
131
|
-
}
|
|
132
|
-
```
|
|
82
|
+
Thanks!
|
|
133
83
|
|
|
134
|
-
|
|
135
|
-
generates a root element "posts". To change it:
|
|
84
|
+
## Documentation
|
|
136
85
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
86
|
+
If you're reading this at https://github.com/rails-api/active_model_serializers you are
|
|
87
|
+
reading documentation for our `master`, which may include features that have not
|
|
88
|
+
been released yet. Please see below for the documentation relevant to you.
|
|
140
89
|
|
|
141
|
-
|
|
142
|
-
|
|
90
|
+
- [0.10 (0-10-stable) Documentation](https://github.com/rails-api/active_model_serializers/tree/0-10-stable)
|
|
91
|
+
- [0.10.10 (latest release) Documentation](https://github.com/rails-api/active_model_serializers/tree/v0.10.10)
|
|
92
|
+
- [](https://www.rubydoc.info/gems/active_model_serializers/0.10.10)
|
|
93
|
+
- [Guides](docs)
|
|
94
|
+
- [0.9 (0-9-stable) Documentation](https://github.com/rails-api/active_model_serializers/tree/0-9-stable)
|
|
95
|
+
- [](https://www.rubydoc.info/gems/active_model_serializers/0.9.7)
|
|
96
|
+
- [0.8 (0-8-stable) Documentation](https://github.com/rails-api/active_model_serializers/tree/0-8-stable)
|
|
97
|
+
- [](https://www.rubydoc.info/gems/active_model_serializers/0.8.4)
|
|
143
98
|
|
|
144
|
-
#### 1. Disable root globally for in `ArraySerializer`. In an initializer:
|
|
145
99
|
|
|
146
|
-
|
|
147
|
-
ActiveSupport.on_load(:active_model_serializers) do
|
|
148
|
-
ActiveModel::ArraySerializer.root = false
|
|
149
|
-
end
|
|
150
|
-
```
|
|
100
|
+
## High-level behavior
|
|
151
101
|
|
|
152
|
-
|
|
102
|
+
Choose an adapter from [adapters](lib/active_model_serializers/adapter):
|
|
153
103
|
|
|
154
|
-
```ruby
|
|
155
|
-
|
|
104
|
+
``` ruby
|
|
105
|
+
ActiveModelSerializers.config.adapter = :json_api # Default: `:attributes`
|
|
156
106
|
```
|
|
157
107
|
|
|
158
|
-
|
|
108
|
+
Given a [serializable model](lib/active_model/serializer/lint.rb):
|
|
159
109
|
|
|
160
110
|
```ruby
|
|
161
|
-
|
|
162
|
-
|
|
111
|
+
# either
|
|
112
|
+
class SomeResource < ActiveRecord::Base
|
|
113
|
+
# columns: title, body
|
|
163
114
|
end
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
Disabling the root element of the array with any of the above 3 methods
|
|
170
|
-
will produce
|
|
171
|
-
|
|
172
|
-
```json
|
|
173
|
-
[
|
|
174
|
-
{ "title": "Post 1", "body": "Hello!" },
|
|
175
|
-
{ "title": "Post 2", "body": "Goodbye!" }
|
|
176
|
-
]
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
To specify a custom serializer for the items within an array:
|
|
180
|
-
|
|
181
|
-
```ruby
|
|
182
|
-
render :json => @posts, :each_serializer => FancyPostSerializer
|
|
183
|
-
```
|
|
184
|
-
#### 4. Define default_serializer_options in your controller
|
|
185
|
-
|
|
186
|
-
If you define `default_serializer_options` method in your controller,
|
|
187
|
-
all serializers in actions of this controller and it's children will use them.
|
|
188
|
-
One of the options may be `root: false`
|
|
189
|
-
|
|
190
|
-
```ruby
|
|
191
|
-
def default_serializer_options
|
|
192
|
-
{
|
|
193
|
-
root: false
|
|
194
|
-
}
|
|
195
|
-
end
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
## Getting the old version
|
|
199
|
-
|
|
200
|
-
If you find that your project is already relying on the old rails to_json
|
|
201
|
-
change `render :json` to `render :json => @your_object.to_json`.
|
|
202
|
-
|
|
203
|
-
# Attributes and Associations
|
|
204
|
-
|
|
205
|
-
Once you have a serializer, you can specify which attributes and associations
|
|
206
|
-
you would like to include in the serialized form.
|
|
207
|
-
|
|
208
|
-
```ruby
|
|
209
|
-
class PostSerializer < ActiveModel::Serializer
|
|
210
|
-
attributes :id, :title, :body
|
|
211
|
-
has_many :comments
|
|
212
|
-
end
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
## Attributes
|
|
216
|
-
|
|
217
|
-
For specified attributes, a serializer will look up the attribute on the
|
|
218
|
-
object you passed to `render :json`. It uses
|
|
219
|
-
`read_attribute_for_serialization`, which `ActiveRecord` objects implement as a
|
|
220
|
-
regular attribute lookup.
|
|
221
|
-
|
|
222
|
-
Before looking up the attribute on the object, a serializer will check for the
|
|
223
|
-
presence of a method with the name of the attribute. This allows serializers to
|
|
224
|
-
include properties beyond the simple attributes of the model. For example:
|
|
225
|
-
|
|
226
|
-
```ruby
|
|
227
|
-
class PersonSerializer < ActiveModel::Serializer
|
|
228
|
-
attributes :first_name, :last_name, :full_name
|
|
229
|
-
|
|
230
|
-
def full_name
|
|
231
|
-
"#{object.first_name} #{object.last_name}"
|
|
232
|
-
end
|
|
233
|
-
end
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
Within a serializer's methods, you can access the object being
|
|
237
|
-
serialized as `object`.
|
|
238
|
-
|
|
239
|
-
You can also access the `current_user` method, which provides an
|
|
240
|
-
authorization context to your serializer. By default, the context
|
|
241
|
-
is the current user of your application, but this
|
|
242
|
-
[can be customized](#customizing-scope).
|
|
243
|
-
|
|
244
|
-
Serializers will check for the presence of a method named
|
|
245
|
-
`include_[ATTRIBUTE]?` to determine whether a particular attribute should be
|
|
246
|
-
included in the output. This is typically used to customize output
|
|
247
|
-
based on `current_user`. For example:
|
|
248
|
-
|
|
249
|
-
```ruby
|
|
250
|
-
class PostSerializer < ActiveModel::Serializer
|
|
251
|
-
attributes :id, :title, :body, :author
|
|
252
|
-
|
|
253
|
-
def include_author?
|
|
254
|
-
current_user.admin?
|
|
255
|
-
end
|
|
115
|
+
# or
|
|
116
|
+
class SomeResource < ActiveModelSerializers::Model
|
|
117
|
+
attributes :title, :body
|
|
256
118
|
end
|
|
257
119
|
```
|
|
258
120
|
|
|
259
|
-
|
|
260
|
-
calculated without some sophisticated static code analysis. To specify the
|
|
261
|
-
type of a computed attribute:
|
|
121
|
+
And initialized as:
|
|
262
122
|
|
|
263
123
|
```ruby
|
|
264
|
-
|
|
265
|
-
attributes :first_name, :last_name, {:full_name => :string}
|
|
266
|
-
|
|
267
|
-
def full_name
|
|
268
|
-
"#{object.first_name} #{object.last_name}"
|
|
269
|
-
end
|
|
270
|
-
end
|
|
124
|
+
resource = SomeResource.new(title: 'ActiveModelSerializers', body: 'Convention over configuration')
|
|
271
125
|
```
|
|
272
126
|
|
|
273
|
-
|
|
274
|
-
in ActiveRecord, you can use the `:key` option to customize it:
|
|
127
|
+
Given a serializer for the serializable model:
|
|
275
128
|
|
|
276
129
|
```ruby
|
|
277
|
-
class
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
# look up :subject on the model, but use +title+ in the JSON
|
|
281
|
-
attribute :subject, :key => :title
|
|
282
|
-
has_many :comments
|
|
130
|
+
class SomeSerializer < ActiveModel::Serializer
|
|
131
|
+
attribute :title, key: :name
|
|
132
|
+
attributes :body
|
|
283
133
|
end
|
|
284
134
|
```
|
|
285
135
|
|
|
286
|
-
|
|
287
|
-
option:
|
|
136
|
+
The model can be serialized as:
|
|
288
137
|
|
|
289
138
|
```ruby
|
|
290
|
-
|
|
139
|
+
options = {}
|
|
140
|
+
serialization = ActiveModelSerializers::SerializableResource.new(resource, options)
|
|
141
|
+
serialization.to_json
|
|
142
|
+
serialization.as_json
|
|
291
143
|
```
|
|
292
144
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
```json
|
|
296
|
-
{
|
|
297
|
-
"meta": { "total": 10 },
|
|
298
|
-
"posts": [
|
|
299
|
-
{ "title": "Post 1", "body": "Hello!" },
|
|
300
|
-
{ "title": "Post 2", "body": "Goodbye!" }
|
|
301
|
-
]
|
|
302
|
-
}
|
|
303
|
-
```
|
|
304
|
-
|
|
305
|
-
If you would like to change the meta key name you can use the `:meta_key` option:
|
|
145
|
+
SerializableResource delegates to the adapter, which it builds as:
|
|
306
146
|
|
|
307
147
|
```ruby
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
```json
|
|
314
|
-
{
|
|
315
|
-
"meta_object": { "total": 10 },
|
|
316
|
-
"posts": [
|
|
317
|
-
{ "title": "Post 1", "body": "Hello!" },
|
|
318
|
-
{ "title": "Post 2", "body": "Goodbye!" }
|
|
319
|
-
]
|
|
320
|
-
}
|
|
148
|
+
adapter_options = {}
|
|
149
|
+
adapter = ActiveModelSerializers::Adapter.create(serializer, adapter_options)
|
|
150
|
+
adapter.to_json
|
|
151
|
+
adapter.as_json
|
|
152
|
+
adapter.serializable_hash
|
|
321
153
|
```
|
|
322
154
|
|
|
323
|
-
|
|
324
|
-
completely override the `attributes` method to return the hash you need:
|
|
155
|
+
The adapter formats the serializer's attributes and associations (a.k.a. includes):
|
|
325
156
|
|
|
326
157
|
```ruby
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
hash = super
|
|
332
|
-
if current_user.admin?
|
|
333
|
-
hash["ssn"] = object.ssn
|
|
334
|
-
hash["secret"] = object.mothers_maiden_name
|
|
335
|
-
end
|
|
336
|
-
hash
|
|
337
|
-
end
|
|
338
|
-
end
|
|
158
|
+
serializer_options = {}
|
|
159
|
+
serializer = SomeSerializer.new(resource, serializer_options)
|
|
160
|
+
serializer.attributes
|
|
161
|
+
serializer.associations
|
|
339
162
|
```
|
|
340
163
|
|
|
341
|
-
##
|
|
342
|
-
|
|
343
|
-
For specified associations, the serializer will look up the association and
|
|
344
|
-
then serialize each element of the association. For instance, a `has_many
|
|
345
|
-
:comments` association will create a new `CommentSerializer` for each comment
|
|
346
|
-
and use it to serialize the comment.
|
|
347
|
-
|
|
348
|
-
By default, serializers simply look up the association on the original object.
|
|
349
|
-
You can customize this behavior by implementing a method with the name of the
|
|
350
|
-
association and returning a different Array. Often, you will do this to
|
|
351
|
-
customize the objects returned based on the current user.
|
|
164
|
+
## Architecture
|
|
352
165
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
has_many :comments
|
|
357
|
-
|
|
358
|
-
# only let the user see comments he created.
|
|
359
|
-
def comments
|
|
360
|
-
object.comments.where(:created_by => current_user)
|
|
361
|
-
end
|
|
362
|
-
end
|
|
363
|
-
```
|
|
166
|
+
This section focuses on architecture the 0.10.x version of ActiveModelSerializers. If you are interested in the architecture of the 0.8 or 0.9 versions,
|
|
167
|
+
please refer to the [0.8 README](https://github.com/rails-api/active_model_serializers/blob/0-8-stable/README.md) or
|
|
168
|
+
[0.9 README](https://github.com/rails-api/active_model_serializers/blob/0-9-stable/README.md).
|
|
364
169
|
|
|
365
|
-
|
|
366
|
-
use for a particular association.
|
|
170
|
+
The original design is also available [here](https://github.com/rails-api/active_model_serializers/blob/d72b66d4c5355b0ff0a75a04895fcc4ea5b0c65e/README.textile).
|
|
367
171
|
|
|
368
|
-
|
|
369
|
-
class PostSerializer < ActiveModel::Serializer
|
|
370
|
-
attributes :id, :title, :body
|
|
172
|
+
### ActiveModel::Serializer
|
|
371
173
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
174
|
+
An **`ActiveModel::Serializer`** wraps a [serializable resource](https://github.com/rails/rails/blob/master/activemodel/lib/active_model/serialization.rb)
|
|
175
|
+
and exposes an `attributes` method, among a few others.
|
|
176
|
+
It allows you to specify which attributes and associations should be represented in the serializatation of the resource.
|
|
177
|
+
It requires an adapter to transform its attributes into a JSON document; it cannot be serialized itself.
|
|
178
|
+
It may be useful to think of it as a
|
|
179
|
+
[presenter](https://blog.steveklabnik.com/posts/2011-09-09-better-ruby-presenters).
|
|
376
180
|
|
|
377
|
-
|
|
378
|
-
of a method named `include_[ASSOCIATION]?` to determine whether a particular association
|
|
379
|
-
should be included in the output. For example:
|
|
181
|
+
#### ActiveModel::CollectionSerializer
|
|
380
182
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
attributes :id, :title, :body
|
|
384
|
-
has_many :comments
|
|
183
|
+
The **`ActiveModel::CollectionSerializer`** represents a collection of resources as serializers
|
|
184
|
+
and, if there is no serializer, primitives.
|
|
385
185
|
|
|
386
|
-
|
|
387
|
-
!object.comments_disabled?
|
|
388
|
-
end
|
|
389
|
-
end
|
|
390
|
-
```
|
|
186
|
+
### ActiveModelSerializers::Adapter::Base
|
|
391
187
|
|
|
392
|
-
|
|
393
|
-
|
|
188
|
+
The **`ActiveModelSerializers::Adapter::Base`** describes the structure of the JSON document generated from a
|
|
189
|
+
serializer. For example, the `Attributes` example represents each serializer as its
|
|
190
|
+
unmodified attributes. The `JsonApi` adapter represents the serializer as a [JSON
|
|
191
|
+
API](https://jsonapi.org/) document.
|
|
394
192
|
|
|
395
|
-
|
|
396
|
-
class PostSerializer < ActiveModel::Serializer
|
|
397
|
-
attributes :id, :title, :body
|
|
398
|
-
has_one :author
|
|
399
|
-
has_many :comments
|
|
400
|
-
|
|
401
|
-
def include_associations!
|
|
402
|
-
include! :author if current_user.admin?
|
|
403
|
-
include! :comments unless object.comments_disabled?
|
|
404
|
-
end
|
|
405
|
-
end
|
|
406
|
-
```
|
|
407
|
-
|
|
408
|
-
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.:
|
|
193
|
+
### ActiveModelSerializers::SerializableResource
|
|
409
194
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
195
|
+
The **`ActiveModelSerializers::SerializableResource`** acts to coordinate the serializer(s) and adapter
|
|
196
|
+
to an object that responds to `to_json`, and `as_json`. It is used in the controller to
|
|
197
|
+
encapsulate the serialization resource when rendered. However, it can also be used on its own
|
|
198
|
+
to serialize a resource outside of a controller, as well.
|
|
414
199
|
|
|
415
|
-
|
|
200
|
+
### Primitive handling
|
|
416
201
|
|
|
417
|
-
|
|
202
|
+
Definitions: A primitive is usually a String or Array. There is no serializer
|
|
203
|
+
defined for them; they will be serialized when the resource is converted to JSON (`as_json` or
|
|
204
|
+
`to_json`). (The below also applies for any object with no serializer.)
|
|
418
205
|
|
|
419
|
-
|
|
420
|
-
you have a post, the outputted JSON will look like:
|
|
206
|
+
- ActiveModelSerializers doesn't handle primitives passed to `render json:` at all.
|
|
421
207
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
"post": {
|
|
425
|
-
"id": 1,
|
|
426
|
-
"title": "New post",
|
|
427
|
-
"body": "A body!",
|
|
428
|
-
"comments": [
|
|
429
|
-
{ "id": 1, "body": "what a dumb post" }
|
|
430
|
-
]
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
```
|
|
208
|
+
Internally, if no serializer can be found in the controller, the resource is not decorated by
|
|
209
|
+
ActiveModelSerializers.
|
|
434
210
|
|
|
435
|
-
|
|
436
|
-
better to supply an Array of IDs for the association. This makes your API more
|
|
437
|
-
flexible from a performance standpoint and avoids wasteful duplication.
|
|
211
|
+
- However, when a primitive value is an attribute or in a collection, it is not modified.
|
|
438
212
|
|
|
439
|
-
|
|
213
|
+
When serializing a collection and the collection serializer (CollectionSerializer) cannot
|
|
214
|
+
identify a serializer for a resource in its collection, it throws [`:no_serializer`](https://github.com/rails-api/active_model_serializers/issues/1191#issuecomment-142327128).
|
|
215
|
+
For example, when caught by `Reflection#build_association`, and the association value is set directly:
|
|
440
216
|
|
|
441
217
|
```ruby
|
|
442
|
-
|
|
443
|
-
embed :ids
|
|
444
|
-
|
|
445
|
-
attributes :id, :title, :body
|
|
446
|
-
has_many :comments
|
|
447
|
-
end
|
|
218
|
+
reflection_options[:virtual_value] = association_value.try(:as_json) || association_value
|
|
448
219
|
```
|
|
449
220
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
```json
|
|
453
|
-
{
|
|
454
|
-
"post": {
|
|
455
|
-
"id": 1,
|
|
456
|
-
"title": "New post",
|
|
457
|
-
"body": "A body!",
|
|
458
|
-
"comment_ids": [ 1, 2, 3 ]
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
```
|
|
221
|
+
(which is called by the adapter as `serializer.associations(*)`.)
|
|
462
222
|
|
|
463
|
-
|
|
223
|
+
### How options are parsed
|
|
464
224
|
|
|
465
|
-
|
|
466
|
-
class PostSerializer < ActiveModel::Serializer
|
|
467
|
-
attributes :id, :title, :body
|
|
225
|
+
High-level overview:
|
|
468
226
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
227
|
+
- For a **collection**
|
|
228
|
+
- `:serializer` specifies the collection serializer and
|
|
229
|
+
- `:each_serializer` specifies the serializer for each resource in the collection.
|
|
230
|
+
- For a **single resource**, the `:serializer` option is the resource serializer.
|
|
231
|
+
- Options are partitioned in serializer options and adapter options. Keys for adapter options are specified by
|
|
232
|
+
[`ADAPTER_OPTION_KEYS`](lib/active_model_serializers/serializable_resource.rb#L5).
|
|
233
|
+
The remaining options are serializer options.
|
|
473
234
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
```json
|
|
477
|
-
{
|
|
478
|
-
"post": {
|
|
479
|
-
"id": 1,
|
|
480
|
-
"title": "New post",
|
|
481
|
-
"body": "A body!",
|
|
482
|
-
"comments": [
|
|
483
|
-
{ "id": 1, "body": "what a dumb post" }
|
|
484
|
-
],
|
|
485
|
-
"tag_ids": [ 1, 2, 3 ]
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
```
|
|
235
|
+
Details:
|
|
489
236
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
237
|
+
1. **ActionController::Serialization**
|
|
238
|
+
1. `serializable_resource = ActiveModelSerializers::SerializableResource.new(resource, options)`
|
|
239
|
+
1. `options` are partitioned into `adapter_opts` and everything else (`serializer_opts`).
|
|
240
|
+
The `adapter_opts` keys are defined in [`ActiveModelSerializers::SerializableResource::ADAPTER_OPTION_KEYS`](lib/active_model_serializers/serializable_resource.rb#L5).
|
|
241
|
+
1. **ActiveModelSerializers::SerializableResource**
|
|
242
|
+
1. `if serializable_resource.serializer?` (there is a serializer for the resource, and an adapter is used.)
|
|
243
|
+
- Where `serializer?` is `use_adapter? && !!(serializer)`
|
|
244
|
+
- Where `use_adapter?`: 'True when no explicit adapter given, or explicit value is truthy (non-nil);
|
|
245
|
+
False when explicit adapter is falsy (nil or false)'
|
|
246
|
+
- Where `serializer`:
|
|
247
|
+
1. from explicit `:serializer` option, else
|
|
248
|
+
2. implicitly from resource `ActiveModel::Serializer.serializer_for(resource)`
|
|
249
|
+
1. A side-effect of checking `serializer` is:
|
|
250
|
+
- The `:serializer` option is removed from the serializer_opts hash
|
|
251
|
+
- If the `:each_serializer` option is present, it is removed from the serializer_opts hash and set as the `:serializer` option
|
|
252
|
+
1. The serializer and adapter are created as
|
|
253
|
+
1. `serializer_instance = serializer.new(resource, serializer_opts)`
|
|
254
|
+
2. `adapter_instance = ActiveModel::Serializer::Adapter.create(serializer_instance, adapter_opts)`
|
|
255
|
+
1. **ActiveModel::Serializer::CollectionSerializer#new**
|
|
256
|
+
1. If the `serializer_instance` was a `CollectionSerializer` and the `:serializer` serializer_opts
|
|
257
|
+
is present, then [that serializer is passed into each resource](https://github.com/rails-api/active_model_serializers/blob/0-10-stable/lib/active_model/serializer/collection_serializer.rb#L77-L79).
|
|
258
|
+
1. **ActiveModel::Serializer#attributes** is used by the adapter to get the attributes for
|
|
259
|
+
resource as defined by the serializer.
|
|
495
260
|
|
|
496
|
-
|
|
261
|
+
(In Rails, the `options` are also passed to the `as_json(options)` or `to_json(options)`
|
|
262
|
+
methods on the resource serialization by the Rails JSON renderer. They are, therefore, important
|
|
263
|
+
to know about, but not part of ActiveModelSerializers.)
|
|
497
264
|
|
|
498
|
-
|
|
499
|
-
class PostSerializer < ActiveModel::Serializer
|
|
500
|
-
embed :ids, :include => true
|
|
265
|
+
### What does a 'serializable resource' look like?
|
|
501
266
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
267
|
+
- An `ActiveRecord::Base` object.
|
|
268
|
+
- Any Ruby object that passes the
|
|
269
|
+
[Lint](https://www.rubydoc.info/gems/active_model_serializers/ActiveModel/Serializer/Lint/Tests)
|
|
270
|
+
[(code)](lib/active_model/serializer/lint.rb).
|
|
506
271
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
```json
|
|
511
|
-
{
|
|
512
|
-
"post": {
|
|
513
|
-
"id": 1,
|
|
514
|
-
"title": "New post",
|
|
515
|
-
"body": "A body!",
|
|
516
|
-
"comment_ids": [ 1, 2 ]
|
|
517
|
-
},
|
|
518
|
-
"comments": [
|
|
519
|
-
{ "id": 1, "body": "what a dumb post", "tag_ids": [ 1, 2 ] },
|
|
520
|
-
{ "id": 2, "body": "i liked it", "tag_ids": [ 1, 3 ] },
|
|
521
|
-
],
|
|
522
|
-
"tags": [
|
|
523
|
-
{ "id": 1, "name": "short" },
|
|
524
|
-
{ "id": 2, "name": "whiny" },
|
|
525
|
-
{ "id": 3, "name": "happy" }
|
|
526
|
-
]
|
|
527
|
-
}
|
|
528
|
-
```
|
|
272
|
+
ActiveModelSerializers provides a
|
|
273
|
+
[`ActiveModelSerializers::Model`](lib/active_model_serializers/model.rb),
|
|
274
|
+
which is a simple serializable PORO (Plain-Old Ruby Object).
|
|
529
275
|
|
|
530
|
-
|
|
531
|
-
used to reference them:
|
|
276
|
+
`ActiveModelSerializers::Model` may be used either as a reference implementation, or in production code.
|
|
532
277
|
|
|
533
278
|
```ruby
|
|
534
|
-
class
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
attributes :id, :title, :body
|
|
538
|
-
has_many :comments, :key => :comment_ids, :root => :comment_objects
|
|
279
|
+
class MyModel < ActiveModelSerializers::Model
|
|
280
|
+
attributes :id, :name, :level
|
|
539
281
|
end
|
|
540
282
|
```
|
|
541
283
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
```json
|
|
545
|
-
{
|
|
546
|
-
"post": {
|
|
547
|
-
"id": 1,
|
|
548
|
-
"title": "New post",
|
|
549
|
-
"body": "A body!",
|
|
550
|
-
"comment_ids": [ 1 ]
|
|
551
|
-
},
|
|
552
|
-
"comment_objects": [
|
|
553
|
-
{ "id": 1, "body": "what a dumb post" }
|
|
554
|
-
]
|
|
555
|
-
}
|
|
556
|
-
```
|
|
284
|
+
The default serializer for `MyModel` would be `MyModelSerializer` whether MyModel is an
|
|
285
|
+
ActiveRecord::Base object or not.
|
|
557
286
|
|
|
558
|
-
|
|
559
|
-
objects:
|
|
287
|
+
Outside of the controller the rules are **exactly** the same as for records. For example:
|
|
560
288
|
|
|
561
289
|
```ruby
|
|
562
|
-
|
|
563
|
-
embed :ids, :include => true
|
|
564
|
-
|
|
565
|
-
attributes :id, :title, :body
|
|
566
|
-
has_many :comments, :embed_key => :external_id
|
|
567
|
-
end
|
|
568
|
-
```
|
|
569
|
-
|
|
570
|
-
This would generate JSON that would look like this:
|
|
571
|
-
|
|
572
|
-
```json
|
|
573
|
-
{
|
|
574
|
-
"post": {
|
|
575
|
-
"id": 1,
|
|
576
|
-
"title": "New post",
|
|
577
|
-
"body": "A body!",
|
|
578
|
-
"comment_ids": [ "COMM001" ]
|
|
579
|
-
},
|
|
580
|
-
"comments": [
|
|
581
|
-
{ "id": 1, "external_id": "COMM001", "body": "what a dumb post" }
|
|
582
|
-
]
|
|
583
|
-
}
|
|
290
|
+
render json: MyModel.new(level: 'awesome'), adapter: :json
|
|
584
291
|
```
|
|
585
292
|
|
|
586
|
-
|
|
587
|
-
data in bulk and load it into a local store. For these clients, the ability to
|
|
588
|
-
easily see all of the data per type, rather than having to recursively scan the
|
|
589
|
-
data looking for information, is extremely useful.
|
|
590
|
-
|
|
591
|
-
If you are mostly working with the data in simple scenarios and manually making
|
|
592
|
-
Ajax requests, you probably just want to use the default embedded behavior.
|
|
593
|
-
|
|
594
|
-
## Customizing Scope
|
|
595
|
-
|
|
596
|
-
In a serializer, `current_user` is the current authorization scope which the controller
|
|
597
|
-
provides to the serializer when you call `render :json`. By default, this is
|
|
598
|
-
`current_user`, but can be customized in your controller by calling
|
|
599
|
-
`serialization_scope`:
|
|
600
|
-
|
|
601
|
-
```ruby
|
|
602
|
-
class ApplicationController < ActionController::Base
|
|
603
|
-
serialization_scope :current_admin
|
|
604
|
-
end
|
|
605
|
-
```
|
|
606
|
-
|
|
607
|
-
The above example will also change the scope name from `current_user` to
|
|
608
|
-
`current_admin`.
|
|
609
|
-
|
|
610
|
-
Please note that, until now, `serialization_scope` doesn't accept a second
|
|
611
|
-
object with options for specifying which actions should or should not take a
|
|
612
|
-
given scope in consideration.
|
|
613
|
-
|
|
614
|
-
To be clear, it's not possible, yet, to do something like this:
|
|
293
|
+
would be serialized the same as
|
|
615
294
|
|
|
616
295
|
```ruby
|
|
617
|
-
|
|
618
|
-
serialization_scope :current_admin, :except => [:index, :show]
|
|
619
|
-
end
|
|
296
|
+
ActiveModelSerializers::SerializableResource.new(MyModel.new(level: 'awesome'), adapter: :json).as_json
|
|
620
297
|
```
|
|
621
298
|
|
|
622
|
-
|
|
623
|
-
consideration for its scope, you may use something like this:
|
|
624
|
-
|
|
625
|
-
```ruby
|
|
626
|
-
class CitiesController < ApplicationController
|
|
627
|
-
serialization_scope nil
|
|
628
|
-
|
|
629
|
-
def index
|
|
630
|
-
@cities = City.all
|
|
631
|
-
|
|
632
|
-
render :json => @cities, :each_serializer => CitySerializer
|
|
633
|
-
end
|
|
299
|
+
## Semantic Versioning
|
|
634
300
|
|
|
635
|
-
|
|
636
|
-
@city = City.find(params[:id])
|
|
301
|
+
This project adheres to [semver](https://semver.org/)
|
|
637
302
|
|
|
638
|
-
|
|
639
|
-
end
|
|
640
|
-
end
|
|
641
|
-
```
|
|
303
|
+
## Contributing
|
|
642
304
|
|
|
643
|
-
|
|
644
|
-
for the current user, the advantage of this approach is that, by setting
|
|
645
|
-
`serialization_scope` to `nil`, the `index` action no longer will need to make
|
|
646
|
-
that query, only the `show` action will.
|
|
305
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md)
|