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.
Files changed (168) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +6 -5
  3. data/.travis.yml +17 -5
  4. data/CHANGELOG.md +126 -2
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +5 -2
  7. data/README.md +166 -26
  8. data/Rakefile +3 -32
  9. data/active_model_serializers.gemspec +22 -25
  10. data/appveyor.yml +9 -3
  11. data/bin/rubocop +38 -0
  12. data/docs/README.md +2 -1
  13. data/docs/general/adapters.md +29 -11
  14. data/docs/general/caching.md +7 -1
  15. data/docs/general/configuration_options.md +70 -1
  16. data/docs/general/deserialization.md +1 -1
  17. data/docs/general/fields.md +31 -0
  18. data/docs/general/getting_started.md +1 -1
  19. data/docs/general/logging.md +7 -0
  20. data/docs/general/rendering.md +62 -24
  21. data/docs/general/serializers.md +121 -13
  22. data/docs/howto/add_pagination_links.md +16 -17
  23. data/docs/howto/add_relationship_links.md +140 -0
  24. data/docs/howto/add_root_key.md +4 -0
  25. data/docs/howto/grape_integration.md +42 -0
  26. data/docs/howto/outside_controller_use.md +12 -4
  27. data/docs/howto/passing_arbitrary_options.md +2 -2
  28. data/docs/howto/serialize_poro.md +46 -5
  29. data/docs/howto/test.md +2 -0
  30. data/docs/howto/upgrade_from_0_8_to_0_10.md +265 -0
  31. data/docs/integrations/ember-and-json-api.md +67 -32
  32. data/docs/jsonapi/schema.md +1 -1
  33. data/lib/action_controller/serialization.rb +13 -3
  34. data/lib/active_model/serializer/adapter/base.rb +2 -0
  35. data/lib/active_model/serializer/array_serializer.rb +8 -5
  36. data/lib/active_model/serializer/association.rb +62 -10
  37. data/lib/active_model/serializer/belongs_to_reflection.rb +4 -3
  38. data/lib/active_model/serializer/collection_serializer.rb +35 -12
  39. data/lib/active_model/serializer/{caching.rb → concerns/caching.rb} +82 -115
  40. data/lib/active_model/serializer/error_serializer.rb +11 -7
  41. data/lib/active_model/serializer/errors_serializer.rb +25 -20
  42. data/lib/active_model/serializer/has_many_reflection.rb +3 -3
  43. data/lib/active_model/serializer/has_one_reflection.rb +1 -4
  44. data/lib/active_model/serializer/lazy_association.rb +95 -0
  45. data/lib/active_model/serializer/lint.rb +134 -130
  46. data/lib/active_model/serializer/reflection.rb +127 -67
  47. data/lib/active_model/serializer/version.rb +1 -1
  48. data/lib/active_model/serializer.rb +296 -79
  49. data/lib/active_model_serializers/adapter/attributes.rb +3 -66
  50. data/lib/active_model_serializers/adapter/base.rb +39 -39
  51. data/lib/active_model_serializers/adapter/json_api/deserialization.rb +1 -1
  52. data/lib/active_model_serializers/adapter/json_api/link.rb +1 -1
  53. data/lib/active_model_serializers/adapter/json_api/pagination_links.rb +8 -1
  54. data/lib/active_model_serializers/adapter/json_api/relationship.rb +63 -23
  55. data/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +32 -9
  56. data/lib/active_model_serializers/adapter/json_api.rb +71 -57
  57. data/lib/active_model_serializers/adapter.rb +6 -0
  58. data/lib/active_model_serializers/deprecate.rb +1 -2
  59. data/lib/active_model_serializers/deserialization.rb +2 -0
  60. data/lib/active_model_serializers/lookup_chain.rb +80 -0
  61. data/lib/active_model_serializers/model.rb +109 -28
  62. data/lib/active_model_serializers/railtie.rb +3 -1
  63. data/lib/active_model_serializers/register_jsonapi_renderer.rb +44 -31
  64. data/lib/active_model_serializers/serializable_resource.rb +6 -5
  65. data/lib/active_model_serializers/serialization_context.rb +10 -3
  66. data/lib/active_model_serializers/test/schema.rb +2 -2
  67. data/lib/active_model_serializers.rb +15 -0
  68. data/lib/generators/rails/resource_override.rb +1 -1
  69. data/lib/generators/rails/serializer_generator.rb +4 -4
  70. data/lib/grape/active_model_serializers.rb +7 -5
  71. data/lib/grape/formatters/active_model_serializers.rb +19 -2
  72. data/lib/grape/helpers/active_model_serializers.rb +1 -0
  73. data/lib/tasks/rubocop.rake +53 -0
  74. data/test/action_controller/adapter_selector_test.rb +14 -5
  75. data/test/action_controller/explicit_serializer_test.rb +5 -4
  76. data/test/action_controller/json/include_test.rb +106 -27
  77. data/test/action_controller/json_api/errors_test.rb +8 -9
  78. data/test/action_controller/json_api/fields_test.rb +66 -0
  79. data/test/action_controller/json_api/linked_test.rb +29 -24
  80. data/test/action_controller/json_api/pagination_test.rb +19 -19
  81. data/test/action_controller/json_api/transform_test.rb +11 -3
  82. data/test/action_controller/lookup_proc_test.rb +49 -0
  83. data/test/action_controller/namespace_lookup_test.rb +232 -0
  84. data/test/action_controller/serialization_scope_name_test.rb +12 -6
  85. data/test/action_controller/serialization_test.rb +12 -9
  86. data/test/active_model_serializers/json_pointer_test.rb +15 -13
  87. data/test/active_model_serializers/model_test.rb +137 -4
  88. data/test/active_model_serializers/railtie_test_isolated.rb +12 -7
  89. data/test/active_model_serializers/register_jsonapi_renderer_test_isolated.rb +161 -0
  90. data/test/active_model_serializers/serialization_context_test_isolated.rb +23 -10
  91. data/test/active_model_serializers/test/schema_test.rb +3 -2
  92. data/test/adapter/attributes_test.rb +40 -0
  93. data/test/adapter/json/collection_test.rb +14 -0
  94. data/test/adapter/json/has_many_test.rb +10 -2
  95. data/test/adapter/json/transform_test.rb +15 -15
  96. data/test/adapter/json_api/collection_test.rb +4 -3
  97. data/test/adapter/json_api/errors_test.rb +17 -19
  98. data/test/adapter/json_api/fields_test.rb +12 -3
  99. data/test/adapter/json_api/has_many_test.rb +49 -20
  100. data/test/adapter/json_api/include_data_if_sideloaded_test.rb +183 -0
  101. data/test/adapter/json_api/json_api_test.rb +5 -7
  102. data/test/adapter/json_api/linked_test.rb +33 -12
  103. data/test/adapter/json_api/links_test.rb +4 -2
  104. data/test/adapter/json_api/pagination_links_test.rb +35 -8
  105. data/test/adapter/json_api/relationship_test.rb +309 -73
  106. data/test/adapter/json_api/resource_identifier_test.rb +27 -2
  107. data/test/adapter/json_api/resource_meta_test.rb +3 -3
  108. data/test/adapter/json_api/transform_test.rb +263 -253
  109. data/test/adapter/json_api/type_test.rb +1 -1
  110. data/test/adapter/json_test.rb +8 -7
  111. data/test/adapter/null_test.rb +1 -2
  112. data/test/adapter/polymorphic_test.rb +5 -5
  113. data/test/adapter_test.rb +1 -1
  114. data/test/benchmark/app.rb +1 -1
  115. data/test/benchmark/benchmarking_support.rb +1 -1
  116. data/test/benchmark/bm_active_record.rb +81 -0
  117. data/test/benchmark/bm_adapter.rb +38 -0
  118. data/test/benchmark/bm_caching.rb +16 -16
  119. data/test/benchmark/bm_lookup_chain.rb +83 -0
  120. data/test/benchmark/bm_transform.rb +21 -10
  121. data/test/benchmark/controllers.rb +16 -17
  122. data/test/benchmark/fixtures.rb +72 -72
  123. data/test/cache_test.rb +235 -69
  124. data/test/collection_serializer_test.rb +25 -12
  125. data/test/fixtures/active_record.rb +45 -10
  126. data/test/fixtures/poro.rb +124 -181
  127. data/test/generators/serializer_generator_test.rb +23 -5
  128. data/test/grape_test.rb +170 -56
  129. data/test/lint_test.rb +1 -1
  130. data/test/logger_test.rb +13 -11
  131. data/test/serializable_resource_test.rb +18 -22
  132. data/test/serializers/association_macros_test.rb +3 -2
  133. data/test/serializers/associations_test.rb +178 -49
  134. data/test/serializers/attribute_test.rb +5 -3
  135. data/test/serializers/attributes_test.rb +1 -1
  136. data/test/serializers/caching_configuration_test_isolated.rb +6 -6
  137. data/test/serializers/fieldset_test.rb +1 -1
  138. data/test/serializers/meta_test.rb +12 -6
  139. data/test/serializers/options_test.rb +17 -6
  140. data/test/serializers/read_attribute_for_serialization_test.rb +3 -3
  141. data/test/serializers/reflection_test.rb +427 -0
  142. data/test/serializers/root_test.rb +1 -1
  143. data/test/serializers/serialization_test.rb +2 -2
  144. data/test/serializers/serializer_for_test.rb +12 -10
  145. data/test/serializers/serializer_for_with_namespace_test.rb +88 -0
  146. data/test/support/isolated_unit.rb +5 -2
  147. data/test/support/rails5_shims.rb +8 -2
  148. data/test/support/rails_app.rb +2 -9
  149. data/test/support/serialization_testing.rb +23 -5
  150. data/test/test_helper.rb +13 -0
  151. metadata +105 -42
  152. data/.rubocop_todo.yml +0 -167
  153. data/docs/ARCHITECTURE.md +0 -126
  154. data/lib/active_model/serializer/associations.rb +0 -100
  155. data/lib/active_model/serializer/attributes.rb +0 -82
  156. data/lib/active_model/serializer/collection_reflection.rb +0 -7
  157. data/lib/active_model/serializer/configuration.rb +0 -35
  158. data/lib/active_model/serializer/include_tree.rb +0 -111
  159. data/lib/active_model/serializer/links.rb +0 -35
  160. data/lib/active_model/serializer/meta.rb +0 -29
  161. data/lib/active_model/serializer/singular_reflection.rb +0 -7
  162. data/lib/active_model/serializer/type.rb +0 -25
  163. data/lib/active_model_serializers/key_transform.rb +0 -70
  164. data/test/active_model_serializers/key_transform_test.rb +0 -263
  165. data/test/adapter/json_api/relationships_test.rb +0 -199
  166. data/test/include_tree/from_include_args_test.rb +0 -26
  167. data/test/include_tree/from_string_test.rb +0 -94
  168. data/test/include_tree/include_args_to_hash_test.rb +0 -64
@@ -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 :title { 'A Different Title'}` | `{ title: 'A Different Title' } `
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
- [PR please for conditional attributes:)](https://github.com/rails-api/active_model_serializers/pull/1403)
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
- The `::type` method defines the JSONAPI [type](http://jsonapi.org/format/#document-resource-object-identification) that will be rendered for this serializer.
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 :author { link_author_url(object) }
198
- link :link_authors { link_authors_url }
232
+ link(:author) { link_author_url(object) }
233
+ link(:link_authors) { link_authors_url }
199
234
  link :other, 'https://example.com/resource'
200
- link :posts { link_author_posts_url(object) }
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
- PR please :)
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
- Specfically, the `scope_name` is defaulted to `:current_user`, and may be set as
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
- PR please :)
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
- PR please :)
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 others adapters does not have this feature, it is possible to
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 see in our docs
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(object)
80
+ def pagination_dict(collection)
81
81
  {
82
- current_page: object.current_page,
83
- next_page: object.next_page,
84
- prev_page: object.prev_page,
85
- total_pages: object.total_pages,
86
- total_count: object.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(@post)
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(resource, extra_meta = {})
125
+ def meta_attributes(collection, extra_meta = {})
126
126
  {
127
- current_page: resource.current_page,
128
- next_page: resource.next_page,
129
- prev_page: resource.prev_page,
130
- total_pages: resource.total_pages,
131
- total_count: resource.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
+ ```
@@ -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 a serializer for a specific resource, you can do the following:
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 an instance, if any, of the resource's serializer.
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, but pretty much any object can be serializable with ActiveModelSerializers. Here is an example of a PORO that is 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
- Fortunately, ActiveModelSerializers provides a [`ActiveModelSerializers::Model`](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model_serializers/model.rb) which you can use in production code that will make your PORO a lot cleaner. The above code now becomes:
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
- attr_accessor :id, :name, :level
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
- The default serializer would be `MyModelSerializer`.
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).
data/docs/howto/test.md CHANGED
@@ -1,3 +1,5 @@
1
+ [Back to Guides](../README.md)
2
+
1
3
  # How to test
2
4
 
3
5
  ## Controller Serializer Usage