active_model_serializers 0.8.3 → 0.9.1

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 (88) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +31 -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 +395 -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 +46 -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 +209 -422
  26. data/lib/active_model/serializer_support.rb +5 -0
  27. data/lib/active_model_serializers.rb +12 -87
  28. data/test/fixtures/active_record.rb +92 -0
  29. data/test/fixtures/poro.rb +184 -0
  30. data/test/fixtures/template.html.erb +1 -0
  31. data/test/integration/action_controller/namespaced_serialization_test.rb +96 -0
  32. data/test/integration/action_controller/serialization_test.rb +303 -0
  33. data/test/integration/action_controller/serialization_test_case_test.rb +71 -0
  34. data/test/integration/active_record/active_record_test.rb +77 -0
  35. data/test/integration/generators/resource_generator_test.rb +26 -0
  36. data/test/integration/generators/scaffold_controller_generator_test.rb +64 -0
  37. data/test/integration/generators/serializer_generator_test.rb +41 -0
  38. data/test/test_app.rb +14 -0
  39. data/test/test_helper.rb +10 -18
  40. data/test/tmp/app/serializers/account_serializer.rb +3 -0
  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/root_test.rb +102 -0
  46. data/test/unit/active_model/array_serializer/scope_test.rb +24 -0
  47. data/test/unit/active_model/array_serializer/serialization_test.rb +199 -0
  48. data/test/unit/active_model/default_serializer_test.rb +13 -0
  49. data/test/unit/active_model/serializer/associations/build_serializer_test.rb +36 -0
  50. data/test/unit/active_model/serializer/associations_test.rb +19 -0
  51. data/test/unit/active_model/serializer/attributes_test.rb +57 -0
  52. data/test/unit/active_model/serializer/config_test.rb +91 -0
  53. data/test/unit/active_model/serializer/filter_test.rb +69 -0
  54. data/test/unit/active_model/serializer/has_many_polymorphic_test.rb +189 -0
  55. data/test/unit/active_model/serializer/has_many_test.rb +265 -0
  56. data/test/unit/active_model/serializer/has_one_and_has_many_test.rb +27 -0
  57. data/test/unit/active_model/serializer/has_one_polymorphic_test.rb +196 -0
  58. data/test/unit/active_model/serializer/has_one_test.rb +239 -0
  59. data/test/unit/active_model/serializer/key_format_test.rb +25 -0
  60. data/test/unit/active_model/serializer/meta_test.rb +39 -0
  61. data/test/unit/active_model/serializer/options_test.rb +34 -0
  62. data/test/unit/active_model/serializer/root_test.rb +117 -0
  63. data/test/unit/active_model/serializer/scope_test.rb +49 -0
  64. data/test/unit/active_model/serializer/url_helpers_test.rb +35 -0
  65. metadata +104 -63
  66. data/.gitignore +0 -18
  67. data/.travis.yml +0 -28
  68. data/Gemfile +0 -4
  69. data/Gemfile.edge +0 -9
  70. data/Rakefile +0 -18
  71. data/active_model_serializers.gemspec +0 -24
  72. data/bench/perf.rb +0 -43
  73. data/cruft.md +0 -19
  74. data/lib/active_model/serializer/associations.rb +0 -233
  75. data/lib/active_record/serializer_override.rb +0 -16
  76. data/lib/generators/resource_override.rb +0 -13
  77. data/lib/generators/serializer/serializer_generator.rb +0 -42
  78. data/lib/generators/serializer/templates/serializer.rb +0 -19
  79. data/test/array_serializer_test.rb +0 -75
  80. data/test/association_test.rb +0 -592
  81. data/test/caching_test.rb +0 -96
  82. data/test/generators_test.rb +0 -85
  83. data/test/no_serialization_scope_test.rb +0 -34
  84. data/test/serialization_scope_name_test.rb +0 -67
  85. data/test/serialization_test.rb +0 -392
  86. data/test/serializer_support_test.rb +0 -51
  87. data/test/serializer_test.rb +0 -1465
  88. 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,57 @@ 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
+
282
342
  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:
343
+ in ActiveRecord, you can declare the attribute with the different name
344
+ and redefine that method:
284
345
 
285
346
  ```ruby
286
347
  class PostSerializer < ActiveModel::Serializer
287
- attributes :id, :body
348
+ # look up subject on the model, but use title in the JSON
349
+ def title
350
+ object.subject
351
+ end
288
352
 
289
- # look up :subject on the model, but use +title+ in the JSON
290
- attribute :subject, :key => :title
353
+ attributes :id, :body, :title
291
354
  has_many :comments
292
355
  end
293
356
  ```
@@ -296,7 +359,7 @@ If you would like to add meta information to the outputted JSON, use the `:meta`
296
359
  option:
297
360
 
298
361
  ```ruby
299
- render :json => @posts, :serializer => CustomArraySerializer, :meta => {:total => 10}
362
+ render json: @posts, serializer: CustomArraySerializer, meta: {total: 10}
300
363
  ```
301
364
 
302
365
  The above usage of `:meta` will produce the following:
@@ -314,7 +377,7 @@ The above usage of `:meta` will produce the following:
314
377
  If you would like to change the meta key name you can use the `:meta_key` option:
315
378
 
316
379
  ```ruby
317
- render :json => @posts, :serializer => CustomArraySerializer, :meta => {:total => 10}, :meta_key => 'meta_object'
380
+ render json: @posts, serializer: CustomArraySerializer, meta_object: {total: 10}, meta_key: 'meta_object'
318
381
  ```
319
382
 
320
383
  The above usage of `:meta_key` will produce the following:
@@ -329,6 +392,9 @@ The above usage of `:meta_key` will produce the following:
329
392
  }
330
393
  ```
331
394
 
395
+ When using meta information, your serializer cannot have the `{ root: false }` option, as this would lead to
396
+ invalid JSON. If you do not have a root key, the meta information will be ignored.
397
+
332
398
  If you would like direct, low-level control of attribute serialization, you can
333
399
  completely override the `attributes` method to return the hash you need:
334
400
 
@@ -338,7 +404,7 @@ class PersonSerializer < ActiveModel::Serializer
338
404
 
339
405
  def attributes
340
406
  hash = super
341
- if current_user.admin?
407
+ if scope.admin?
342
408
  hash["ssn"] = object.ssn
343
409
  hash["secret"] = object.mothers_maiden_name
344
410
  end
@@ -357,7 +423,7 @@ and use it to serialize the comment.
357
423
  By default, serializers simply look up the association on the original object.
358
424
  You can customize this behavior by implementing a method with the name of the
359
425
  association and returning a different Array. Often, you will do this to
360
- customize the objects returned based on the current user.
426
+ customize the objects returned based on the current user (scope).
361
427
 
362
428
  ```ruby
363
429
  class PostSerializer < ActiveModel::Serializer
@@ -366,7 +432,7 @@ class PostSerializer < ActiveModel::Serializer
366
432
 
367
433
  # only let the user see comments he created.
368
434
  def comments
369
- object.comments.where(:created_by => current_user)
435
+ object.comments.where(created_by: scope)
370
436
  end
371
437
  end
372
438
  ```
@@ -379,27 +445,27 @@ class PostSerializer < ActiveModel::Serializer
379
445
  attributes :id, :title, :body
380
446
 
381
447
  # look up comments, but use +my_comments+ as the key in JSON
382
- has_many :comments, :key => :my_comments
448
+ has_many :comments, root: :my_comments
383
449
  end
384
450
  ```
385
451
 
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:
452
+ Also, as with attributes, serializers will execute a filter method to
453
+ determine which associations should be included in the output. For
454
+ example:
389
455
 
390
456
  ```ruby
391
457
  class PostSerializer < ActiveModel::Serializer
392
458
  attributes :id, :title, :body
393
459
  has_many :comments
394
460
 
395
- def include_comments?
396
- !object.comments_disabled?
461
+ def filter(keys)
462
+ keys.delete :comments if object.comments_disabled?
463
+ keys
397
464
  end
398
465
  end
399
466
  ```
400
467
 
401
- If you would like lower-level control of association serialization, you can
402
- override `include_associations!` to specify which associations should be included:
468
+ Or ...
403
469
 
404
470
  ```ruby
405
471
  class PostSerializer < ActiveModel::Serializer
@@ -407,9 +473,10 @@ class PostSerializer < ActiveModel::Serializer
407
473
  has_one :author
408
474
  has_many :comments
409
475
 
410
- def include_associations!
411
- include! :author if current_user.admin?
412
- include! :comments unless object.comments_disabled?
476
+ def filter(keys)
477
+ keys.delete :author unless scope.admin?
478
+ keys.delete :comments if object.comments_disabled?
479
+ keys
413
480
  end
414
481
  end
415
482
  ```
@@ -417,8 +484,8 @@ end
417
484
  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
485
 
419
486
  ```ruby
420
- has_many :comments, :serializer => CommentShortSerializer
421
- has_one :reviewer, :polymorphic => true
487
+ has_many :comments, serializer: CommentShortSerializer
488
+ has_one :reviewer, polymorphic: true
422
489
  ```
423
490
 
424
491
  Serializers are only concerned with multiplicity, and not ownership. `belongs_to` ActiveRecord associations can be included using `has_one` in your serializer.
@@ -469,6 +536,33 @@ Now, any associations will be supplied as an Array of IDs:
469
536
  }
470
537
  ```
471
538
 
539
+ You may also choose to embed the IDs by the association's name underneath a
540
+ `key` for the resource. For example, say we want to change `comment_ids`
541
+ to `comments` underneath a `links` key:
542
+
543
+ ```ruby
544
+ class PostSerializer < ActiveModel::Serializer
545
+ attributes :id, :title, :body
546
+
547
+ has_many :comments, embed: :ids, key: :comments, embed_namespace: :links
548
+ end
549
+ ```
550
+
551
+ The JSON will look like this:
552
+
553
+ ```json
554
+ {
555
+ "post": {
556
+ "id": 1,
557
+ "title": "New post",
558
+ "body": "A body!",
559
+ "links": {
560
+ "comments": [ 1, 2, 3 ]
561
+ }
562
+ }
563
+ }
564
+ ```
565
+
472
566
  Alternatively, you can choose to embed only the ids or the associated objects per association:
473
567
 
474
568
  ```ruby
@@ -506,7 +600,7 @@ You can specify that the data be included like this:
506
600
 
507
601
  ```ruby
508
602
  class PostSerializer < ActiveModel::Serializer
509
- embed :ids, :include => true
603
+ embed :ids, include: true
510
604
 
511
605
  attributes :id, :title, :body
512
606
  has_many :comments
@@ -536,15 +630,55 @@ this:
536
630
  }
537
631
  ```
538
632
 
633
+ If you would like to namespace association JSON underneath a certain key in
634
+ the root document (say, `linked`), you can specify an `embed_in_root_key`:
635
+
636
+ ```ruby
637
+ class PostSerializer < ActiveModel::Serializer
638
+ embed :ids, include: true, embed_in_root_key: :linked
639
+
640
+ attributes: :id, :title, :body
641
+ has_many :comments, :tags
642
+ end
643
+ ```
644
+
645
+ The above would yield the following JSON document:
646
+
647
+ ```json
648
+ {
649
+ "post": {
650
+ "id": 1,
651
+ "title": "New post",
652
+ "body": "A body!",
653
+ "comment_ids": [ 1, 2 ]
654
+ },
655
+ "linked": {
656
+ "comments": [
657
+ { "id": 1, "body": "what a dumb post", "tag_ids": [ 1, 2 ] },
658
+ { "id": 2, "body": "i liked it", "tag_ids": [ 1, 3 ] },
659
+ ],
660
+ "tags": [
661
+ { "id": 1, "name": "short" },
662
+ { "id": 2, "name": "whiny" },
663
+ { "id": 3, "name": "happy" }
664
+ ]
665
+ }
666
+ }
667
+ ```
668
+
669
+ When side-loading data, your serializer cannot have the `{ root: false }` option,
670
+ as this would lead to invalid JSON. If you do not have a root key, the `include`
671
+ instruction will be ignored
672
+
539
673
  You can also specify a different root for the embedded objects than the key
540
674
  used to reference them:
541
675
 
542
676
  ```ruby
543
677
  class PostSerializer < ActiveModel::Serializer
544
- embed :ids, :include => true
678
+ embed :ids, include: true
545
679
 
546
680
  attributes :id, :title, :body
547
- has_many :comments, :key => :comment_ids, :root => :comment_objects
681
+ has_many :comments, key: :comment_ids, root: :comment_objects
548
682
  end
549
683
  ```
550
684
 
@@ -569,10 +703,10 @@ objects:
569
703
 
570
704
  ```ruby
571
705
  class PostSerializer < ActiveModel::Serializer
572
- embed :ids, :include => true
706
+ embed :ids, include: true
573
707
 
574
708
  attributes :id, :title, :body
575
- has_many :comments, :embed_key => :external_id
709
+ has_many :comments, key: :external_id
576
710
  end
577
711
  ```
578
712
 
@@ -600,6 +734,78 @@ data looking for information, is extremely useful.
600
734
  If you are mostly working with the data in simple scenarios and manually making
601
735
  Ajax requests, you probably just want to use the default embedded behavior.
602
736
 
737
+
738
+ ## Embedding Polymorphic Associations
739
+
740
+ 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.
741
+
742
+ When embedding entire objects:
743
+
744
+ ```ruby
745
+ class PostSerializer < ActiveModel::Serializer
746
+ attributes :id, :title
747
+ has_many :attachments, polymorphic: true
748
+ end
749
+ ```
750
+
751
+ ```json
752
+ {
753
+ "post": {
754
+ "id": 1,
755
+ "title": "New post",
756
+ "attachments": [
757
+ {
758
+ "type": "image"
759
+ "image": {
760
+ "id": 3
761
+ "name": "logo"
762
+ "url": "http://images.com/logo.jpg"
763
+ }
764
+ },
765
+ {
766
+ "type": "video"
767
+ "video": {
768
+ "id": 12
769
+ "uid": "XCSSMDFWW"
770
+ "source": "youtube"
771
+ }
772
+ }
773
+ ]
774
+ }
775
+ }
776
+ ```
777
+
778
+ When embedding ids:
779
+
780
+ ```ruby
781
+ class PostSerializer < ActiveModel::Serializer
782
+ embed :ids
783
+
784
+ attributes :id, :title
785
+ has_many :attachments, polymorphic: true
786
+ end
787
+ ```
788
+
789
+ ```json
790
+ {
791
+ "post": {
792
+ "id": 1,
793
+ "title": "New post",
794
+ "attachment_ids": [
795
+ {
796
+ "type": "image"
797
+ "id": 12
798
+ },
799
+ {
800
+ "type": "video"
801
+ "id": 3
802
+ }
803
+ ]
804
+ }
805
+ }
806
+ ```
807
+
808
+
603
809
  ## Customizing Scope
604
810
 
605
811
  In a serializer, `current_user` is the current authorization scope which the controller
@@ -613,7 +819,7 @@ class ApplicationController < ActionController::Base
613
819
  end
614
820
  ```
615
821
 
616
- The above example will also change the scope name from `current_user` to
822
+ The above example will also change the scope from `current_user` to
617
823
  `current_admin`.
618
824
 
619
825
  Please note that, until now, `serialization_scope` doesn't accept a second
@@ -624,7 +830,7 @@ To be clear, it's not possible, yet, to do something like this:
624
830
 
625
831
  ```ruby
626
832
  class SomeController < ApplicationController
627
- serialization_scope :current_admin, :except => [:index, :show]
833
+ serialization_scope :current_admin, except: [:index, :show]
628
834
  end
629
835
  ```
630
836
 
@@ -638,13 +844,13 @@ class CitiesController < ApplicationController
638
844
  def index
639
845
  @cities = City.all
640
846
 
641
- render :json => @cities, :each_serializer => CitySerializer
847
+ render json: @cities, each_serializer: CitySerializer
642
848
  end
643
849
 
644
850
  def show
645
851
  @city = City.find(params[:id])
646
852
 
647
- render :json => @city, :scope => current_admin, :scope_name => :current_admin
853
+ render json: @city, scope: current_admin
648
854
  end
649
855
  end
650
856
  ```
@@ -653,3 +859,97 @@ Assuming that the `current_admin` method needs to make a query in the database
653
859
  for the current user, the advantage of this approach is that, by setting
654
860
  `serialization_scope` to `nil`, the `index` action no longer will need to make
655
861
  that query, only the `show` action will.
862
+
863
+ ## Testing
864
+
865
+ In order to test a Serializer, you can just call `.new` on it, passing the object to serialize:
866
+
867
+ ### MiniTest
868
+
869
+ ```ruby
870
+ class TestPostSerializer < Minitest::Test
871
+ def setup
872
+ @serializer = PostSerializer.new Post.new(id: 123, title: 'some title', body: 'some text')
873
+ end
874
+
875
+ def test_special_json_for_api
876
+ assert_equal '{"post":{"id":123,"title":"some title","body":"some text"}}', @serializer.to_json
877
+ end
878
+ ```
879
+
880
+ ### RSpec
881
+
882
+ ```ruby
883
+ describe PostSerializer do
884
+ it "creates special JSON for the API" do
885
+ serializer = PostSerializer.new Post.new(id: 123, title: 'some title', body: 'some text')
886
+ expect(serializer.to_json).to eql('{"post":{"id":123,"title":"some title","body":"some text"}}')
887
+ end
888
+ end
889
+ ```
890
+
891
+ ## Caching
892
+
893
+ NOTE: This functionality was removed from AMS and it's in the TODO list.
894
+ We need to re-think and re-design the caching strategy for the next
895
+ version of AMS.
896
+
897
+ To cache a serializer, call `cached` and define a `cache_key` method:
898
+
899
+ ```ruby
900
+ class PostSerializer < ActiveModel::Serializer
901
+ cached # enables caching for this serializer
902
+
903
+ attributes :title, :body
904
+
905
+ def cache_key
906
+ [object, scope]
907
+ end
908
+ end
909
+ ```
910
+
911
+ The caching interface uses `Rails.cache` under the hood.
912
+
913
+ # ApplicationSerializer
914
+
915
+ 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```:
916
+
917
+ ```ruby
918
+ class ApplicationSerializer < ActiveModel::Serializer
919
+ end
920
+ ```
921
+
922
+ Any newly generated serializers will automatically descend from ApplicationSerializer.
923
+
924
+ ```
925
+ $ rails g serializer post
926
+ ```
927
+
928
+ now generates:
929
+
930
+ ```ruby
931
+ class PostSerializer < ApplicationSerializer
932
+ attributes :id
933
+ end
934
+ ````
935
+
936
+ # Design and Implementation Guidelines
937
+
938
+ ## Keep it Simple
939
+
940
+ `ActiveModel::Serializers` is capable of producing complex JSON views/large object
941
+ trees, and it may be tempting to design in this way so that your client can make
942
+ fewer requests to get data and so that related querying can be optimized.
943
+ However, keeping things simple in your serializers and controllers may
944
+ significantly reduce complexity and maintenance over the long-term development
945
+ of your application. Please consider reducing the complexity of the JSON views
946
+ you provide via the serializers as you build out your application, so that
947
+ controllers/services can be more easily reused without a lot of complexity
948
+ later.
949
+
950
+ ## Performance
951
+
952
+ As you develop your controllers or other code that utilizes serializers, try to
953
+ avoid n+1 queries by ensuring that data loads in an optimal fashion, e.g. if you
954
+ are using ActiveRecord, you might want to use query includes or joins as needed
955
+ to make the data available that the serializer(s) need.