active_model_serializers 0.8.3 → 0.9.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +122 -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 +396 -95
  7. data/lib/action_controller/serialization.rb +50 -12
  8. data/lib/action_controller/serialization_test_case.rb +79 -0
  9. data/lib/active_model/array_serializer.rb +47 -78
  10. data/lib/active_model/default_serializer.rb +32 -0
  11. data/lib/active_model/serializable/utils.rb +16 -0
  12. data/lib/active_model/serializable.rb +62 -0
  13. data/lib/active_model/serializer/association/has_many.rb +39 -0
  14. data/lib/active_model/serializer/association/has_one.rb +25 -0
  15. data/lib/active_model/serializer/association.rb +58 -0
  16. data/lib/active_model/serializer/config.rb +31 -0
  17. data/lib/active_model/serializer/generators/resource_override.rb +13 -0
  18. data/lib/{generators → active_model/serializer/generators}/serializer/USAGE +0 -0
  19. data/lib/active_model/serializer/generators/serializer/scaffold_controller_generator.rb +14 -0
  20. data/lib/active_model/serializer/generators/serializer/serializer_generator.rb +37 -0
  21. data/lib/active_model/serializer/generators/serializer/templates/controller.rb +93 -0
  22. data/lib/active_model/serializer/generators/serializer/templates/serializer.rb +8 -0
  23. data/lib/active_model/serializer/railtie.rb +18 -0
  24. data/lib/active_model/{serializers → serializer}/version.rb +1 -1
  25. data/lib/active_model/serializer.rb +214 -423
  26. data/lib/active_model/serializer_support.rb +5 -0
  27. data/lib/active_model_serializers/mime_types.rb +14 -0
  28. data/lib/active_model_serializers.rb +12 -87
  29. data/test/fixtures/active_record.rb +96 -0
  30. data/test/fixtures/poro.rb +187 -0
  31. data/test/fixtures/template.html.erb +1 -0
  32. data/test/integration/action_controller/namespaced_serialization_test.rb +105 -0
  33. data/test/integration/action_controller/serialization_test.rb +287 -0
  34. data/test/integration/action_controller/serialization_test_case_test.rb +71 -0
  35. data/test/integration/active_record/active_record_test.rb +94 -0
  36. data/test/integration/generators/resource_generator_test.rb +26 -0
  37. data/test/integration/generators/scaffold_controller_generator_test.rb +64 -0
  38. data/test/integration/generators/serializer_generator_test.rb +41 -0
  39. data/test/test_app.rb +14 -0
  40. data/test/test_helper.rb +10 -18
  41. data/test/unit/active_model/array_serializer/except_test.rb +18 -0
  42. data/test/unit/active_model/array_serializer/key_format_test.rb +18 -0
  43. data/test/unit/active_model/array_serializer/meta_test.rb +53 -0
  44. data/test/unit/active_model/array_serializer/only_test.rb +18 -0
  45. data/test/unit/active_model/array_serializer/options_test.rb +16 -0
  46. data/test/unit/active_model/array_serializer/root_test.rb +102 -0
  47. data/test/unit/active_model/array_serializer/scope_test.rb +24 -0
  48. data/test/unit/active_model/array_serializer/serialization_test.rb +216 -0
  49. data/test/unit/active_model/default_serializer_test.rb +13 -0
  50. data/test/unit/active_model/serializer/associations/build_serializer_test.rb +36 -0
  51. data/test/unit/active_model/serializer/associations_test.rb +19 -0
  52. data/test/unit/active_model/serializer/attributes_test.rb +57 -0
  53. data/test/unit/active_model/serializer/config_test.rb +91 -0
  54. data/test/unit/active_model/serializer/filter_test.rb +69 -0
  55. data/test/unit/active_model/serializer/has_many_polymorphic_test.rb +189 -0
  56. data/test/unit/active_model/serializer/has_many_test.rb +265 -0
  57. data/test/unit/active_model/serializer/has_one_and_has_many_test.rb +27 -0
  58. data/test/unit/active_model/serializer/has_one_polymorphic_test.rb +196 -0
  59. data/test/unit/active_model/serializer/has_one_test.rb +253 -0
  60. data/test/unit/active_model/serializer/key_format_test.rb +25 -0
  61. data/test/unit/active_model/serializer/meta_test.rb +39 -0
  62. data/test/unit/active_model/serializer/options_test.rb +42 -0
  63. data/test/unit/active_model/serializer/root_test.rb +117 -0
  64. data/test/unit/active_model/serializer/scope_test.rb +49 -0
  65. data/test/unit/active_model/serializer/url_helpers_test.rb +35 -0
  66. metadata +107 -64
  67. data/.gitignore +0 -18
  68. data/.travis.yml +0 -28
  69. data/Gemfile +0 -4
  70. data/Gemfile.edge +0 -9
  71. data/Rakefile +0 -18
  72. data/active_model_serializers.gemspec +0 -24
  73. data/bench/perf.rb +0 -43
  74. data/cruft.md +0 -19
  75. data/lib/active_model/serializer/associations.rb +0 -233
  76. data/lib/active_record/serializer_override.rb +0 -16
  77. data/lib/generators/resource_override.rb +0 -13
  78. data/lib/generators/serializer/serializer_generator.rb +0 -42
  79. data/lib/generators/serializer/templates/serializer.rb +0 -19
  80. data/test/array_serializer_test.rb +0 -75
  81. data/test/association_test.rb +0 -592
  82. data/test/caching_test.rb +0 -96
  83. data/test/generators_test.rb +0 -85
  84. data/test/no_serialization_scope_test.rb +0 -34
  85. data/test/serialization_scope_name_test.rb +0 -67
  86. data/test/serialization_test.rb +0 -392
  87. data/test/serializer_support_test.rb +0 -51
  88. data/test/serializer_test.rb +0 -1465
  89. 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,22 @@ 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:
90
+ To specify a custom serializer for an object, you can specify the
91
+ serializer when you render the object:
88
92
 
89
93
  ```ruby
90
- class Post < ActiveRecord::Base
91
- def active_model_serializer
92
- FancyPostSerializer
93
- end
94
- end
94
+ render json: @post, serializer: FancyPostSerializer
95
95
  ```
96
96
 
97
- #### 2. Specify the serializer when you render the object:
97
+ ### Use serialization outside of ActionController::Base
98
+
99
+ When controller does not inherit from ActionController::Base,
100
+ include Serialization module manually:
98
101
 
99
102
  ```ruby
100
- render :json => @post, :serializer => FancyPostSerializer
103
+ class ApplicationController < ActionController::API
104
+ include ActionController::Serialization
105
+ end
101
106
  ```
102
107
 
103
108
  ## Arrays
@@ -114,7 +119,7 @@ end
114
119
  class PostsController < ApplicationController
115
120
  def index
116
121
  @posts = Post.all
117
- render :json => @posts
122
+ render json: @posts
118
123
  end
119
124
  end
120
125
  ```
@@ -135,7 +140,7 @@ By default, the root element is the name of the controller. For example, `PostsC
135
140
  generates a root element "posts". To change it:
136
141
 
137
142
  ```ruby
138
- render :json => @posts, :root => "some_posts"
143
+ render json: @posts, root: "some_posts"
139
144
  ```
140
145
 
141
146
  You may disable the root element for arrays at the top level, which will result in
@@ -152,9 +157,32 @@ root element of the array with any of those methods will produce
152
157
  To specify a custom serializer for the items within an array:
153
158
 
154
159
  ```ruby
155
- render :json => @posts, :each_serializer => FancyPostSerializer
160
+ render json: @posts, each_serializer: FancyPostSerializer
161
+ ```
162
+
163
+ ## Render independently
164
+
165
+ By default the setting of serializer is in controller as described above which is the
166
+ recommended way. However, there may be cases you need to render the json object elsewhere
167
+ say in a helper or a view when controller is only for main object.
168
+
169
+ Then you can render the serialized JSON independently.
170
+
171
+ ```ruby
172
+ def current_user_as_json_helper
173
+ CurrentUserSerializer.new(current_user).to_json
174
+ end
156
175
  ```
157
176
 
177
+ You can also render an array of objects using ArraySerializer.
178
+
179
+ ```ruby
180
+ def users_array_as_json_helper(users)
181
+ ActiveModel::ArraySerializer.new(users, each_serializer: UserSerializer).to_json
182
+ end
183
+ ```
184
+
185
+
158
186
  ## Disabling the root element
159
187
 
160
188
  You have 4 options to disable the root element, each with a slightly different scope:
@@ -164,19 +192,17 @@ You have 4 options to disable the root element, each with a slightly different s
164
192
  In an initializer:
165
193
 
166
194
  ```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
195
+ # Disable for all serializers (except ArraySerializer)
196
+ ActiveModel::Serializer.root = false
197
+
198
+ # Disable for ArraySerializer
199
+ ActiveModel::ArraySerializer.root = false
174
200
  ```
175
201
 
176
202
  #### 2. Disable root per render call in your controller
177
203
 
178
204
  ```ruby
179
- render :json => @posts, :root => false
205
+ render json: @posts, root: false
180
206
  ```
181
207
 
182
208
  #### 3. Subclass the serializer, and specify using it
@@ -187,7 +213,7 @@ class CustomArraySerializer < ActiveModel::ArraySerializer
187
213
  end
188
214
 
189
215
  # controller:
190
- render :json => @posts, :serializer => CustomArraySerializer
216
+ render json: @posts, serializer: CustomArraySerializer
191
217
  ```
192
218
 
193
219
  #### 4. Define default_serializer_options in your controller
@@ -204,10 +230,39 @@ def default_serializer_options
204
230
  end
205
231
  ```
206
232
 
233
+ ## Changing the Key Format
234
+
235
+ You can specify that serializers use the lower-camel key format at the config, class or instance level.
236
+
237
+ ```ruby
238
+
239
+ ActiveModel::Serializer.setup do |config|
240
+ config.key_format = :lower_camel
241
+ end
242
+
243
+ class BlogLowerCamelSerializer < ActiveModel::Serializer
244
+ format_keys :lower_camel
245
+ end
246
+
247
+ BlogSerializer.new(object, key_format: :lower_camel)
248
+ ```
249
+
250
+ ## Changing the default association key type
251
+
252
+ You can specify that serializers use unsuffixed names as association keys by default.
253
+
254
+ `````ruby
255
+ ActiveModel::Serializer.setup do |config|
256
+ config.default_key_type = :name
257
+ end
258
+ ````
259
+
260
+ This will build association keys like `comments` or `author` instead of `comment_ids` or `author_id`.
261
+
207
262
  ## Getting the old version
208
263
 
209
264
  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`.
265
+ change `render :json` to `render json: @your_object.to_json`.
211
266
 
212
267
  # Attributes and Associations
213
268
 
@@ -245,49 +300,58 @@ end
245
300
  Within a serializer's methods, you can access the object being
246
301
  serialized as `object`.
247
302
 
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:
303
+ Since this shadows any attribute named `object`, you can include them through `object.object`. For example:
257
304
 
258
305
  ```ruby
259
- class PostSerializer < ActiveModel::Serializer
260
- attributes :id, :title, :body, :author
306
+ class VersionSerializer < ActiveModel::Serializer
307
+ attributes :version_object
261
308
 
262
- def include_author?
263
- current_user.admin?
309
+ def version_object
310
+ object.object
264
311
  end
265
312
  end
266
313
  ```
267
314
 
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:
315
+ You can also access the `scope` method, which provides an
316
+ authorization context to your serializer. By default, the context
317
+ is the current user of your application, but this
318
+ [can be customized](#customizing-scope).
319
+
320
+ Serializers provide a method named `filter`, which should return an array
321
+ used to determine what attributes and associations should be included in the output.
322
+ This is typically used to customize output based on `current_user`. For example:
271
323
 
272
324
  ```ruby
273
- class PersonSerializer < ActiveModel::Serializer
274
- attributes :first_name, :last_name, {:full_name => :string}
325
+ class PostSerializer < ActiveModel::Serializer
326
+ attributes :id, :title, :body, :author
275
327
 
276
- def full_name
277
- "#{object.first_name} #{object.last_name}"
328
+ def filter(keys)
329
+ if scope.admin?
330
+ keys
331
+ else
332
+ keys - [:author]
333
+ end
278
334
  end
279
335
  end
280
336
  ```
281
337
 
338
+ And it's also safe to mutate keys argument by doing keys.delete(:author)
339
+ in case you want to avoid creating two extra arrays. Note that if you do an
340
+ in-place modification, you still need to return the modified array.
341
+
342
+ ### Alias Attribute
282
343
  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:
344
+ in ActiveRecord, you can declare the attribute with the different name
345
+ and redefine that method:
284
346
 
285
347
  ```ruby
286
348
  class PostSerializer < ActiveModel::Serializer
287
- attributes :id, :body
349
+ # look up subject on the model, but use title in the JSON
350
+ def title
351
+ object.subject
352
+ end
288
353
 
289
- # look up :subject on the model, but use +title+ in the JSON
290
- attribute :subject, :key => :title
354
+ attributes :id, :body, :title
291
355
  has_many :comments
292
356
  end
293
357
  ```
@@ -296,7 +360,7 @@ If you would like to add meta information to the outputted JSON, use the `:meta`
296
360
  option:
297
361
 
298
362
  ```ruby
299
- render :json => @posts, :serializer => CustomArraySerializer, :meta => {:total => 10}
363
+ render json: @posts, serializer: CustomArraySerializer, meta: {total: 10}
300
364
  ```
301
365
 
302
366
  The above usage of `:meta` will produce the following:
@@ -314,7 +378,7 @@ The above usage of `:meta` will produce the following:
314
378
  If you would like to change the meta key name you can use the `:meta_key` option:
315
379
 
316
380
  ```ruby
317
- render :json => @posts, :serializer => CustomArraySerializer, :meta => {:total => 10}, :meta_key => 'meta_object'
381
+ render json: @posts, serializer: CustomArraySerializer, meta_object: {total: 10}, meta_key: 'meta_object'
318
382
  ```
319
383
 
320
384
  The above usage of `:meta_key` will produce the following:
@@ -329,6 +393,9 @@ The above usage of `:meta_key` will produce the following:
329
393
  }
330
394
  ```
331
395
 
396
+ When using meta information, your serializer cannot have the `{ root: false }` option, as this would lead to
397
+ invalid JSON. If you do not have a root key, the meta information will be ignored.
398
+
332
399
  If you would like direct, low-level control of attribute serialization, you can
333
400
  completely override the `attributes` method to return the hash you need:
334
401
 
@@ -338,7 +405,7 @@ class PersonSerializer < ActiveModel::Serializer
338
405
 
339
406
  def attributes
340
407
  hash = super
341
- if current_user.admin?
408
+ if scope.admin?
342
409
  hash["ssn"] = object.ssn
343
410
  hash["secret"] = object.mothers_maiden_name
344
411
  end
@@ -357,7 +424,7 @@ and use it to serialize the comment.
357
424
  By default, serializers simply look up the association on the original object.
358
425
  You can customize this behavior by implementing a method with the name of the
359
426
  association and returning a different Array. Often, you will do this to
360
- customize the objects returned based on the current user.
427
+ customize the objects returned based on the current user (scope).
361
428
 
362
429
  ```ruby
363
430
  class PostSerializer < ActiveModel::Serializer
@@ -366,7 +433,7 @@ class PostSerializer < ActiveModel::Serializer
366
433
 
367
434
  # only let the user see comments he created.
368
435
  def comments
369
- object.comments.where(:created_by => current_user)
436
+ object.comments.where(created_by: scope)
370
437
  end
371
438
  end
372
439
  ```
@@ -379,27 +446,27 @@ class PostSerializer < ActiveModel::Serializer
379
446
  attributes :id, :title, :body
380
447
 
381
448
  # look up comments, but use +my_comments+ as the key in JSON
382
- has_many :comments, :key => :my_comments
449
+ has_many :comments, root: :my_comments
383
450
  end
384
451
  ```
385
452
 
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:
453
+ Also, as with attributes, serializers will execute a filter method to
454
+ determine which associations should be included in the output. For
455
+ example:
389
456
 
390
457
  ```ruby
391
458
  class PostSerializer < ActiveModel::Serializer
392
459
  attributes :id, :title, :body
393
460
  has_many :comments
394
461
 
395
- def include_comments?
396
- !object.comments_disabled?
462
+ def filter(keys)
463
+ keys.delete :comments if object.comments_disabled?
464
+ keys
397
465
  end
398
466
  end
399
467
  ```
400
468
 
401
- If you would like lower-level control of association serialization, you can
402
- override `include_associations!` to specify which associations should be included:
469
+ Or ...
403
470
 
404
471
  ```ruby
405
472
  class PostSerializer < ActiveModel::Serializer
@@ -407,9 +474,10 @@ class PostSerializer < ActiveModel::Serializer
407
474
  has_one :author
408
475
  has_many :comments
409
476
 
410
- def include_associations!
411
- include! :author if current_user.admin?
412
- include! :comments unless object.comments_disabled?
477
+ def filter(keys)
478
+ keys.delete :author unless scope.admin?
479
+ keys.delete :comments if object.comments_disabled?
480
+ keys
413
481
  end
414
482
  end
415
483
  ```
@@ -417,8 +485,8 @@ end
417
485
  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
486
 
419
487
  ```ruby
420
- has_many :comments, :serializer => CommentShortSerializer
421
- has_one :reviewer, :polymorphic => true
488
+ has_many :comments, serializer: CommentShortSerializer
489
+ has_one :reviewer, polymorphic: true
422
490
  ```
423
491
 
424
492
  Serializers are only concerned with multiplicity, and not ownership. `belongs_to` ActiveRecord associations can be included using `has_one` in your serializer.
@@ -469,6 +537,33 @@ Now, any associations will be supplied as an Array of IDs:
469
537
  }
470
538
  ```
471
539
 
540
+ You may also choose to embed the IDs by the association's name underneath a
541
+ `key` for the resource. For example, say we want to change `comment_ids`
542
+ to `comments` underneath a `links` key:
543
+
544
+ ```ruby
545
+ class PostSerializer < ActiveModel::Serializer
546
+ attributes :id, :title, :body
547
+
548
+ has_many :comments, embed: :ids, key: :comments, embed_namespace: :links
549
+ end
550
+ ```
551
+
552
+ The JSON will look like this:
553
+
554
+ ```json
555
+ {
556
+ "post": {
557
+ "id": 1,
558
+ "title": "New post",
559
+ "body": "A body!",
560
+ "links": {
561
+ "comments": [ 1, 2, 3 ]
562
+ }
563
+ }
564
+ }
565
+ ```
566
+
472
567
  Alternatively, you can choose to embed only the ids or the associated objects per association:
473
568
 
474
569
  ```ruby
@@ -506,7 +601,7 @@ You can specify that the data be included like this:
506
601
 
507
602
  ```ruby
508
603
  class PostSerializer < ActiveModel::Serializer
509
- embed :ids, :include => true
604
+ embed :ids, include: true
510
605
 
511
606
  attributes :id, :title, :body
512
607
  has_many :comments
@@ -536,15 +631,55 @@ this:
536
631
  }
537
632
  ```
538
633
 
634
+ If you would like to namespace association JSON underneath a certain key in
635
+ the root document (say, `linked`), you can specify an `embed_in_root_key`:
636
+
637
+ ```ruby
638
+ class PostSerializer < ActiveModel::Serializer
639
+ embed :ids, include: true, embed_in_root_key: :linked
640
+
641
+ attributes: :id, :title, :body
642
+ has_many :comments, :tags
643
+ end
644
+ ```
645
+
646
+ The above would yield the following JSON document:
647
+
648
+ ```json
649
+ {
650
+ "post": {
651
+ "id": 1,
652
+ "title": "New post",
653
+ "body": "A body!",
654
+ "comment_ids": [ 1, 2 ]
655
+ },
656
+ "linked": {
657
+ "comments": [
658
+ { "id": 1, "body": "what a dumb post", "tag_ids": [ 1, 2 ] },
659
+ { "id": 2, "body": "i liked it", "tag_ids": [ 1, 3 ] },
660
+ ],
661
+ "tags": [
662
+ { "id": 1, "name": "short" },
663
+ { "id": 2, "name": "whiny" },
664
+ { "id": 3, "name": "happy" }
665
+ ]
666
+ }
667
+ }
668
+ ```
669
+
670
+ When side-loading data, your serializer cannot have the `{ root: false }` option,
671
+ as this would lead to invalid JSON. If you do not have a root key, the `include`
672
+ instruction will be ignored
673
+
539
674
  You can also specify a different root for the embedded objects than the key
540
675
  used to reference them:
541
676
 
542
677
  ```ruby
543
678
  class PostSerializer < ActiveModel::Serializer
544
- embed :ids, :include => true
679
+ embed :ids, include: true
545
680
 
546
681
  attributes :id, :title, :body
547
- has_many :comments, :key => :comment_ids, :root => :comment_objects
682
+ has_many :comments, key: :comment_ids, root: :comment_objects
548
683
  end
549
684
  ```
550
685
 
@@ -569,10 +704,10 @@ objects:
569
704
 
570
705
  ```ruby
571
706
  class PostSerializer < ActiveModel::Serializer
572
- embed :ids, :include => true
707
+ embed :ids, include: true
573
708
 
574
709
  attributes :id, :title, :body
575
- has_many :comments, :embed_key => :external_id
710
+ has_many :comments, key: :external_id
576
711
  end
577
712
  ```
578
713
 
@@ -600,6 +735,78 @@ data looking for information, is extremely useful.
600
735
  If you are mostly working with the data in simple scenarios and manually making
601
736
  Ajax requests, you probably just want to use the default embedded behavior.
602
737
 
738
+
739
+ ## Embedding Polymorphic Associations
740
+
741
+ Because we need both the id and the type to be able to identify a polymorphic associated model, these are serialized in a slightly different format than common ones.
742
+
743
+ When embedding entire objects:
744
+
745
+ ```ruby
746
+ class PostSerializer < ActiveModel::Serializer
747
+ attributes :id, :title
748
+ has_many :attachments, polymorphic: true
749
+ end
750
+ ```
751
+
752
+ ```json
753
+ {
754
+ "post": {
755
+ "id": 1,
756
+ "title": "New post",
757
+ "attachments": [
758
+ {
759
+ "type": "image",
760
+ "image": {
761
+ "id": 3,
762
+ "name": "logo",
763
+ "url": "http://images.com/logo.jpg"
764
+ }
765
+ },
766
+ {
767
+ "type": "video",
768
+ "video": {
769
+ "id": 12,
770
+ "uid": "XCSSMDFWW",
771
+ "source": "youtube"
772
+ }
773
+ }
774
+ ]
775
+ }
776
+ }
777
+ ```
778
+
779
+ When embedding ids:
780
+
781
+ ```ruby
782
+ class PostSerializer < ActiveModel::Serializer
783
+ embed :ids
784
+
785
+ attributes :id, :title
786
+ has_many :attachments, polymorphic: true
787
+ end
788
+ ```
789
+
790
+ ```json
791
+ {
792
+ "post": {
793
+ "id": 1,
794
+ "title": "New post",
795
+ "attachment_ids": [
796
+ {
797
+ "type": "image",
798
+ "id": 12
799
+ },
800
+ {
801
+ "type": "video",
802
+ "id": 3
803
+ }
804
+ ]
805
+ }
806
+ }
807
+ ```
808
+
809
+
603
810
  ## Customizing Scope
604
811
 
605
812
  In a serializer, `current_user` is the current authorization scope which the controller
@@ -613,7 +820,7 @@ class ApplicationController < ActionController::Base
613
820
  end
614
821
  ```
615
822
 
616
- The above example will also change the scope name from `current_user` to
823
+ The above example will also change the scope from `current_user` to
617
824
  `current_admin`.
618
825
 
619
826
  Please note that, until now, `serialization_scope` doesn't accept a second
@@ -624,7 +831,7 @@ To be clear, it's not possible, yet, to do something like this:
624
831
 
625
832
  ```ruby
626
833
  class SomeController < ApplicationController
627
- serialization_scope :current_admin, :except => [:index, :show]
834
+ serialization_scope :current_admin, except: [:index, :show]
628
835
  end
629
836
  ```
630
837
 
@@ -638,13 +845,13 @@ class CitiesController < ApplicationController
638
845
  def index
639
846
  @cities = City.all
640
847
 
641
- render :json => @cities, :each_serializer => CitySerializer
848
+ render json: @cities, each_serializer: CitySerializer
642
849
  end
643
850
 
644
851
  def show
645
852
  @city = City.find(params[:id])
646
853
 
647
- render :json => @city, :scope => current_admin, :scope_name => :current_admin
854
+ render json: @city, scope: current_admin
648
855
  end
649
856
  end
650
857
  ```
@@ -653,3 +860,97 @@ Assuming that the `current_admin` method needs to make a query in the database
653
860
  for the current user, the advantage of this approach is that, by setting
654
861
  `serialization_scope` to `nil`, the `index` action no longer will need to make
655
862
  that query, only the `show` action will.
863
+
864
+ ## Testing
865
+
866
+ In order to test a Serializer, you can just call `.new` on it, passing the object to serialize:
867
+
868
+ ### MiniTest
869
+
870
+ ```ruby
871
+ class TestPostSerializer < Minitest::Test
872
+ def setup
873
+ @serializer = PostSerializer.new Post.new(id: 123, title: 'some title', body: 'some text')
874
+ end
875
+
876
+ def test_special_json_for_api
877
+ assert_equal '{"post":{"id":123,"title":"some title","body":"some text"}}', @serializer.to_json
878
+ end
879
+ ```
880
+
881
+ ### RSpec
882
+
883
+ ```ruby
884
+ describe PostSerializer do
885
+ it "creates special JSON for the API" do
886
+ serializer = PostSerializer.new Post.new(id: 123, title: 'some title', body: 'some text')
887
+ expect(serializer.to_json).to eql('{"post":{"id":123,"title":"some title","body":"some text"}}')
888
+ end
889
+ end
890
+ ```
891
+
892
+ ## Caching
893
+
894
+ NOTE: This functionality was removed from AMS and it's in the TODO list.
895
+ We need to re-think and re-design the caching strategy for the next
896
+ version of AMS.
897
+
898
+ To cache a serializer, call `cached` and define a `cache_key` method:
899
+
900
+ ```ruby
901
+ class PostSerializer < ActiveModel::Serializer
902
+ cached # enables caching for this serializer
903
+
904
+ attributes :title, :body
905
+
906
+ def cache_key
907
+ [object, scope]
908
+ end
909
+ end
910
+ ```
911
+
912
+ The caching interface uses `Rails.cache` under the hood.
913
+
914
+ # ApplicationSerializer
915
+
916
+ 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```:
917
+
918
+ ```ruby
919
+ class ApplicationSerializer < ActiveModel::Serializer
920
+ end
921
+ ```
922
+
923
+ Any newly generated serializers will automatically descend from ApplicationSerializer.
924
+
925
+ ```
926
+ $ rails g serializer post
927
+ ```
928
+
929
+ now generates:
930
+
931
+ ```ruby
932
+ class PostSerializer < ApplicationSerializer
933
+ attributes :id
934
+ end
935
+ ````
936
+
937
+ # Design and Implementation Guidelines
938
+
939
+ ## Keep it Simple
940
+
941
+ `ActiveModel::Serializers` is capable of producing complex JSON views/large object
942
+ trees, and it may be tempting to design in this way so that your client can make
943
+ fewer requests to get data and so that related querying can be optimized.
944
+ However, keeping things simple in your serializers and controllers may
945
+ significantly reduce complexity and maintenance over the long-term development
946
+ of your application. Please consider reducing the complexity of the JSON views
947
+ you provide via the serializers as you build out your application, so that
948
+ controllers/services can be more easily reused without a lot of complexity
949
+ later.
950
+
951
+ ## Performance
952
+
953
+ As you develop your controllers or other code that utilizes serializers, try to
954
+ avoid n+1 queries by ensuring that data loads in an optimal fashion, e.g. if you
955
+ are using ActiveRecord, you might want to use query includes or joins as needed
956
+ to make the data available that the serializer(s) need.