active_model_serializers 0.9.0 → 0.10.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +679 -9
  3. data/MIT-LICENSE +3 -2
  4. data/README.md +195 -753
  5. data/lib/action_controller/serialization.rb +45 -49
  6. data/lib/active_model/serializable_resource.rb +13 -0
  7. data/lib/active_model/serializer.rb +369 -212
  8. data/lib/active_model/serializer/adapter.rb +26 -0
  9. data/lib/active_model/serializer/adapter/attributes.rb +17 -0
  10. data/lib/active_model/serializer/adapter/base.rb +20 -0
  11. data/lib/active_model/serializer/adapter/json.rb +17 -0
  12. data/lib/active_model/serializer/adapter/json_api.rb +17 -0
  13. data/lib/active_model/serializer/adapter/null.rb +17 -0
  14. data/lib/active_model/serializer/array_serializer.rb +14 -0
  15. data/lib/active_model/serializer/association.rb +73 -0
  16. data/lib/active_model/serializer/attribute.rb +27 -0
  17. data/lib/active_model/serializer/belongs_to_reflection.rb +13 -0
  18. data/lib/active_model/serializer/collection_serializer.rb +90 -0
  19. data/lib/active_model/serializer/concerns/caching.rb +305 -0
  20. data/lib/active_model/serializer/error_serializer.rb +16 -0
  21. data/lib/active_model/serializer/errors_serializer.rb +34 -0
  22. data/lib/active_model/serializer/field.rb +92 -0
  23. data/lib/active_model/serializer/fieldset.rb +33 -0
  24. data/lib/active_model/serializer/has_many_reflection.rb +12 -0
  25. data/lib/active_model/serializer/has_one_reflection.rb +9 -0
  26. data/lib/active_model/serializer/lazy_association.rb +99 -0
  27. data/lib/active_model/serializer/link.rb +23 -0
  28. data/lib/active_model/serializer/lint.rb +152 -0
  29. data/lib/active_model/serializer/null.rb +19 -0
  30. data/lib/active_model/serializer/reflection.rb +212 -0
  31. data/lib/active_model/serializer/version.rb +3 -1
  32. data/lib/active_model_serializers.rb +60 -17
  33. data/lib/active_model_serializers/adapter.rb +100 -0
  34. data/lib/active_model_serializers/adapter/attributes.rb +36 -0
  35. data/lib/active_model_serializers/adapter/base.rb +85 -0
  36. data/lib/active_model_serializers/adapter/json.rb +23 -0
  37. data/lib/active_model_serializers/adapter/json_api.rb +535 -0
  38. data/lib/active_model_serializers/adapter/json_api/deserialization.rb +215 -0
  39. data/lib/active_model_serializers/adapter/json_api/error.rb +98 -0
  40. data/lib/active_model_serializers/adapter/json_api/jsonapi.rb +51 -0
  41. data/lib/active_model_serializers/adapter/json_api/link.rb +85 -0
  42. data/lib/active_model_serializers/adapter/json_api/meta.rb +39 -0
  43. data/lib/active_model_serializers/adapter/json_api/pagination_links.rb +90 -0
  44. data/lib/active_model_serializers/adapter/json_api/relationship.rb +106 -0
  45. data/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +68 -0
  46. data/lib/active_model_serializers/adapter/null.rb +11 -0
  47. data/lib/active_model_serializers/callbacks.rb +57 -0
  48. data/lib/active_model_serializers/deprecate.rb +56 -0
  49. data/lib/active_model_serializers/deserialization.rb +17 -0
  50. data/lib/active_model_serializers/json_pointer.rb +16 -0
  51. data/lib/active_model_serializers/logging.rb +124 -0
  52. data/lib/active_model_serializers/lookup_chain.rb +82 -0
  53. data/lib/active_model_serializers/model.rb +132 -0
  54. data/lib/active_model_serializers/model/caching.rb +25 -0
  55. data/lib/active_model_serializers/railtie.rb +52 -0
  56. data/lib/active_model_serializers/register_jsonapi_renderer.rb +80 -0
  57. data/lib/active_model_serializers/serializable_resource.rb +84 -0
  58. data/lib/active_model_serializers/serialization_context.rb +41 -0
  59. data/lib/active_model_serializers/test.rb +9 -0
  60. data/lib/active_model_serializers/test/schema.rb +140 -0
  61. data/lib/active_model_serializers/test/serializer.rb +127 -0
  62. data/lib/generators/rails/USAGE +6 -0
  63. data/lib/{active_model/serializer/generators → generators/rails}/resource_override.rb +3 -4
  64. data/lib/{active_model/serializer/generators/serializer → generators/rails}/serializer_generator.rb +6 -5
  65. data/lib/{active_model/serializer/generators/serializer/templates/serializer.rb → generators/rails/templates/serializer.rb.erb} +0 -0
  66. data/lib/grape/active_model_serializers.rb +18 -0
  67. data/lib/grape/formatters/active_model_serializers.rb +34 -0
  68. data/lib/grape/helpers/active_model_serializers.rb +19 -0
  69. data/lib/tasks/rubocop.rake +55 -0
  70. metadata +315 -99
  71. data/CONTRIBUTING.md +0 -20
  72. data/DESIGN.textile +0 -586
  73. data/lib/action_controller/serialization_test_case.rb +0 -79
  74. data/lib/active_model/array_serializer.rb +0 -65
  75. data/lib/active_model/default_serializer.rb +0 -32
  76. data/lib/active_model/serializable.rb +0 -40
  77. data/lib/active_model/serializer/associations.rb +0 -102
  78. data/lib/active_model/serializer/config.rb +0 -31
  79. data/lib/active_model/serializer/generators/serializer/USAGE +0 -9
  80. data/lib/active_model/serializer/generators/serializer/scaffold_controller_generator.rb +0 -14
  81. data/lib/active_model/serializer/generators/serializer/templates/controller.rb +0 -93
  82. data/lib/active_model/serializer/railtie.rb +0 -10
  83. data/lib/active_model/serializer_support.rb +0 -5
  84. data/test/fixtures/active_record.rb +0 -92
  85. data/test/fixtures/poro.rb +0 -75
  86. data/test/integration/action_controller/serialization_test.rb +0 -287
  87. data/test/integration/action_controller/serialization_test_case_test.rb +0 -61
  88. data/test/integration/active_record/active_record_test.rb +0 -77
  89. data/test/integration/generators/resource_generator_test.rb +0 -26
  90. data/test/integration/generators/scaffold_controller_generator_test.rb +0 -64
  91. data/test/integration/generators/serializer_generator_test.rb +0 -41
  92. data/test/test_app.rb +0 -11
  93. data/test/test_helper.rb +0 -24
  94. data/test/unit/active_model/array_serializer/except_test.rb +0 -18
  95. data/test/unit/active_model/array_serializer/key_format_test.rb +0 -18
  96. data/test/unit/active_model/array_serializer/meta_test.rb +0 -53
  97. data/test/unit/active_model/array_serializer/only_test.rb +0 -18
  98. data/test/unit/active_model/array_serializer/root_test.rb +0 -102
  99. data/test/unit/active_model/array_serializer/scope_test.rb +0 -24
  100. data/test/unit/active_model/array_serializer/serialization_test.rb +0 -199
  101. data/test/unit/active_model/default_serializer_test.rb +0 -13
  102. data/test/unit/active_model/serializer/associations/build_serializer_test.rb +0 -21
  103. data/test/unit/active_model/serializer/associations_test.rb +0 -19
  104. data/test/unit/active_model/serializer/attributes_test.rb +0 -41
  105. data/test/unit/active_model/serializer/config_test.rb +0 -88
  106. data/test/unit/active_model/serializer/filter_test.rb +0 -69
  107. data/test/unit/active_model/serializer/has_many_test.rb +0 -230
  108. data/test/unit/active_model/serializer/has_one_test.rb +0 -207
  109. data/test/unit/active_model/serializer/key_format_test.rb +0 -25
  110. data/test/unit/active_model/serializer/meta_test.rb +0 -39
  111. data/test/unit/active_model/serializer/options_test.rb +0 -15
  112. data/test/unit/active_model/serializer/root_test.rb +0 -117
  113. data/test/unit/active_model/serializer/scope_test.rb +0 -49
data/MIT-LICENSE CHANGED
@@ -1,4 +1,6 @@
1
- Copyright (c) 2011-2012 José Valim & Yehuda Katz
1
+ Copyright (c) 2014 Steve Klabnik
2
+
3
+ MIT License
2
4
 
3
5
  Permission is hereby granted, free of charge, to any person obtaining
4
6
  a copy of this software and associated documentation files (the
@@ -18,4 +20,3 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
20
  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
21
  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
22
  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
-
data/README.md CHANGED
@@ -1,863 +1,305 @@
1
- [![Build Status](https://api.travis-ci.org/rails-api/active_model_serializers.png?branch=0-9-stable)](https://travis-ci.org/rails-api/active_model_serializers)
2
- [![Code Climate](https://codeclimate.com/github/rails-api/active_model_serializers.png)](https://codeclimate.com/github/rails-api/active_model_serializers)
1
+ # ActiveModelSerializers
3
2
 
4
- # ActiveModel::Serializers
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>
5
26
 
6
- ## Purpose
27
+ ## About
7
28
 
8
- `ActiveModel::Serializers` encapsulates the JSON serialization of objects.
9
- Objects that respond to read\_attribute\_for\_serialization
10
- (including `ActiveModel` and `ActiveRecord` objects) are supported.
29
+ ActiveModelSerializers brings convention over configuration to your JSON generation.
11
30
 
12
- Serializers know about both a model and the `current_user`, so you can
13
- customize serialization based upon whether a user is authorized to see the
14
- content.
31
+ ActiveModelSerializers works through two components: **serializers** and **adapters**.
15
32
 
16
- In short, **serializers replace hash-driven development with object-oriented
17
- development.**
33
+ Serializers describe _which_ attributes and relationships should be serialized.
18
34
 
19
- # Installing
35
+ Adapters describe _how_ attributes and relationships should be serialized.
20
36
 
21
- The easiest way to install `ActiveModel::Serializers` is to add it to your
22
- `Gemfile`:
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.)
23
41
 
24
- ```ruby
25
- gem "active_model_serializers"
26
- ```
27
-
28
- Then, install it on the command line:
29
-
30
- ```
31
- $ bundle install
32
- ```
33
-
34
- #### Ruby 1.8 is no longer supported!
35
-
36
- If you must use a ruby 1.8 version (MRI 1.8.7, REE, Rubinius 1.8, or JRuby 1.8), you need to use version 0.8.x.
37
- Versions after 0.9.0 do not support ruby 1.8. To specify version 0.8, include this in your Gemfile:
38
-
39
- ```ruby
40
- gem "active_model_serializers", "~> 0.8.0"
41
- ```
42
-
43
-
44
- # Creating a Serializer
45
-
46
- The easiest way to create a new serializer is to generate a new resource, which
47
- will generate a serializer at the same time:
48
-
49
- ```
50
- $ rails g resource post title:string body:string
51
- ```
52
-
53
- This will generate a serializer in `app/serializers/post_serializer.rb` for
54
- your new model. You can also generate a serializer for an existing model with
55
- the serializer generator:
56
-
57
- ```
58
- $ rails g serializer post
59
- ```
60
-
61
- ### Support for POROs
62
-
63
- The PORO should include ActiveModel::SerializerSupport. That's all you need to
64
- do to have your POROs supported.
65
-
66
- For Rails versions before Rails 4 ActiveModel::Serializers expects objects to
67
- implement `read_attribute_for_serialization`.
68
-
69
- # render :json
70
-
71
- In your controllers, when you use `render :json`, Rails will now first search
72
- for a serializer for the object and use it if available.
73
-
74
- ```ruby
75
- class PostsController < ApplicationController
76
- def show
77
- @post = Post.find(params[:id])
78
- render json: @post
79
- end
80
- end
81
- ```
82
-
83
- In this case, Rails will look for a serializer named `PostSerializer`, and if
84
- it exists, use it to serialize the `Post`.
85
-
86
- This also works with `respond_with`, which uses `to_json` under the hood. Also
87
- note that any options passed to `render :json` will be passed to your
88
- serializer and available as `@options` inside.
89
-
90
- To specify a custom serializer for an object, you can specify the
91
- serializer when you render the object:
92
-
93
- ```ruby
94
- render json: @post, serializer: FancyPostSerializer
95
- ```
96
-
97
- ## Arrays
98
-
99
- In your controllers, when you use `render :json` for an array of objects, AMS will
100
- use `ActiveModel::ArraySerializer` (included in this project) as the base serializer,
101
- and the individual `Serializer` for the objects contained in that array.
102
-
103
- ```ruby
104
- class PostSerializer < ActiveModel::Serializer
105
- attributes :title, :body
106
- end
107
-
108
- class PostsController < ApplicationController
109
- def index
110
- @posts = Post.all
111
- render json: @posts
112
- end
113
- end
114
- ```
115
-
116
- Given the example above, the index action will return
117
-
118
- ```json
119
- {
120
- "posts":
121
- [
122
- { "title": "Post 1", "body": "Hello!" },
123
- { "title": "Post 2", "body": "Goodbye!" }
124
- ]
125
- }
126
- ```
127
-
128
- By default, the root element is the name of the controller. For example, `PostsController`
129
- generates a root element "posts". To change it:
130
-
131
- ```ruby
132
- render json: @posts, root: "some_posts"
133
- ```
134
-
135
- You may disable the root element for arrays at the top level, which will result in
136
- more concise json. See the next section for ways on how to do this. Disabling the
137
- root element of the array with any of those methods will produce
138
-
139
- ```json
140
- [
141
- { "title": "Post 1", "body": "Hello!" },
142
- { "title": "Post 2", "body": "Goodbye!" }
143
- ]
144
- ```
145
-
146
- To specify a custom serializer for the items within an array:
147
-
148
- ```ruby
149
- render json: @posts, each_serializer: FancyPostSerializer
150
- ```
151
-
152
- ## Render independently
153
-
154
- By default the setting of serializer is in controller as described above which is the
155
- recommended way. However, there may be cases you need to render the json object elsewhere
156
- say in a helper or a view when controller is only for main object.
157
-
158
- Then you can render the serialized JSON independently.
159
-
160
- ```ruby
161
- def current_user_as_json_helper
162
- CurrentUserSerializer.new(current_user).to_json
163
- end
164
- ```
165
-
166
- You can also render an array of objects using ArraySerializer.
167
-
168
- ```ruby
169
- def users_array_as_json_helper(users)
170
- ActiveModel::ArraySerializer.new(users, each_serializer: UserSerializer).to_json
171
- end
172
- ```
173
-
174
-
175
- ## Disabling the root element
176
-
177
- You have 4 options to disable the root element, each with a slightly different scope:
178
-
179
- #### 1. Disable root globally for all, or per class
180
-
181
- In an initializer:
182
-
183
- ```ruby
184
- # Disable for all serializers (except ArraySerializer)
185
- ActiveModel::Serializer.root = false
186
-
187
- # Disable for ArraySerializer
188
- ActiveModel::ArraySerializer.root = false
189
- ```
190
-
191
- #### 2. Disable root per render call in your controller
192
-
193
- ```ruby
194
- render json: @posts, root: false
195
- ```
196
-
197
- #### 3. Subclass the serializer, and specify using it
198
-
199
- ```ruby
200
- class CustomArraySerializer < ActiveModel::ArraySerializer
201
- self.root = false
202
- end
203
-
204
- # controller:
205
- render json: @posts, serializer: CustomArraySerializer
206
- ```
207
-
208
- #### 4. Define default_serializer_options in your controller
209
-
210
- If you define `default_serializer_options` method in your controller,
211
- all serializers in actions of this controller and it's children will use them.
212
- One of the options may be `root: false`
213
-
214
- ```ruby
215
- def default_serializer_options
216
- {
217
- root: false
218
- }
219
- end
220
- ```
221
-
222
- ## Changing the Key Format
223
-
224
- You can specify that serializers use the lower-camel key format at the config, class or instance level.
225
-
226
- ```ruby
227
-
228
- ActiveModel::Serializer.setup do |config|
229
- config.key_format = :lower_camel
230
- end
231
-
232
- class BlogLowerCamelSerializer < ActiveModel::Serializer
233
- format_keys :lower_camel
234
- end
235
-
236
- BlogSerializer.new(object, key_format: :lower_camel)
237
- ```
238
-
239
- ## Getting the old version
240
-
241
- If you find that your project is already relying on the old rails to_json
242
- change `render :json` to `render json: @your_object.to_json`.
243
-
244
- # Attributes and Associations
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.
245
46
 
246
- Once you have a serializer, you can specify which attributes and associations
247
- you would like to include in the serialized form.
47
+ `0.10.x` is **not** backward compatible with `0.9.x` nor `0.8.x`.
248
48
 
249
- ```ruby
250
- class PostSerializer < ActiveModel::Serializer
251
- attributes :id, :title, :body
252
- has_many :comments
253
- end
254
- ```
255
-
256
- ## Attributes
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)
257
51
 
258
- For specified attributes, a serializer will look up the attribute on the
259
- object you passed to `render :json`. It uses
260
- `read_attribute_for_serialization`, which `ActiveRecord` objects implement as a
261
- regular attribute lookup.
52
+ ## Installation
262
53
 
263
- Before looking up the attribute on the object, a serializer will check for the
264
- presence of a method with the name of the attribute. This allows serializers to
265
- include properties beyond the simple attributes of the model. For example:
54
+ Add this line to your application's Gemfile:
266
55
 
267
- ```ruby
268
- class PersonSerializer < ActiveModel::Serializer
269
- attributes :first_name, :last_name, :full_name
270
-
271
- def full_name
272
- "#{object.first_name} #{object.last_name}"
273
- end
274
- end
275
56
  ```
276
-
277
- Within a serializer's methods, you can access the object being
278
- serialized as `object`.
279
-
280
- Since this shadows any attribute named `object`, you can include them through `object.object`. For example:
281
-
282
- ```ruby
283
- class VersionSerializer < ActiveModel::Serializer
284
- attribute :version_object, key: :object
285
-
286
- def version_object
287
- object.object
288
- end
289
- end
57
+ gem 'active_model_serializers', '~> 0.10.0'
290
58
  ```
291
59
 
292
- You can also access the `scope` method, which provides an
293
- authorization context to your serializer. By default, the context
294
- is the current user of your application, but this
295
- [can be customized](#customizing-scope).
296
-
297
- Serializers provide a method named `filter`, which should return an array
298
- used to determine what attributes and associations should be included in the output.
299
- This is typically used to customize output based on `current_user`. For example:
60
+ And then execute:
300
61
 
301
- ```ruby
302
- class PostSerializer < ActiveModel::Serializer
303
- attributes :id, :title, :body, :author
304
-
305
- def filter(keys)
306
- if scope.admin?
307
- keys
308
- else
309
- keys - [:author]
310
- end
311
- end
312
- end
313
62
  ```
314
-
315
- And it's also safe to mutate keys argument by doing keys.delete(:author)
316
- in case you want to avoid creating two extra arrays. Note that if you do an
317
- in-place modification, you still need to return the modified array.
318
-
319
- If you would like the key in the outputted JSON to be different from its name
320
- in ActiveRecord, you can declare the attribute with the different name
321
- and redefine that method:
322
-
323
- ```ruby
324
- class PostSerializer < ActiveModel::Serializer
325
- # look up subject on the model, but use title in the JSON
326
- def title
327
- object.subject
328
- end
329
-
330
- attributes :id, :body, :title
331
- has_many :comments
332
- end
63
+ $ bundle
333
64
  ```
334
65
 
335
- If you would like to add meta information to the outputted JSON, use the `:meta`
336
- option:
66
+ ## Getting Started
337
67
 
338
- ```ruby
339
- render json: @posts, serializer: CustomArraySerializer, meta: {total: 10}
340
- ```
68
+ See [Getting Started](docs/general/getting_started.md) for the nuts and bolts.
341
69
 
342
- The above usage of `:meta` will produce the following:
70
+ More information is available in the [Guides](docs) and
71
+ [High-level behavior](README.md#high-level-behavior).
343
72
 
344
- ```json
345
- {
346
- "meta": { "total": 10 },
347
- "posts": [
348
- { "title": "Post 1", "body": "Hello!" },
349
- { "title": "Post 2", "body": "Goodbye!" }
350
- ]
351
- }
352
- ```
73
+ ## Getting Help
353
74
 
354
- If you would like to change the meta key name you can use the `:meta_key` option:
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).
355
77
 
356
- ```ruby
357
- render json: @posts, serializer: CustomArraySerializer, meta: {total: 10}, meta_key: 'meta_object'
358
- ```
78
+ If you have a question, please [post to Stack Overflow](https://stackoverflow.com/questions/tagged/active-model-serializers).
359
79
 
360
- The above usage of `:meta_key` will produce the following:
80
+ If you'd like to chat, we have a [community slack](https://amserializers.herokuapp.com).
361
81
 
362
- ```json
363
- {
364
- "meta_object": { "total": 10 },
365
- "posts": [
366
- { "title": "Post 1", "body": "Hello!" },
367
- { "title": "Post 2", "body": "Goodbye!" }
368
- ]
369
- }
370
- ```
82
+ Thanks!
371
83
 
372
- When using meta information, your serializer cannot have the `{ root: false }` option, as this would lead to
373
- invalid JSON. If you do not have a root key, the meta information will be ignored.
84
+ ## Documentation
374
85
 
375
- If you would like direct, low-level control of attribute serialization, you can
376
- completely override the `attributes` method to return the hash you need:
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.
377
89
 
378
- ```ruby
379
- class PersonSerializer < ActiveModel::Serializer
380
- attributes :first_name, :last_name
381
-
382
- def attributes
383
- hash = super
384
- if scope.admin?
385
- hash["ssn"] = object.ssn
386
- hash["secret"] = object.mothers_maiden_name
387
- end
388
- hash
389
- end
390
- end
391
- ```
392
-
393
- ## Associations
394
-
395
- For specified associations, the serializer will look up the association and
396
- then serialize each element of the association. For instance, a `has_many
397
- :comments` association will create a new `CommentSerializer` for each comment
398
- and use it to serialize the comment.
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
+ - [![API Docs](https://img.shields.io/badge/yard-docs-blue.svg)](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
+ - [![API Docs](https://img.shields.io/badge/yard-docs-blue.svg)](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
+ - [![API Docs](https://img.shields.io/badge/yard-docs-blue.svg)](https://www.rubydoc.info/gems/active_model_serializers/0.8.4)
399
98
 
400
- By default, serializers simply look up the association on the original object.
401
- You can customize this behavior by implementing a method with the name of the
402
- association and returning a different Array. Often, you will do this to
403
- customize the objects returned based on the current user (scope).
404
-
405
- ```ruby
406
- class PostSerializer < ActiveModel::Serializer
407
- attributes :id, :title, :body
408
- has_many :comments
409
-
410
- # only let the user see comments he created.
411
- def comments
412
- object.comments.where(created_by: scope)
413
- end
414
- end
415
- ```
416
99
 
417
- As with attributes, you can change the JSON key that the serializer should
418
- use for a particular association.
100
+ ## High-level behavior
419
101
 
420
- ```ruby
421
- class PostSerializer < ActiveModel::Serializer
422
- attributes :id, :title, :body
102
+ Choose an adapter from [adapters](lib/active_model_serializers/adapter):
423
103
 
424
- # look up comments, but use +my_comments+ as the key in JSON
425
- has_many :comments, root: :my_comments
426
- end
104
+ ``` ruby
105
+ ActiveModelSerializers.config.adapter = :json_api # Default: `:attributes`
427
106
  ```
428
107
 
429
- Also, as with attributes, serializers will execute a filter method to
430
- determine which associations should be included in the output. For
431
- example:
108
+ Given a [serializable model](lib/active_model/serializer/lint.rb):
432
109
 
433
110
  ```ruby
434
- class PostSerializer < ActiveModel::Serializer
435
- attributes :id, :title, :body
436
- has_many :comments
437
-
438
- def filter(keys)
439
- keys.delete :comments if object.comments_disabled?
440
- keys
441
- end
111
+ # either
112
+ class SomeResource < ActiveRecord::Base
113
+ # columns: title, body
442
114
  end
443
- ```
444
-
445
- Or ...
446
-
447
- ```ruby
448
- class PostSerializer < ActiveModel::Serializer
449
- attributes :id, :title, :body
450
- has_one :author
451
- has_many :comments
452
-
453
- def filter(keys)
454
- keys.delete :author unless scope.admin?
455
- keys.delete :comments if object.comments_disabled?
456
- keys
457
- end
115
+ # or
116
+ class SomeResource < ActiveModelSerializers::Model
117
+ attributes :title, :body
458
118
  end
459
119
  ```
460
120
 
461
- 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.:
121
+ And initialized as:
462
122
 
463
123
  ```ruby
464
- has_many :comments, serializer: CommentShortSerializer
465
- has_one :reviewer, polymorphic: true
466
- ```
467
-
468
- Serializers are only concerned with multiplicity, and not ownership. `belongs_to` ActiveRecord associations can be included using `has_one` in your serializer.
469
-
470
- NOTE: polymorphic was removed because was only supported for has\_one
471
- associations and is in the TODO list of the project.
472
-
473
- ## Embedding Associations
474
-
475
- By default, associations will be embedded inside the serialized object. So if
476
- you have a post, the outputted JSON will look like:
477
-
478
- ```json
479
- {
480
- "post": {
481
- "id": 1,
482
- "title": "New post",
483
- "body": "A body!",
484
- "comments": [
485
- { "id": 1, "body": "what a dumb post" }
486
- ]
487
- }
488
- }
124
+ resource = SomeResource.new(title: 'ActiveModelSerializers', body: 'Convention over configuration')
489
125
  ```
490
126
 
491
- This is convenient for simple use-cases, but for more complex clients, it is
492
- better to supply an Array of IDs for the association. This makes your API more
493
- flexible from a performance standpoint and avoids wasteful duplication.
494
-
495
- To embed IDs instead of associations, simply use the `embed` class method:
127
+ Given a serializer for the serializable model:
496
128
 
497
129
  ```ruby
498
- class PostSerializer < ActiveModel::Serializer
499
- embed :ids
500
-
501
- attributes :id, :title, :body
502
- has_many :comments
130
+ class SomeSerializer < ActiveModel::Serializer
131
+ attribute :title, key: :name
132
+ attributes :body
503
133
  end
504
134
  ```
505
135
 
506
- Now, any associations will be supplied as an Array of IDs:
507
-
508
- ```json
509
- {
510
- "post": {
511
- "id": 1,
512
- "title": "New post",
513
- "body": "A body!",
514
- "comment_ids": [ 1, 2, 3 ]
515
- }
516
- }
517
- ```
518
-
519
- You may also choose to embed the IDs by the association's name underneath an
520
- `embed_key` for the resource. For example, say we want to change `comment_ids`
521
- to `comments` underneath a `links` key:
136
+ The model can be serialized as:
522
137
 
523
138
  ```ruby
524
- class PostSerializer < ActiveModel::Serializer
525
- attributes :id, :title, :body
526
-
527
- has_many :comments, embed: ids, embed_namespace: :links
528
- end
139
+ options = {}
140
+ serialization = ActiveModelSerializers::SerializableResource.new(resource, options)
141
+ serialization.to_json
142
+ serialization.as_json
529
143
  ```
530
144
 
531
- The JSON will look like this:
532
-
533
- ```json
534
- {
535
- "post": {
536
- "id": 1,
537
- "title": "New post",
538
- "body": "A body!",
539
- "links": {
540
- "comments": [ 1, 2, 3 ]
541
- }
542
- }
543
- }
544
- ```
545
-
546
- Alternatively, you can choose to embed only the ids or the associated objects per association:
145
+ SerializableResource delegates to the adapter, which it builds as:
547
146
 
548
147
  ```ruby
549
- class PostSerializer < ActiveModel::Serializer
550
- attributes :id, :title, :body
551
-
552
- has_many :comments, embed: :objects
553
- has_many :tags, embed: :ids
554
- end
148
+ adapter_options = {}
149
+ adapter = ActiveModelSerializers::Adapter.create(serializer, adapter_options)
150
+ adapter.to_json
151
+ adapter.as_json
152
+ adapter.serializable_hash
555
153
  ```
556
154
 
557
- The JSON will look like this:
558
-
559
- ```json
560
- {
561
- "post": {
562
- "id": 1,
563
- "title": "New post",
564
- "body": "A body!",
565
- "comments": [
566
- { "id": 1, "body": "what a dumb post" }
567
- ],
568
- "tag_ids": [ 1, 2, 3 ]
569
- }
570
- }
571
- ```
572
-
573
- In addition to supplying an Array of IDs, you may want to side-load the data
574
- alongside the main object. This makes it easier to process the entire package
575
- of data without having to recursively scan the tree looking for embedded
576
- information. It also ensures that associations that are shared between several
577
- objects (like tags), are only delivered once for the entire payload.
578
-
579
- You can specify that the data be included like this:
155
+ The adapter formats the serializer's attributes and associations (a.k.a. includes):
580
156
 
581
157
  ```ruby
582
- class PostSerializer < ActiveModel::Serializer
583
- embed :ids, include: true
584
-
585
- attributes :id, :title, :body
586
- has_many :comments
587
- end
158
+ serializer_options = {}
159
+ serializer = SomeSerializer.new(resource, serializer_options)
160
+ serializer.attributes
161
+ serializer.associations
588
162
  ```
589
163
 
590
- Assuming that the comments also `has_many :tags`, you will get a JSON like
591
- this:
592
-
593
- ```json
594
- {
595
- "post": {
596
- "id": 1,
597
- "title": "New post",
598
- "body": "A body!",
599
- "comment_ids": [ 1, 2 ]
600
- },
601
- "comments": [
602
- { "id": 1, "body": "what a dumb post", "tag_ids": [ 1, 2 ] },
603
- { "id": 2, "body": "i liked it", "tag_ids": [ 1, 3 ] },
604
- ],
605
- "tags": [
606
- { "id": 1, "name": "short" },
607
- { "id": 2, "name": "whiny" },
608
- { "id": 3, "name": "happy" }
609
- ]
610
- }
611
- ```
164
+ ## Architecture
612
165
 
613
- If you would like to namespace association JSON underneath a certain key in
614
- the root document (say, `linked`), you can specify an `embed_in_root_key`:
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).
615
169
 
616
- ```ruby
617
- class PostSerializer < ActiveModel::Serializer
618
- embed: ids, include: true, embed_in_root_key: :linked
170
+ The original design is also available [here](https://github.com/rails-api/active_model_serializers/blob/d72b66d4c5355b0ff0a75a04895fcc4ea5b0c65e/README.textile).
619
171
 
620
- attributes: :id, :title, :body
621
- has_many :comments, :tags
622
- end
623
- ```
172
+ ### ActiveModel::Serializer
624
173
 
625
- The above would yield the following JSON document:
626
-
627
- ```json
628
- {
629
- "post": {
630
- "id": 1,
631
- "title": "New post",
632
- "body": "A body!",
633
- "comment_ids": [ 1, 2 ]
634
- },
635
- "linked": {
636
- "comments": [
637
- { "id": 1, "body": "what a dumb post", "tag_ids": [ 1, 2 ] },
638
- { "id": 2, "body": "i liked it", "tag_ids": [ 1, 3 ] },
639
- ],
640
- "tags": [
641
- { "id": 1, "name": "short" },
642
- { "id": 2, "name": "whiny" },
643
- { "id": 3, "name": "happy" }
644
- ]
645
- }
646
- }
647
- ```
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).
648
180
 
649
- When side-loading data, your serializer cannot have the `{ root: false }` option,
650
- as this would lead to invalid JSON. If you do not have a root key, the `include`
651
- instruction will be ignored
181
+ #### ActiveModel::CollectionSerializer
652
182
 
653
- You can also specify a different root for the embedded objects than the key
654
- used to reference them:
183
+ The **`ActiveModel::CollectionSerializer`** represents a collection of resources as serializers
184
+ and, if there is no serializer, primitives.
655
185
 
656
- ```ruby
657
- class PostSerializer < ActiveModel::Serializer
658
- embed :ids, include: true
659
-
660
- attributes :id, :title, :body
661
- has_many :comments, key: :comment_ids, root: :comment_objects
662
- end
663
- ```
664
-
665
- This would generate JSON that would look like this:
666
-
667
- ```json
668
- {
669
- "post": {
670
- "id": 1,
671
- "title": "New post",
672
- "body": "A body!",
673
- "comment_ids": [ 1 ]
674
- },
675
- "comment_objects": [
676
- { "id": 1, "body": "what a dumb post" }
677
- ]
678
- }
679
- ```
680
-
681
- You can also specify a different attribute to use rather than the ID of the
682
- objects:
186
+ ### ActiveModelSerializers::Adapter::Base
683
187
 
684
- ```ruby
685
- class PostSerializer < ActiveModel::Serializer
686
- embed :ids, include: true
687
-
688
- attributes :id, :title, :body
689
- has_many :comments, embed_key: :external_id
690
- end
691
- ```
692
-
693
- This would generate JSON that would look like this:
694
-
695
- ```json
696
- {
697
- "post": {
698
- "id": 1,
699
- "title": "New post",
700
- "body": "A body!",
701
- "comment_ids": [ "COMM001" ]
702
- },
703
- "comments": [
704
- { "id": 1, "external_id": "COMM001", "body": "what a dumb post" }
705
- ]
706
- }
707
- ```
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.
708
192
 
709
- **NOTE**: The `embed :ids` mechanism is primary useful for clients that process
710
- data in bulk and load it into a local store. For these clients, the ability to
711
- easily see all of the data per type, rather than having to recursively scan the
712
- data looking for information, is extremely useful.
193
+ ### ActiveModelSerializers::SerializableResource
713
194
 
714
- If you are mostly working with the data in simple scenarios and manually making
715
- Ajax requests, you probably just want to use the default embedded behavior.
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.
716
199
 
717
- ## Customizing Scope
200
+ ### Primitive handling
718
201
 
719
- In a serializer, `current_user` is the current authorization scope which the controller
720
- provides to the serializer when you call `render :json`. By default, this is
721
- `current_user`, but can be customized in your controller by calling
722
- `serialization_scope`:
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.)
723
205
 
724
- ```ruby
725
- class ApplicationController < ActionController::Base
726
- serialization_scope :current_admin
727
- end
728
- ```
206
+ - ActiveModelSerializers doesn't handle primitives passed to `render json:` at all.
729
207
 
730
- The above example will also change the scope from `current_user` to
731
- `current_admin`.
208
+ Internally, if no serializer can be found in the controller, the resource is not decorated by
209
+ ActiveModelSerializers.
732
210
 
733
- Please note that, until now, `serialization_scope` doesn't accept a second
734
- object with options for specifying which actions should or should not take a
735
- given scope in consideration.
211
+ - However, when a primitive value is an attribute or in a collection, it is not modified.
736
212
 
737
- To be clear, it's not possible, yet, to do something like this:
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:
738
216
 
739
217
  ```ruby
740
- class SomeController < ApplicationController
741
- serialization_scope :current_admin, except: [:index, :show]
742
- end
218
+ reflection_options[:virtual_value] = association_value.try(:as_json) || association_value
743
219
  ```
744
220
 
745
- So, in order to have a fine grained control of what each action should take in
746
- consideration for its scope, you may use something like this:
221
+ (which is called by the adapter as `serializer.associations(*)`.)
747
222
 
748
- ```ruby
749
- class CitiesController < ApplicationController
750
- serialization_scope nil
751
-
752
- def index
753
- @cities = City.all
223
+ ### How options are parsed
754
224
 
755
- render json: @cities, each_serializer: CitySerializer
756
- end
225
+ High-level overview:
757
226
 
758
- def show
759
- @city = City.find(params[:id])
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.
760
234
 
761
- render json: @city, scope: current_admin
762
- end
763
- end
764
- ```
235
+ Details:
765
236
 
766
- Assuming that the `current_admin` method needs to make a query in the database
767
- for the current user, the advantage of this approach is that, by setting
768
- `serialization_scope` to `nil`, the `index` action no longer will need to make
769
- that query, only the `show` action will.
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.
770
260
 
771
- ## Testing
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.)
772
264
 
773
- In order to test a Serializer, you can just call `.new` on it, passing the object to serialize:
265
+ ### What does a 'serializable resource' look like?
774
266
 
775
- ### MiniTest
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).
776
271
 
777
- ```ruby
778
- class TestPostSerializer < Minitest::Test
779
- def setup
780
- @serializer = PostSerializer.new Post.new(id: 123, title: 'some title', body: 'some text')
781
- end
782
-
783
- def test_special_json_for_api
784
- assert_equal '{"post":{"id":123,"title":"some title","body":"some text"}}', @serializer.to_json
785
- end
786
- ```
272
+ ActiveModelSerializers provides a
273
+ [`ActiveModelSerializers::Model`](lib/active_model_serializers/model.rb),
274
+ which is a simple serializable PORO (Plain-Old Ruby Object).
787
275
 
788
- ### RSpec
276
+ `ActiveModelSerializers::Model` may be used either as a reference implementation, or in production code.
789
277
 
790
278
  ```ruby
791
- describe PostSerializer do
792
- it "creates special JSON for the API" do
793
- serializer = PostSerializer.new Post.new(id: 123, title: 'some title', body: 'some text')
794
- expect(serializer.to_json).to eql('{"post":{"id":123,"title":"some title","body":"some text"}}')
795
- end
279
+ class MyModel < ActiveModelSerializers::Model
280
+ attributes :id, :name, :level
796
281
  end
797
282
  ```
798
283
 
799
- ## Caching
800
-
801
- NOTE: This functionality was removed from AMS and it's in the TODO list.
802
- We need to re-think and re-design the caching strategy for the next
803
- version of AMS.
284
+ The default serializer for `MyModel` would be `MyModelSerializer` whether MyModel is an
285
+ ActiveRecord::Base object or not.
804
286
 
805
- To cache a serializer, call `cached` and define a `cache_key` method:
287
+ Outside of the controller the rules are **exactly** the same as for records. For example:
806
288
 
807
289
  ```ruby
808
- class PostSerializer < ActiveModel::Serializer
809
- cached # enables caching for this serializer
810
-
811
- attributes :title, :body
812
-
813
- def cache_key
814
- [object, scope]
815
- end
816
- end
290
+ render json: MyModel.new(level: 'awesome'), adapter: :json
817
291
  ```
818
292
 
819
- The caching interface uses `Rails.cache` under the hood.
820
-
821
- # ApplicationSerializer
822
-
823
- By default, new serializers descend from ActiveModel::Serializer. However, if you wish to share behaviour across your serializers you can create an ApplicationSerializer at ```app/serializers/application_serializer.rb```:
293
+ would be serialized the same as
824
294
 
825
295
  ```ruby
826
- class ApplicationSerializer < ActiveModel::Serializer
827
- end
828
- ```
829
-
830
- Any newly generated serializers will automatically descend from ApplicationSerializer.
831
-
296
+ ActiveModelSerializers::SerializableResource.new(MyModel.new(level: 'awesome'), adapter: :json).as_json
832
297
  ```
833
- $ rails g serializer post
834
- ```
835
-
836
- now generates:
837
-
838
- ```ruby
839
- class PostSerializer < ApplicationSerializer
840
- attributes :id
841
- end
842
- ````
843
-
844
- # Design and Implementation Guidelines
845
298
 
846
- ## Keep it Simple
299
+ ## Semantic Versioning
847
300
 
848
- `ActiveModel::Serializers` is capable of producing complex JSON views/large object
849
- trees, and it may be tempting to design in this way so that your client can make
850
- fewer requests to get data and so that related querying can be optimized.
851
- However, keeping things simple in your serializers and controllers may
852
- significantly reduce complexity and maintenance over the long-term development
853
- of your application. Please consider reducing the complexity of the JSON views
854
- you provide via the serializers as you build out your application, so that
855
- controllers/services can be more easily reused without a lot of complexity
856
- later.
301
+ This project adheres to [semver](https://semver.org/)
857
302
 
858
- ## Performance
303
+ ## Contributing
859
304
 
860
- As you develop your controllers or other code that utilizes serializers, try to
861
- avoid n+1 queries by ensuring that data loads in an optimal fashion, e.g. if you
862
- are using ActiveRecord, you might want to use query includes or joins as needed
863
- to make the data available that the serializer(s) need.
305
+ See [CONTRIBUTING.md](CONTRIBUTING.md)