active_model_serializers 0.8.0 → 0.10.12

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