active_model_serializers 0.8.3 → 0.9.0

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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -5
  3. data/CONTRIBUTING.md +20 -0
  4. data/DESIGN.textile +4 -4
  5. data/{MIT-LICENSE.txt → MIT-LICENSE} +0 -0
  6. data/README.md +307 -99
  7. data/lib/action_controller/serialization.rb +35 -16
  8. data/lib/action_controller/serialization_test_case.rb +79 -0
  9. data/lib/active_model/array_serializer.rb +40 -79
  10. data/lib/active_model/default_serializer.rb +32 -0
  11. data/lib/active_model/serializable.rb +40 -0
  12. data/lib/active_model/serializer/associations.rb +71 -202
  13. data/lib/active_model/serializer/config.rb +31 -0
  14. data/lib/active_model/serializer/generators/resource_override.rb +13 -0
  15. data/lib/{generators → active_model/serializer/generators}/serializer/USAGE +0 -0
  16. data/lib/active_model/serializer/generators/serializer/scaffold_controller_generator.rb +14 -0
  17. data/lib/active_model/serializer/generators/serializer/serializer_generator.rb +37 -0
  18. data/lib/active_model/serializer/generators/serializer/templates/controller.rb +93 -0
  19. data/lib/active_model/serializer/generators/serializer/templates/serializer.rb +8 -0
  20. data/lib/active_model/serializer/railtie.rb +10 -0
  21. data/lib/active_model/{serializers → serializer}/version.rb +1 -1
  22. data/lib/active_model/serializer.rb +186 -433
  23. data/lib/active_model/serializer_support.rb +5 -0
  24. data/lib/active_model_serializers.rb +13 -88
  25. data/test/fixtures/active_record.rb +92 -0
  26. data/test/fixtures/poro.rb +75 -0
  27. data/test/integration/action_controller/serialization_test.rb +287 -0
  28. data/test/integration/action_controller/serialization_test_case_test.rb +61 -0
  29. data/test/integration/active_record/active_record_test.rb +77 -0
  30. data/test/integration/generators/resource_generator_test.rb +26 -0
  31. data/test/integration/generators/scaffold_controller_generator_test.rb +64 -0
  32. data/test/integration/generators/serializer_generator_test.rb +41 -0
  33. data/test/test_app.rb +11 -0
  34. data/test/test_helper.rb +10 -18
  35. data/test/unit/active_model/array_serializer/except_test.rb +18 -0
  36. data/test/unit/active_model/array_serializer/key_format_test.rb +18 -0
  37. data/test/unit/active_model/array_serializer/meta_test.rb +53 -0
  38. data/test/unit/active_model/array_serializer/only_test.rb +18 -0
  39. data/test/unit/active_model/array_serializer/root_test.rb +102 -0
  40. data/test/unit/active_model/array_serializer/scope_test.rb +24 -0
  41. data/test/unit/active_model/array_serializer/serialization_test.rb +199 -0
  42. data/test/unit/active_model/default_serializer_test.rb +13 -0
  43. data/test/unit/active_model/serializer/associations/build_serializer_test.rb +21 -0
  44. data/test/unit/active_model/serializer/associations_test.rb +19 -0
  45. data/test/unit/active_model/serializer/attributes_test.rb +41 -0
  46. data/test/unit/active_model/serializer/config_test.rb +88 -0
  47. data/test/unit/active_model/serializer/filter_test.rb +69 -0
  48. data/test/unit/active_model/serializer/has_many_test.rb +230 -0
  49. data/test/unit/active_model/serializer/has_one_test.rb +207 -0
  50. data/test/unit/active_model/serializer/key_format_test.rb +25 -0
  51. data/test/unit/active_model/serializer/meta_test.rb +39 -0
  52. data/test/unit/active_model/serializer/options_test.rb +15 -0
  53. data/test/unit/active_model/serializer/root_test.rb +117 -0
  54. data/test/unit/active_model/serializer/scope_test.rb +49 -0
  55. metadata +86 -62
  56. data/.gitignore +0 -18
  57. data/.travis.yml +0 -28
  58. data/Gemfile +0 -4
  59. data/Gemfile.edge +0 -9
  60. data/Rakefile +0 -18
  61. data/active_model_serializers.gemspec +0 -24
  62. data/bench/perf.rb +0 -43
  63. data/cruft.md +0 -19
  64. data/lib/active_record/serializer_override.rb +0 -16
  65. data/lib/generators/resource_override.rb +0 -13
  66. data/lib/generators/serializer/serializer_generator.rb +0 -42
  67. data/lib/generators/serializer/templates/serializer.rb +0 -19
  68. data/test/array_serializer_test.rb +0 -75
  69. data/test/association_test.rb +0 -592
  70. data/test/caching_test.rb +0 -96
  71. data/test/generators_test.rb +0 -85
  72. data/test/no_serialization_scope_test.rb +0 -34
  73. data/test/serialization_scope_name_test.rb +0 -67
  74. data/test/serialization_test.rb +0 -392
  75. data/test/serializer_support_test.rb +0 -51
  76. data/test/serializer_test.rb +0 -1465
  77. data/test/test_fakes.rb +0 -217
data/README.md CHANGED
@@ -1,10 +1,13 @@
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)
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)
2
3
 
3
- # Purpose
4
+ # ActiveModel::Serializers
4
5
 
5
- The purpose of `ActiveModel::Serializers` is to provide an object to
6
- encapsulate serialization of `ActiveModel` objects, including `ActiveRecord`
7
- objects.
6
+ ## Purpose
7
+
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.
8
11
 
9
12
  Serializers know about both a model and the `current_user`, so you can
10
13
  customize serialization based upon whether a user is authorized to see the
@@ -13,13 +16,13 @@ content.
13
16
  In short, **serializers replace hash-driven development with object-oriented
14
17
  development.**
15
18
 
16
- # Installing Serializers
19
+ # Installing
17
20
 
18
21
  The easiest way to install `ActiveModel::Serializers` is to add it to your
19
22
  `Gemfile`:
20
23
 
21
24
  ```ruby
22
- gem "active_model_serializers", "~> 0.8.0"
25
+ gem "active_model_serializers"
23
26
  ```
24
27
 
25
28
  Then, install it on the command line:
@@ -28,6 +31,16 @@ Then, install it on the command line:
28
31
  $ bundle install
29
32
  ```
30
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
+
31
44
  # Creating a Serializer
32
45
 
33
46
  The easiest way to create a new serializer is to generate a new resource, which
@@ -45,21 +58,13 @@ the serializer generator:
45
58
  $ rails g serializer post
46
59
  ```
47
60
 
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::ArraySerializerSupport` into your ORM's
58
- relation/criteria class.
61
+ ### Support for POROs
59
62
 
60
- # ActiveModel::Serializer
63
+ The PORO should include ActiveModel::SerializerSupport. That's all you need to
64
+ do to have your POROs supported.
61
65
 
62
- All new serializers descend from ActiveModel::Serializer
66
+ For Rails versions before Rails 4 ActiveModel::Serializers expects objects to
67
+ implement `read_attribute_for_serialization`.
63
68
 
64
69
  # render :json
65
70
 
@@ -70,7 +75,7 @@ for a serializer for the object and use it if available.
70
75
  class PostsController < ApplicationController
71
76
  def show
72
77
  @post = Post.find(params[:id])
73
- render :json => @post
78
+ render json: @post
74
79
  end
75
80
  end
76
81
  ```
@@ -82,22 +87,11 @@ This also works with `respond_with`, which uses `to_json` under the hood. Also
82
87
  note that any options passed to `render :json` will be passed to your
83
88
  serializer and available as `@options` inside.
84
89
 
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
95
- ```
96
-
97
- #### 2. Specify the serializer when you render the object:
90
+ To specify a custom serializer for an object, you can specify the
91
+ serializer when you render the object:
98
92
 
99
93
  ```ruby
100
- render :json => @post, :serializer => FancyPostSerializer
94
+ render json: @post, serializer: FancyPostSerializer
101
95
  ```
102
96
 
103
97
  ## Arrays
@@ -114,7 +108,7 @@ end
114
108
  class PostsController < ApplicationController
115
109
  def index
116
110
  @posts = Post.all
117
- render :json => @posts
111
+ render json: @posts
118
112
  end
119
113
  end
120
114
  ```
@@ -135,7 +129,7 @@ By default, the root element is the name of the controller. For example, `PostsC
135
129
  generates a root element "posts". To change it:
136
130
 
137
131
  ```ruby
138
- render :json => @posts, :root => "some_posts"
132
+ render json: @posts, root: "some_posts"
139
133
  ```
140
134
 
141
135
  You may disable the root element for arrays at the top level, which will result in
@@ -152,9 +146,32 @@ root element of the array with any of those methods will produce
152
146
  To specify a custom serializer for the items within an array:
153
147
 
154
148
  ```ruby
155
- render :json => @posts, :each_serializer => FancyPostSerializer
149
+ render json: @posts, each_serializer: FancyPostSerializer
156
150
  ```
157
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
+
158
175
  ## Disabling the root element
159
176
 
160
177
  You have 4 options to disable the root element, each with a slightly different scope:
@@ -164,19 +181,17 @@ You have 4 options to disable the root element, each with a slightly different s
164
181
  In an initializer:
165
182
 
166
183
  ```ruby
167
- ActiveSupport.on_load(:active_model_serializers) do
168
- # Disable for all serializers (except ArraySerializer)
169
- ActiveModel::Serializer.root = false
170
-
171
- # Disable for ArraySerializer
172
- ActiveModel::ArraySerializer.root = false
173
- end
184
+ # Disable for all serializers (except ArraySerializer)
185
+ ActiveModel::Serializer.root = false
186
+
187
+ # Disable for ArraySerializer
188
+ ActiveModel::ArraySerializer.root = false
174
189
  ```
175
190
 
176
191
  #### 2. Disable root per render call in your controller
177
192
 
178
193
  ```ruby
179
- render :json => @posts, :root => false
194
+ render json: @posts, root: false
180
195
  ```
181
196
 
182
197
  #### 3. Subclass the serializer, and specify using it
@@ -187,7 +202,7 @@ class CustomArraySerializer < ActiveModel::ArraySerializer
187
202
  end
188
203
 
189
204
  # controller:
190
- render :json => @posts, :serializer => CustomArraySerializer
205
+ render json: @posts, serializer: CustomArraySerializer
191
206
  ```
192
207
 
193
208
  #### 4. Define default_serializer_options in your controller
@@ -204,10 +219,27 @@ def default_serializer_options
204
219
  end
205
220
  ```
206
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
+
207
239
  ## Getting the old version
208
240
 
209
241
  If you find that your project is already relying on the old rails to_json
210
- change `render :json` to `render :json => @your_object.to_json`.
242
+ change `render :json` to `render json: @your_object.to_json`.
211
243
 
212
244
  # Attributes and Associations
213
245
 
@@ -245,49 +277,57 @@ end
245
277
  Within a serializer's methods, you can access the object being
246
278
  serialized as `object`.
247
279
 
248
- You can also access the `current_user` method, which provides an
249
- authorization context to your serializer. By default, the context
250
- is the current user of your application, but this
251
- [can be customized](#customizing-scope).
252
-
253
- Serializers will check for the presence of a method named
254
- `include_[ATTRIBUTE]?` to determine whether a particular attribute should be
255
- included in the output. This is typically used to customize output
256
- based on `current_user`. For example:
280
+ Since this shadows any attribute named `object`, you can include them through `object.object`. For example:
257
281
 
258
282
  ```ruby
259
- class PostSerializer < ActiveModel::Serializer
260
- attributes :id, :title, :body, :author
283
+ class VersionSerializer < ActiveModel::Serializer
284
+ attribute :version_object, key: :object
261
285
 
262
- def include_author?
263
- current_user.admin?
286
+ def version_object
287
+ object.object
264
288
  end
265
289
  end
266
290
  ```
267
291
 
268
- The type of a computed attribute (like :full_name above) is not easily
269
- calculated without some sophisticated static code analysis. To specify the
270
- type of a computed attribute:
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:
271
300
 
272
301
  ```ruby
273
- class PersonSerializer < ActiveModel::Serializer
274
- attributes :first_name, :last_name, {:full_name => :string}
302
+ class PostSerializer < ActiveModel::Serializer
303
+ attributes :id, :title, :body, :author
275
304
 
276
- def full_name
277
- "#{object.first_name} #{object.last_name}"
305
+ def filter(keys)
306
+ if scope.admin?
307
+ keys
308
+ else
309
+ keys - [:author]
310
+ end
278
311
  end
279
312
  end
280
313
  ```
281
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
+
282
319
  If you would like the key in the outputted JSON to be different from its name
283
- in ActiveRecord, you can use the `:key` option to customize it:
320
+ in ActiveRecord, you can declare the attribute with the different name
321
+ and redefine that method:
284
322
 
285
323
  ```ruby
286
324
  class PostSerializer < ActiveModel::Serializer
287
- attributes :id, :body
325
+ # look up subject on the model, but use title in the JSON
326
+ def title
327
+ object.subject
328
+ end
288
329
 
289
- # look up :subject on the model, but use +title+ in the JSON
290
- attribute :subject, :key => :title
330
+ attributes :id, :body, :title
291
331
  has_many :comments
292
332
  end
293
333
  ```
@@ -296,7 +336,7 @@ If you would like to add meta information to the outputted JSON, use the `:meta`
296
336
  option:
297
337
 
298
338
  ```ruby
299
- render :json => @posts, :serializer => CustomArraySerializer, :meta => {:total => 10}
339
+ render json: @posts, serializer: CustomArraySerializer, meta: {total: 10}
300
340
  ```
301
341
 
302
342
  The above usage of `:meta` will produce the following:
@@ -314,7 +354,7 @@ The above usage of `:meta` will produce the following:
314
354
  If you would like to change the meta key name you can use the `:meta_key` option:
315
355
 
316
356
  ```ruby
317
- render :json => @posts, :serializer => CustomArraySerializer, :meta => {:total => 10}, :meta_key => 'meta_object'
357
+ render json: @posts, serializer: CustomArraySerializer, meta: {total: 10}, meta_key: 'meta_object'
318
358
  ```
319
359
 
320
360
  The above usage of `:meta_key` will produce the following:
@@ -329,6 +369,9 @@ The above usage of `:meta_key` will produce the following:
329
369
  }
330
370
  ```
331
371
 
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.
374
+
332
375
  If you would like direct, low-level control of attribute serialization, you can
333
376
  completely override the `attributes` method to return the hash you need:
334
377
 
@@ -338,7 +381,7 @@ class PersonSerializer < ActiveModel::Serializer
338
381
 
339
382
  def attributes
340
383
  hash = super
341
- if current_user.admin?
384
+ if scope.admin?
342
385
  hash["ssn"] = object.ssn
343
386
  hash["secret"] = object.mothers_maiden_name
344
387
  end
@@ -357,7 +400,7 @@ and use it to serialize the comment.
357
400
  By default, serializers simply look up the association on the original object.
358
401
  You can customize this behavior by implementing a method with the name of the
359
402
  association and returning a different Array. Often, you will do this to
360
- customize the objects returned based on the current user.
403
+ customize the objects returned based on the current user (scope).
361
404
 
362
405
  ```ruby
363
406
  class PostSerializer < ActiveModel::Serializer
@@ -366,7 +409,7 @@ class PostSerializer < ActiveModel::Serializer
366
409
 
367
410
  # only let the user see comments he created.
368
411
  def comments
369
- object.comments.where(:created_by => current_user)
412
+ object.comments.where(created_by: scope)
370
413
  end
371
414
  end
372
415
  ```
@@ -379,27 +422,27 @@ class PostSerializer < ActiveModel::Serializer
379
422
  attributes :id, :title, :body
380
423
 
381
424
  # look up comments, but use +my_comments+ as the key in JSON
382
- has_many :comments, :key => :my_comments
425
+ has_many :comments, root: :my_comments
383
426
  end
384
427
  ```
385
428
 
386
- Also, as with attributes, serializers will check for the presence
387
- of a method named `include_[ASSOCIATION]?` to determine whether a particular association
388
- should be included in the output. For example:
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:
389
432
 
390
433
  ```ruby
391
434
  class PostSerializer < ActiveModel::Serializer
392
435
  attributes :id, :title, :body
393
436
  has_many :comments
394
437
 
395
- def include_comments?
396
- !object.comments_disabled?
438
+ def filter(keys)
439
+ keys.delete :comments if object.comments_disabled?
440
+ keys
397
441
  end
398
442
  end
399
443
  ```
400
444
 
401
- If you would like lower-level control of association serialization, you can
402
- override `include_associations!` to specify which associations should be included:
445
+ Or ...
403
446
 
404
447
  ```ruby
405
448
  class PostSerializer < ActiveModel::Serializer
@@ -407,9 +450,10 @@ class PostSerializer < ActiveModel::Serializer
407
450
  has_one :author
408
451
  has_many :comments
409
452
 
410
- def include_associations!
411
- include! :author if current_user.admin?
412
- include! :comments unless object.comments_disabled?
453
+ def filter(keys)
454
+ keys.delete :author unless scope.admin?
455
+ keys.delete :comments if object.comments_disabled?
456
+ keys
413
457
  end
414
458
  end
415
459
  ```
@@ -417,12 +461,15 @@ end
417
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.:
418
462
 
419
463
  ```ruby
420
- has_many :comments, :serializer => CommentShortSerializer
421
- has_one :reviewer, :polymorphic => true
464
+ has_many :comments, serializer: CommentShortSerializer
465
+ has_one :reviewer, polymorphic: true
422
466
  ```
423
467
 
424
468
  Serializers are only concerned with multiplicity, and not ownership. `belongs_to` ActiveRecord associations can be included using `has_one` in your serializer.
425
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
+
426
473
  ## Embedding Associations
427
474
 
428
475
  By default, associations will be embedded inside the serialized object. So if
@@ -469,6 +516,33 @@ Now, any associations will be supplied as an Array of IDs:
469
516
  }
470
517
  ```
471
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:
522
+
523
+ ```ruby
524
+ class PostSerializer < ActiveModel::Serializer
525
+ attributes :id, :title, :body
526
+
527
+ has_many :comments, embed: ids, embed_namespace: :links
528
+ end
529
+ ```
530
+
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
+
472
546
  Alternatively, you can choose to embed only the ids or the associated objects per association:
473
547
 
474
548
  ```ruby
@@ -506,7 +580,7 @@ You can specify that the data be included like this:
506
580
 
507
581
  ```ruby
508
582
  class PostSerializer < ActiveModel::Serializer
509
- embed :ids, :include => true
583
+ embed :ids, include: true
510
584
 
511
585
  attributes :id, :title, :body
512
586
  has_many :comments
@@ -536,15 +610,55 @@ this:
536
610
  }
537
611
  ```
538
612
 
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`:
615
+
616
+ ```ruby
617
+ class PostSerializer < ActiveModel::Serializer
618
+ embed: ids, include: true, embed_in_root_key: :linked
619
+
620
+ attributes: :id, :title, :body
621
+ has_many :comments, :tags
622
+ end
623
+ ```
624
+
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
+ ```
648
+
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
652
+
539
653
  You can also specify a different root for the embedded objects than the key
540
654
  used to reference them:
541
655
 
542
656
  ```ruby
543
657
  class PostSerializer < ActiveModel::Serializer
544
- embed :ids, :include => true
658
+ embed :ids, include: true
545
659
 
546
660
  attributes :id, :title, :body
547
- has_many :comments, :key => :comment_ids, :root => :comment_objects
661
+ has_many :comments, key: :comment_ids, root: :comment_objects
548
662
  end
549
663
  ```
550
664
 
@@ -569,10 +683,10 @@ objects:
569
683
 
570
684
  ```ruby
571
685
  class PostSerializer < ActiveModel::Serializer
572
- embed :ids, :include => true
686
+ embed :ids, include: true
573
687
 
574
688
  attributes :id, :title, :body
575
- has_many :comments, :embed_key => :external_id
689
+ has_many :comments, embed_key: :external_id
576
690
  end
577
691
  ```
578
692
 
@@ -613,7 +727,7 @@ class ApplicationController < ActionController::Base
613
727
  end
614
728
  ```
615
729
 
616
- The above example will also change the scope name from `current_user` to
730
+ The above example will also change the scope from `current_user` to
617
731
  `current_admin`.
618
732
 
619
733
  Please note that, until now, `serialization_scope` doesn't accept a second
@@ -624,7 +738,7 @@ To be clear, it's not possible, yet, to do something like this:
624
738
 
625
739
  ```ruby
626
740
  class SomeController < ApplicationController
627
- serialization_scope :current_admin, :except => [:index, :show]
741
+ serialization_scope :current_admin, except: [:index, :show]
628
742
  end
629
743
  ```
630
744
 
@@ -638,13 +752,13 @@ class CitiesController < ApplicationController
638
752
  def index
639
753
  @cities = City.all
640
754
 
641
- render :json => @cities, :each_serializer => CitySerializer
755
+ render json: @cities, each_serializer: CitySerializer
642
756
  end
643
757
 
644
758
  def show
645
759
  @city = City.find(params[:id])
646
760
 
647
- render :json => @city, :scope => current_admin, :scope_name => :current_admin
761
+ render json: @city, scope: current_admin
648
762
  end
649
763
  end
650
764
  ```
@@ -653,3 +767,97 @@ Assuming that the `current_admin` method needs to make a query in the database
653
767
  for the current user, the advantage of this approach is that, by setting
654
768
  `serialization_scope` to `nil`, the `index` action no longer will need to make
655
769
  that query, only the `show` action will.
770
+
771
+ ## Testing
772
+
773
+ In order to test a Serializer, you can just call `.new` on it, passing the object to serialize:
774
+
775
+ ### MiniTest
776
+
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
+ ```
787
+
788
+ ### RSpec
789
+
790
+ ```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
796
+ end
797
+ ```
798
+
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.
804
+
805
+ To cache a serializer, call `cached` and define a `cache_key` method:
806
+
807
+ ```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
817
+ ```
818
+
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```:
824
+
825
+ ```ruby
826
+ class ApplicationSerializer < ActiveModel::Serializer
827
+ end
828
+ ```
829
+
830
+ Any newly generated serializers will automatically descend from ApplicationSerializer.
831
+
832
+ ```
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
+
846
+ ## Keep it Simple
847
+
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.
857
+
858
+ ## Performance
859
+
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.