active_model_serializers 0.10.0 → 0.10.6
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/.rubocop.yml +6 -5
- data/.travis.yml +17 -5
- data/CHANGELOG.md +126 -2
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +5 -2
- data/README.md +166 -26
- data/Rakefile +3 -32
- data/active_model_serializers.gemspec +22 -25
- data/appveyor.yml +9 -3
- data/bin/rubocop +38 -0
- data/docs/README.md +2 -1
- data/docs/general/adapters.md +29 -11
- data/docs/general/caching.md +7 -1
- data/docs/general/configuration_options.md +70 -1
- data/docs/general/deserialization.md +1 -1
- data/docs/general/fields.md +31 -0
- data/docs/general/getting_started.md +1 -1
- data/docs/general/logging.md +7 -0
- data/docs/general/rendering.md +62 -24
- data/docs/general/serializers.md +121 -13
- data/docs/howto/add_pagination_links.md +16 -17
- data/docs/howto/add_relationship_links.md +140 -0
- data/docs/howto/add_root_key.md +4 -0
- data/docs/howto/grape_integration.md +42 -0
- data/docs/howto/outside_controller_use.md +12 -4
- data/docs/howto/passing_arbitrary_options.md +2 -2
- data/docs/howto/serialize_poro.md +46 -5
- data/docs/howto/test.md +2 -0
- data/docs/howto/upgrade_from_0_8_to_0_10.md +265 -0
- data/docs/integrations/ember-and-json-api.md +67 -32
- data/docs/jsonapi/schema.md +1 -1
- data/lib/action_controller/serialization.rb +13 -3
- data/lib/active_model/serializer/adapter/base.rb +2 -0
- data/lib/active_model/serializer/array_serializer.rb +8 -5
- data/lib/active_model/serializer/association.rb +62 -10
- data/lib/active_model/serializer/belongs_to_reflection.rb +4 -3
- data/lib/active_model/serializer/collection_serializer.rb +35 -12
- data/lib/active_model/serializer/{caching.rb → concerns/caching.rb} +82 -115
- data/lib/active_model/serializer/error_serializer.rb +11 -7
- data/lib/active_model/serializer/errors_serializer.rb +25 -20
- data/lib/active_model/serializer/has_many_reflection.rb +3 -3
- data/lib/active_model/serializer/has_one_reflection.rb +1 -4
- data/lib/active_model/serializer/lazy_association.rb +95 -0
- data/lib/active_model/serializer/lint.rb +134 -130
- data/lib/active_model/serializer/reflection.rb +127 -67
- data/lib/active_model/serializer/version.rb +1 -1
- data/lib/active_model/serializer.rb +296 -79
- data/lib/active_model_serializers/adapter/attributes.rb +3 -66
- data/lib/active_model_serializers/adapter/base.rb +39 -39
- data/lib/active_model_serializers/adapter/json_api/deserialization.rb +1 -1
- data/lib/active_model_serializers/adapter/json_api/link.rb +1 -1
- data/lib/active_model_serializers/adapter/json_api/pagination_links.rb +8 -1
- data/lib/active_model_serializers/adapter/json_api/relationship.rb +63 -23
- data/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +32 -9
- data/lib/active_model_serializers/adapter/json_api.rb +71 -57
- data/lib/active_model_serializers/adapter.rb +6 -0
- data/lib/active_model_serializers/deprecate.rb +1 -2
- data/lib/active_model_serializers/deserialization.rb +2 -0
- data/lib/active_model_serializers/lookup_chain.rb +80 -0
- data/lib/active_model_serializers/model.rb +109 -28
- data/lib/active_model_serializers/railtie.rb +3 -1
- data/lib/active_model_serializers/register_jsonapi_renderer.rb +44 -31
- data/lib/active_model_serializers/serializable_resource.rb +6 -5
- data/lib/active_model_serializers/serialization_context.rb +10 -3
- data/lib/active_model_serializers/test/schema.rb +2 -2
- data/lib/active_model_serializers.rb +15 -0
- data/lib/generators/rails/resource_override.rb +1 -1
- data/lib/generators/rails/serializer_generator.rb +4 -4
- data/lib/grape/active_model_serializers.rb +7 -5
- data/lib/grape/formatters/active_model_serializers.rb +19 -2
- data/lib/grape/helpers/active_model_serializers.rb +1 -0
- data/lib/tasks/rubocop.rake +53 -0
- data/test/action_controller/adapter_selector_test.rb +14 -5
- data/test/action_controller/explicit_serializer_test.rb +5 -4
- data/test/action_controller/json/include_test.rb +106 -27
- data/test/action_controller/json_api/errors_test.rb +8 -9
- data/test/action_controller/json_api/fields_test.rb +66 -0
- data/test/action_controller/json_api/linked_test.rb +29 -24
- data/test/action_controller/json_api/pagination_test.rb +19 -19
- data/test/action_controller/json_api/transform_test.rb +11 -3
- data/test/action_controller/lookup_proc_test.rb +49 -0
- data/test/action_controller/namespace_lookup_test.rb +232 -0
- data/test/action_controller/serialization_scope_name_test.rb +12 -6
- data/test/action_controller/serialization_test.rb +12 -9
- data/test/active_model_serializers/json_pointer_test.rb +15 -13
- data/test/active_model_serializers/model_test.rb +137 -4
- data/test/active_model_serializers/railtie_test_isolated.rb +12 -7
- data/test/active_model_serializers/register_jsonapi_renderer_test_isolated.rb +161 -0
- data/test/active_model_serializers/serialization_context_test_isolated.rb +23 -10
- data/test/active_model_serializers/test/schema_test.rb +3 -2
- data/test/adapter/attributes_test.rb +40 -0
- data/test/adapter/json/collection_test.rb +14 -0
- data/test/adapter/json/has_many_test.rb +10 -2
- data/test/adapter/json/transform_test.rb +15 -15
- data/test/adapter/json_api/collection_test.rb +4 -3
- data/test/adapter/json_api/errors_test.rb +17 -19
- data/test/adapter/json_api/fields_test.rb +12 -3
- data/test/adapter/json_api/has_many_test.rb +49 -20
- data/test/adapter/json_api/include_data_if_sideloaded_test.rb +183 -0
- data/test/adapter/json_api/json_api_test.rb +5 -7
- data/test/adapter/json_api/linked_test.rb +33 -12
- data/test/adapter/json_api/links_test.rb +4 -2
- data/test/adapter/json_api/pagination_links_test.rb +35 -8
- data/test/adapter/json_api/relationship_test.rb +309 -73
- data/test/adapter/json_api/resource_identifier_test.rb +27 -2
- data/test/adapter/json_api/resource_meta_test.rb +3 -3
- data/test/adapter/json_api/transform_test.rb +263 -253
- data/test/adapter/json_api/type_test.rb +1 -1
- data/test/adapter/json_test.rb +8 -7
- data/test/adapter/null_test.rb +1 -2
- data/test/adapter/polymorphic_test.rb +5 -5
- data/test/adapter_test.rb +1 -1
- data/test/benchmark/app.rb +1 -1
- data/test/benchmark/benchmarking_support.rb +1 -1
- data/test/benchmark/bm_active_record.rb +81 -0
- data/test/benchmark/bm_adapter.rb +38 -0
- data/test/benchmark/bm_caching.rb +16 -16
- data/test/benchmark/bm_lookup_chain.rb +83 -0
- data/test/benchmark/bm_transform.rb +21 -10
- data/test/benchmark/controllers.rb +16 -17
- data/test/benchmark/fixtures.rb +72 -72
- data/test/cache_test.rb +235 -69
- data/test/collection_serializer_test.rb +25 -12
- data/test/fixtures/active_record.rb +45 -10
- data/test/fixtures/poro.rb +124 -181
- data/test/generators/serializer_generator_test.rb +23 -5
- data/test/grape_test.rb +170 -56
- data/test/lint_test.rb +1 -1
- data/test/logger_test.rb +13 -11
- data/test/serializable_resource_test.rb +18 -22
- data/test/serializers/association_macros_test.rb +3 -2
- data/test/serializers/associations_test.rb +178 -49
- data/test/serializers/attribute_test.rb +5 -3
- data/test/serializers/attributes_test.rb +1 -1
- data/test/serializers/caching_configuration_test_isolated.rb +6 -6
- data/test/serializers/fieldset_test.rb +1 -1
- data/test/serializers/meta_test.rb +12 -6
- data/test/serializers/options_test.rb +17 -6
- data/test/serializers/read_attribute_for_serialization_test.rb +3 -3
- data/test/serializers/reflection_test.rb +427 -0
- data/test/serializers/root_test.rb +1 -1
- data/test/serializers/serialization_test.rb +2 -2
- data/test/serializers/serializer_for_test.rb +12 -10
- data/test/serializers/serializer_for_with_namespace_test.rb +88 -0
- data/test/support/isolated_unit.rb +5 -2
- data/test/support/rails5_shims.rb +8 -2
- data/test/support/rails_app.rb +2 -9
- data/test/support/serialization_testing.rb +23 -5
- data/test/test_helper.rb +13 -0
- metadata +105 -42
- data/.rubocop_todo.yml +0 -167
- data/docs/ARCHITECTURE.md +0 -126
- data/lib/active_model/serializer/associations.rb +0 -100
- data/lib/active_model/serializer/attributes.rb +0 -82
- data/lib/active_model/serializer/collection_reflection.rb +0 -7
- data/lib/active_model/serializer/configuration.rb +0 -35
- data/lib/active_model/serializer/include_tree.rb +0 -111
- data/lib/active_model/serializer/links.rb +0 -35
- data/lib/active_model/serializer/meta.rb +0 -29
- data/lib/active_model/serializer/singular_reflection.rb +0 -7
- data/lib/active_model/serializer/type.rb +0 -25
- data/lib/active_model_serializers/key_transform.rb +0 -70
- data/test/active_model_serializers/key_transform_test.rb +0 -263
- data/test/adapter/json_api/relationships_test.rb +0 -199
- data/test/include_tree/from_include_args_test.rb +0 -26
- data/test/include_tree/from_string_test.rb +0 -94
- data/test/include_tree/include_args_to_hash_test.rb +0 -64
data/docs/general/serializers.md
CHANGED
|
@@ -31,10 +31,21 @@ Serialization of the resource `title`
|
|
|
31
31
|
|---------------------------- |-------------|
|
|
32
32
|
| `attribute :title` | `{ title: 'Some Title' } `
|
|
33
33
|
| `attribute :title, key: :name` | `{ name: 'Some Title' } `
|
|
34
|
-
| `attribute
|
|
34
|
+
| `attribute(:title) { 'A Different Title'}` | `{ title: 'A Different Title' } `
|
|
35
35
|
| `attribute :title`<br>`def title 'A Different Title' end` | `{ title: 'A Different Title' }`
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
An `if` or `unless` option can make an attribute conditional. It takes a symbol of a method name on the serializer, or a lambda literal.
|
|
38
|
+
|
|
39
|
+
e.g.
|
|
40
|
+
|
|
41
|
+
```ruby
|
|
42
|
+
attribute :private_data, if: :is_current_user?
|
|
43
|
+
attribute :another_private_data, if: -> { scope.admin? }
|
|
44
|
+
|
|
45
|
+
def is_current_user?
|
|
46
|
+
object.id == current_user.id
|
|
47
|
+
end
|
|
48
|
+
```
|
|
38
49
|
|
|
39
50
|
### Associations
|
|
40
51
|
|
|
@@ -53,6 +64,10 @@ Where:
|
|
|
53
64
|
- `unless:`
|
|
54
65
|
- `virtual_value:`
|
|
55
66
|
- `polymorphic:` defines if polymorphic relation type should be nested in serialized association.
|
|
67
|
+
- `type:` the resource type as used by JSON:API, especially on a `belongs_to` relationship.
|
|
68
|
+
- `class_name:` used to determine `type` when `type` not given
|
|
69
|
+
- `foreign_key:` used by JSON:API on a `belongs_to` relationship to avoid unnecessarily loading the association object.
|
|
70
|
+
- `namespace:` used when looking up the serializer and `serializer` is not given. Falls back to the parent serializer's `:namespace` instance options, which, when present, comes from the render options. See [Rendering#namespace](rendering.md#namespace] for more details.
|
|
56
71
|
- optional: `&block` is a context that returns the association's attributes.
|
|
57
72
|
- prevents `association_name` method from being called.
|
|
58
73
|
- return value of block is used as the association value.
|
|
@@ -129,7 +144,7 @@ class PictureSerializer < ActiveModel::Serializer
|
|
|
129
144
|
end
|
|
130
145
|
```
|
|
131
146
|
|
|
132
|
-
For more context, see the [tests](../../test/adapter/polymorphic_test.rb) for each adapter.
|
|
147
|
+
You can specify the serializers by [overriding serializer_for](serializers.md#overriding-association-serializer-lookup). For more context about polymorphic relationships, see the [tests](../../test/adapter/polymorphic_test.rb) for each adapter.
|
|
133
148
|
|
|
134
149
|
### Caching
|
|
135
150
|
|
|
@@ -162,18 +177,25 @@ end
|
|
|
162
177
|
|
|
163
178
|
#### ::type
|
|
164
179
|
|
|
165
|
-
|
|
180
|
+
When using the `:json_api` adapter, the `::type` method defines the JSONAPI [type](http://jsonapi.org/format/#document-resource-object-identification) that will be rendered for this serializer.
|
|
181
|
+
|
|
182
|
+
When using the `:json` adapter, the `::type` method defines the name of the root element.
|
|
183
|
+
|
|
166
184
|
It either takes a `String` or `Symbol` as parameter.
|
|
167
185
|
|
|
168
|
-
Note: This method is useful only when using the `:json_api` adapter.
|
|
186
|
+
Note: This method is useful only when using the `:json_api` or `:json` adapter.
|
|
169
187
|
|
|
170
188
|
Examples:
|
|
171
189
|
```ruby
|
|
172
190
|
class UserProfileSerializer < ActiveModel::Serializer
|
|
173
191
|
type 'profile'
|
|
192
|
+
|
|
193
|
+
attribute :name
|
|
174
194
|
end
|
|
175
195
|
class AuthorProfileSerializer < ActiveModel::Serializer
|
|
176
196
|
type :profile
|
|
197
|
+
|
|
198
|
+
attribute :name
|
|
177
199
|
end
|
|
178
200
|
```
|
|
179
201
|
|
|
@@ -183,7 +205,20 @@ With the `:json_api` adapter, the previous serializers would be rendered as:
|
|
|
183
205
|
{
|
|
184
206
|
"data": {
|
|
185
207
|
"id": "1",
|
|
186
|
-
"type": "profile"
|
|
208
|
+
"type": "profile",
|
|
209
|
+
"attributes": {
|
|
210
|
+
"name": "Julia"
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
With the `:json` adapter, the previous serializer would be rendered as:
|
|
217
|
+
|
|
218
|
+
``` json
|
|
219
|
+
{
|
|
220
|
+
"profile": {
|
|
221
|
+
"name": "Julia"
|
|
187
222
|
}
|
|
188
223
|
}
|
|
189
224
|
```
|
|
@@ -194,10 +229,10 @@ With the `:json_api` adapter, the previous serializers would be rendered as:
|
|
|
194
229
|
link :self do
|
|
195
230
|
href "https://example.com/link_author/#{object.id}"
|
|
196
231
|
end
|
|
197
|
-
link
|
|
198
|
-
link
|
|
232
|
+
link(:author) { link_author_url(object) }
|
|
233
|
+
link(:link_authors) { link_authors_url }
|
|
199
234
|
link :other, 'https://example.com/resource'
|
|
200
|
-
link
|
|
235
|
+
link(:posts) { link_author_posts_url(object) }
|
|
201
236
|
```
|
|
202
237
|
|
|
203
238
|
#### #object
|
|
@@ -206,7 +241,17 @@ The object being serialized.
|
|
|
206
241
|
|
|
207
242
|
#### #root
|
|
208
243
|
|
|
209
|
-
|
|
244
|
+
Resource root which is included in `JSON` adapter. As you can see at [Adapters Document](adapters.md), `Attribute` adapter (default) and `JSON API` adapter does not include root at top level.
|
|
245
|
+
By default, the resource root comes from the `model_name` of the serialized object's class.
|
|
246
|
+
|
|
247
|
+
There are several ways to specify root:
|
|
248
|
+
* [Overriding the root key](rendering.md#overriding-the-root-key)
|
|
249
|
+
* [Setting `type`](serializers.md#type)
|
|
250
|
+
* Specifying the `root` option, e.g. `root: 'specific_name'`, during the serializer's initialization:
|
|
251
|
+
|
|
252
|
+
```ruby
|
|
253
|
+
ActiveModelSerializers::SerializableResource.new(foo, root: 'bar')
|
|
254
|
+
```
|
|
210
255
|
|
|
211
256
|
#### #scope
|
|
212
257
|
|
|
@@ -255,7 +300,7 @@ In the controller, the scope/scope_name options are equal to
|
|
|
255
300
|
the [`serialization_scope`method](https://github.com/rails-api/active_model_serializers/blob/d02cd30fe55a3ea85e1d351b6e039620903c1871/lib/action_controller/serialization.rb#L13-L20),
|
|
256
301
|
which is `:current_user`, by default.
|
|
257
302
|
|
|
258
|
-
|
|
303
|
+
Specifically, the `scope_name` is defaulted to `:current_user`, and may be set as
|
|
259
304
|
`serialization_scope :view_context`. The `scope` is set to `send(scope_name)` when `scope_name` is
|
|
260
305
|
present and the controller responds to `scope_name`.
|
|
261
306
|
|
|
@@ -303,17 +348,64 @@ So that when we render the `#edit` action, we'll get
|
|
|
303
348
|
|
|
304
349
|
Where `can_edit` is `view_context.current_user.admin?` (true).
|
|
305
350
|
|
|
351
|
+
You can also tell what to set as `serialization_scope` for specific actions.
|
|
352
|
+
|
|
353
|
+
For example, use `admin_user` only for `Admin::PostSerializer` and `current_user` for rest.
|
|
354
|
+
|
|
355
|
+
```ruby
|
|
356
|
+
class PostsController < ActionController::Base
|
|
357
|
+
|
|
358
|
+
before_action only: :edit do
|
|
359
|
+
self.class.serialization_scope :admin_user
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
def show
|
|
363
|
+
render json: @post, serializer: PostSerializer
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
def edit
|
|
367
|
+
@post.save
|
|
368
|
+
render json: @post, serializer: Admin::PostSerializer
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
private
|
|
372
|
+
|
|
373
|
+
def admin_user
|
|
374
|
+
User.new(id: 2, name: 'Bob', admin: true)
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
def current_user
|
|
378
|
+
User.new(id: 2, name: 'Bob', admin: false)
|
|
379
|
+
end
|
|
380
|
+
end
|
|
381
|
+
```
|
|
382
|
+
|
|
306
383
|
#### #read_attribute_for_serialization(key)
|
|
307
384
|
|
|
308
385
|
The serialized value for a given key. e.g. `read_attribute_for_serialization(:title) #=> 'Hello World'`
|
|
309
386
|
|
|
310
387
|
#### #links
|
|
311
388
|
|
|
312
|
-
|
|
389
|
+
Allows you to modify the `links` node. By default, this node will be populated with the attributes set using the [::link](#link) method. Using `links: nil` will remove the `links` node.
|
|
390
|
+
|
|
391
|
+
```ruby
|
|
392
|
+
ActiveModelSerializers::SerializableResource.new(
|
|
393
|
+
@post,
|
|
394
|
+
adapter: :json_api,
|
|
395
|
+
links: {
|
|
396
|
+
self: {
|
|
397
|
+
href: 'http://example.com/posts',
|
|
398
|
+
meta: {
|
|
399
|
+
stuff: 'value'
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
)
|
|
404
|
+
```
|
|
313
405
|
|
|
314
406
|
#### #json_key
|
|
315
407
|
|
|
316
|
-
|
|
408
|
+
Returns the key used by the adapter as the resource root. See [root](#root) for more information.
|
|
317
409
|
|
|
318
410
|
## Examples
|
|
319
411
|
|
|
@@ -370,3 +462,19 @@ class PostSerializer < ActiveModel::Serializer
|
|
|
370
462
|
end
|
|
371
463
|
end
|
|
372
464
|
```
|
|
465
|
+
|
|
466
|
+
## Overriding association serializer lookup
|
|
467
|
+
|
|
468
|
+
If you want to define a specific serializer lookup for your associations, you can override
|
|
469
|
+
the `ActiveModel::Serializer.serializer_for` method to return a serializer class based on defined conditions.
|
|
470
|
+
|
|
471
|
+
```ruby
|
|
472
|
+
class MySerializer < ActiveModel::Serializer
|
|
473
|
+
def self.serializer_for(model, options)
|
|
474
|
+
return SparseAdminSerializer if model.class == 'Admin'
|
|
475
|
+
super
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
# the rest of the serializer
|
|
479
|
+
end
|
|
480
|
+
```
|
|
@@ -10,9 +10,9 @@ the resource is paginated and if you are using the ```JsonApi``` adapter.
|
|
|
10
10
|
If you want pagination links in your response, use [Kaminari](https://github.com/amatsuda/kaminari)
|
|
11
11
|
or [WillPaginate](https://github.com/mislav/will_paginate).
|
|
12
12
|
|
|
13
|
-
Although the
|
|
13
|
+
Although the other adapters do not have this feature, it is possible to
|
|
14
14
|
implement pagination links to `JSON` adapter. For more information about it,
|
|
15
|
-
please
|
|
15
|
+
please check our docs.
|
|
16
16
|
|
|
17
17
|
###### Kaminari examples
|
|
18
18
|
|
|
@@ -72,18 +72,18 @@ ActiveModelSerializers pagination relies on a paginated collection with the meth
|
|
|
72
72
|
|
|
73
73
|
### JSON adapter
|
|
74
74
|
|
|
75
|
-
If you are using `JSON` adapter, pagination links will not be included automatically, but it is possible to do so using `meta` key.
|
|
75
|
+
If you are not using `JSON` adapter, pagination links will not be included automatically, but it is possible to do so using `meta` key.
|
|
76
76
|
|
|
77
77
|
Add this method to your base API controller.
|
|
78
78
|
|
|
79
79
|
```ruby
|
|
80
|
-
def pagination_dict(
|
|
80
|
+
def pagination_dict(collection)
|
|
81
81
|
{
|
|
82
|
-
current_page:
|
|
83
|
-
next_page:
|
|
84
|
-
prev_page:
|
|
85
|
-
total_pages:
|
|
86
|
-
total_count:
|
|
82
|
+
current_page: collection.current_page,
|
|
83
|
+
next_page: collection.next_page,
|
|
84
|
+
prev_page: collection.prev_page, # use collection.previous_page when using will_paginate
|
|
85
|
+
total_pages: collection.total_pages,
|
|
86
|
+
total_count: collection.total_count
|
|
87
87
|
}
|
|
88
88
|
end
|
|
89
89
|
```
|
|
@@ -117,23 +117,22 @@ ex.
|
|
|
117
117
|
You can also achieve the same result if you have a helper method that adds the pagination info in the meta tag. For instance, in your action specify a custom serializer.
|
|
118
118
|
|
|
119
119
|
```ruby
|
|
120
|
-
render json: @posts, each_serializer: PostPreviewSerializer, meta: meta_attributes(@
|
|
120
|
+
render json: @posts, each_serializer: PostPreviewSerializer, meta: meta_attributes(@posts)
|
|
121
121
|
```
|
|
122
122
|
|
|
123
123
|
```ruby
|
|
124
124
|
#expects pagination!
|
|
125
|
-
def meta_attributes(
|
|
125
|
+
def meta_attributes(collection, extra_meta = {})
|
|
126
126
|
{
|
|
127
|
-
current_page:
|
|
128
|
-
next_page:
|
|
129
|
-
prev_page:
|
|
130
|
-
total_pages:
|
|
131
|
-
total_count:
|
|
127
|
+
current_page: collection.current_page,
|
|
128
|
+
next_page: collection.next_page,
|
|
129
|
+
prev_page: collection.prev_page, # use collection.previous_page when using will_paginate
|
|
130
|
+
total_pages: collection.total_pages,
|
|
131
|
+
total_count: collection.total_count
|
|
132
132
|
}.merge(extra_meta)
|
|
133
133
|
end
|
|
134
134
|
```
|
|
135
135
|
|
|
136
|
-
|
|
137
136
|
### Attributes adapter
|
|
138
137
|
|
|
139
138
|
This adapter does not allow us to use `meta` key, due to that it is not possible to add pagination links.
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
[Back to Guides](../README.md)
|
|
2
|
+
|
|
3
|
+
# How to add relationship links
|
|
4
|
+
|
|
5
|
+
ActiveModelSerializers offers you many ways to add links in your JSON, depending on your needs.
|
|
6
|
+
The most common use case for links is supporting nested resources.
|
|
7
|
+
|
|
8
|
+
The following examples are without included relationship data (`include` param is empty),
|
|
9
|
+
specifically the following Rails controller was used for these examples:
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
class Api::V1::UsersController < ApplicationController
|
|
13
|
+
def show
|
|
14
|
+
render jsonapi: User.find(params[:id]),
|
|
15
|
+
serializer: Api::V1::UserSerializer,
|
|
16
|
+
include: []
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Bear in mind though that ActiveModelSerializers are [framework-agnostic](outside_controller_use.md), Rails is just a common example here.
|
|
22
|
+
|
|
23
|
+
### Links as an attribute of a resource
|
|
24
|
+
**This is applicable to JSON and Attributes adapters**
|
|
25
|
+
|
|
26
|
+
You can define an attribute in the resource, named `links`.
|
|
27
|
+
|
|
28
|
+
```ruby
|
|
29
|
+
class Api::V1::UserSerializer < ActiveModel::Serializer
|
|
30
|
+
include Rails.application.routes.url_helpers
|
|
31
|
+
|
|
32
|
+
attributes :id, :name
|
|
33
|
+
|
|
34
|
+
attribute :links do
|
|
35
|
+
id = object.id
|
|
36
|
+
{
|
|
37
|
+
self: api_v1_user_path(id),
|
|
38
|
+
microposts: api_v1_microposts_path(user_id: id)
|
|
39
|
+
}
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Using the `JSON` adapter, this will result in:
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"user": {
|
|
49
|
+
"id": "1",
|
|
50
|
+
"name": "John",
|
|
51
|
+
"links": {
|
|
52
|
+
"self": "/api/v1/users/1",
|
|
53
|
+
"microposts": "/api/v1/microposts?user_id=1"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
### Links as a property of the resource definiton
|
|
61
|
+
**This is only applicable to JSONAPI adapter**
|
|
62
|
+
|
|
63
|
+
You can use the `link` class method to define the links you need in the resource's primary data.
|
|
64
|
+
|
|
65
|
+
```ruby
|
|
66
|
+
class Api::V1::UserSerializer < ActiveModel::Serializer
|
|
67
|
+
attributes :id, :name
|
|
68
|
+
|
|
69
|
+
link(:self) { api_v1_user_path(object.id) }
|
|
70
|
+
link(:microposts) { api_v1_microposts_path(user_id: object.id) }
|
|
71
|
+
end
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Using the `JSONAPI` adapter, this will result in:
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"data": {
|
|
79
|
+
"id": "1",
|
|
80
|
+
"type": "users",
|
|
81
|
+
"attributes": {
|
|
82
|
+
"name": "Example User"
|
|
83
|
+
},
|
|
84
|
+
"links": {
|
|
85
|
+
"self": "/api/v1/users/1",
|
|
86
|
+
"microposts": "/api/v1/microposts?user_id=1"
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Links that follow the JSONAPI spec
|
|
93
|
+
**This is only applicable to JSONAPI adapter**
|
|
94
|
+
|
|
95
|
+
If you have a JSONAPI-strict client that you are working with (like `ember-data`)
|
|
96
|
+
you need to construct the links inside the relationships. Also the link to fetch the
|
|
97
|
+
relationship data must be under the `related` attribute, whereas to manipulate the
|
|
98
|
+
relationship (in case of many-to-many relationship) must be under the `self` attribute.
|
|
99
|
+
|
|
100
|
+
You can find more info in the [spec](http://jsonapi.org/format/#document-resource-object-relationships).
|
|
101
|
+
|
|
102
|
+
Here is how you can do this:
|
|
103
|
+
|
|
104
|
+
```ruby
|
|
105
|
+
class Api::V1::UserSerializer < ActiveModel::Serializer
|
|
106
|
+
attributes :id, :name
|
|
107
|
+
|
|
108
|
+
has_many :microposts, serializer: Api::V1::MicropostSerializer do
|
|
109
|
+
link(:related) { api_v1_microposts_path(user_id: object.id) }
|
|
110
|
+
|
|
111
|
+
microposts = object.microposts
|
|
112
|
+
# The following code is needed to avoid n+1 queries.
|
|
113
|
+
# Core devs are working to remove this necessity.
|
|
114
|
+
# See: https://github.com/rails-api/active_model_serializers/issues/1325
|
|
115
|
+
microposts.loaded? ? microposts : microposts.none
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
This will result in:
|
|
121
|
+
|
|
122
|
+
```json
|
|
123
|
+
{
|
|
124
|
+
"data": {
|
|
125
|
+
"id": "1",
|
|
126
|
+
"type": "users",
|
|
127
|
+
"attributes": {
|
|
128
|
+
"name": "Example User"
|
|
129
|
+
},
|
|
130
|
+
"relationships": {
|
|
131
|
+
"microposts": {
|
|
132
|
+
"data": [],
|
|
133
|
+
"links": {
|
|
134
|
+
"related": "/api/v1/microposts?user_id=1"
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
```
|
data/docs/howto/add_root_key.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
[Back to Guides](../README.md)
|
|
2
|
+
|
|
1
3
|
# How to add root key
|
|
2
4
|
|
|
3
5
|
Add the root key to your API is quite simple with ActiveModelSerializers. The **Adapter** is what determines the format of your JSON response. The default adapter is the ```Attributes``` which doesn't have the root key, so your response is something similar to:
|
|
@@ -49,3 +51,5 @@ or if it returns a collection:
|
|
|
49
51
|
]
|
|
50
52
|
}
|
|
51
53
|
```
|
|
54
|
+
|
|
55
|
+
[There are several ways to specify root](../general/serializers.md#root) when using the JSON adapter.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
[Back to Guides](../README.md)
|
|
2
|
+
|
|
3
|
+
The ActiveModelSerializers grape formatter relies on the existence of `env['grape.request']` which is implemeted by `Grape::Middleware::Globals`. You can meet his dependency by calling it before mounting the endpoints.
|
|
4
|
+
|
|
5
|
+
In the simpliest way:
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
class API < Grape::API
|
|
9
|
+
# @note Make sure this is above you're first +mount+
|
|
10
|
+
use Grape::Middleware::Globals
|
|
11
|
+
end
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
or more like what is shown in current Grape tutorials:
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
module MyApi
|
|
18
|
+
class ApiBase < Grape::API
|
|
19
|
+
use Grape::Middleware::Globals
|
|
20
|
+
|
|
21
|
+
require 'grape/active_model_serializers'
|
|
22
|
+
include Grape::ActiveModelSerializers
|
|
23
|
+
|
|
24
|
+
mount MyApi::V1::ApiBase
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
You could meet this dependency with your own middleware. The invocation might look like:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
module MyApi
|
|
33
|
+
class ApiBase < Grape::API
|
|
34
|
+
use My::Middleware::Thingamabob
|
|
35
|
+
|
|
36
|
+
require 'grape/active_model_serializers'
|
|
37
|
+
include Grape::ActiveModelSerializers
|
|
38
|
+
|
|
39
|
+
mount MyApi::V1::ApiBase
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
```
|
|
@@ -10,8 +10,8 @@ In ActiveModelSerializers versions 0.10 or later, serializing resources outside
|
|
|
10
10
|
# Create our resource
|
|
11
11
|
post = Post.create(title: "Sample post", body: "I love Active Model Serializers!")
|
|
12
12
|
|
|
13
|
-
# Optional options parameters
|
|
14
|
-
options = {}
|
|
13
|
+
# Optional options parameters for both the serializer and instance
|
|
14
|
+
options = {serializer: PostDetailedSerializer, username: 'sample user'}
|
|
15
15
|
|
|
16
16
|
# Create a serializable resource instance
|
|
17
17
|
serializable_resource = ActiveModelSerializers::SerializableResource.new(post, options)
|
|
@@ -19,10 +19,12 @@ serializable_resource = ActiveModelSerializers::SerializableResource.new(post, o
|
|
|
19
19
|
# Convert your resource into json
|
|
20
20
|
model_json = serializable_resource.as_json
|
|
21
21
|
```
|
|
22
|
+
The object that is passed to `ActiveModelSerializers::SerializableResource.new` can be a single resource or a collection.
|
|
23
|
+
The additional options are the same options that are passed [through controllers](../general/rendering.md#explicit-serializer).
|
|
22
24
|
|
|
23
25
|
### Looking up the Serializer for a Resource
|
|
24
26
|
|
|
25
|
-
If you want to retrieve
|
|
27
|
+
If you want to retrieve the serializer class for a specific resource, you can do the following:
|
|
26
28
|
|
|
27
29
|
```ruby
|
|
28
30
|
# Create our resource
|
|
@@ -41,7 +43,13 @@ You could also retrieve the serializer via:
|
|
|
41
43
|
ActiveModelSerializers::SerializableResource.new(post, options).serializer
|
|
42
44
|
```
|
|
43
45
|
|
|
44
|
-
Both approaches will return
|
|
46
|
+
Both approaches will return the serializer class that will be used for the resource.
|
|
47
|
+
|
|
48
|
+
Additionally, you could retrieve the serializer instance for the resource via:
|
|
49
|
+
|
|
50
|
+
```ruby
|
|
51
|
+
ActiveModelSerializers::SerializableResource.new(post, options).serializer_instance
|
|
52
|
+
```
|
|
45
53
|
|
|
46
54
|
## Serializing before controller render
|
|
47
55
|
|
|
@@ -11,7 +11,7 @@ For example, we could pass in a field, such as `user_id` into our serializer.
|
|
|
11
11
|
```ruby
|
|
12
12
|
# posts_controller.rb
|
|
13
13
|
class PostsController < ApplicationController
|
|
14
|
-
def dashboard
|
|
14
|
+
def dashboard
|
|
15
15
|
render json: @post, user_id: 12
|
|
16
16
|
end
|
|
17
17
|
end
|
|
@@ -20,7 +20,7 @@ end
|
|
|
20
20
|
class PostSerializer < ActiveModel::Serializer
|
|
21
21
|
attributes :id, :title, :body
|
|
22
22
|
|
|
23
|
-
def comments_by_me
|
|
23
|
+
def comments_by_me
|
|
24
24
|
Comments.where(user_id: instance_options[:user_id], post_id: object.id)
|
|
25
25
|
end
|
|
26
26
|
end
|
|
@@ -2,13 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
# How to serialize a Plain-Old Ruby Object (PORO)
|
|
4
4
|
|
|
5
|
-
When you are first getting started with ActiveModelSerializers, it may seem only `ActiveRecord::Base` objects can be serializable,
|
|
5
|
+
When you are first getting started with ActiveModelSerializers, it may seem only `ActiveRecord::Base` objects can be serializable,
|
|
6
|
+
but pretty much any object can be serializable with ActiveModelSerializers.
|
|
7
|
+
Here is an example of a PORO that is serializable in most situations:
|
|
8
|
+
|
|
6
9
|
```ruby
|
|
7
10
|
# my_model.rb
|
|
8
11
|
class MyModel
|
|
9
12
|
alias :read_attribute_for_serialization :send
|
|
10
13
|
attr_accessor :id, :name, :level
|
|
11
|
-
|
|
14
|
+
|
|
12
15
|
def initialize(attributes)
|
|
13
16
|
@id = attributes[:id]
|
|
14
17
|
@name = attributes[:name]
|
|
@@ -21,12 +24,50 @@ class MyModel
|
|
|
21
24
|
end
|
|
22
25
|
```
|
|
23
26
|
|
|
24
|
-
|
|
27
|
+
The [ActiveModel::Serializer::Lint::Tests](../../lib/active_model/serializer/lint.rb)
|
|
28
|
+
define and validate which methods ActiveModelSerializers expects to be implemented.
|
|
29
|
+
|
|
30
|
+
An implementation of the complete spec is included either for use or as reference:
|
|
31
|
+
[`ActiveModelSerializers::Model`](../../lib/active_model_serializers/model.rb).
|
|
32
|
+
You can use in production code that will make your PORO a lot cleaner.
|
|
33
|
+
|
|
34
|
+
The above code now becomes:
|
|
35
|
+
|
|
25
36
|
```ruby
|
|
26
37
|
# my_model.rb
|
|
27
38
|
class MyModel < ActiveModelSerializers::Model
|
|
28
|
-
|
|
39
|
+
attributes :id, :name, :level
|
|
40
|
+
end
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The default serializer would be `MyModelSerializer`.
|
|
44
|
+
|
|
45
|
+
*IMPORTANT*: There is a surprising behavior (bug) in the current implementation of ActiveModelSerializers::Model that
|
|
46
|
+
prevents an accessor from modifying attributes on the instance. The fix for this bug
|
|
47
|
+
is a breaking change, so we have made an opt-in configuration.
|
|
48
|
+
|
|
49
|
+
New applications should set:
|
|
50
|
+
|
|
51
|
+
```ruby
|
|
52
|
+
ActiveModelSerializers::Model.derive_attributes_from_names_and_fix_accessors
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Existing applications can use the fix *and* avoid breaking changes
|
|
56
|
+
by making a superclass for new models. For example:
|
|
57
|
+
|
|
58
|
+
```ruby
|
|
59
|
+
class SerializablePoro < ActiveModelSerializers::Model
|
|
60
|
+
derive_attributes_from_names_and_fix_accessors
|
|
29
61
|
end
|
|
30
62
|
```
|
|
31
63
|
|
|
32
|
-
|
|
64
|
+
So that `MyModel` above would inherit from `SerializablePoro`.
|
|
65
|
+
|
|
66
|
+
`derive_attributes_from_names_and_fix_accessors` prepends the `DeriveAttributesFromNamesAndFixAccessors`
|
|
67
|
+
module and does the following:
|
|
68
|
+
|
|
69
|
+
- `id` will *always* be in the attributes. (This is until we separate out the caching requirement for POROs.)
|
|
70
|
+
- Overwrites the `attributes` method to that it only returns declared attributes.
|
|
71
|
+
`attributes` will now be a frozen hash with indifferent access.
|
|
72
|
+
|
|
73
|
+
For more information, see [README: What does a 'serializable resource' look like?](../../README.md#what-does-a-serializable-resource-look-like).
|