active_model_serializers_rails_2.3 0.9.0.alpha1

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 (39) hide show
  1. data/CHANGELOG.md +87 -0
  2. data/CONTRIBUTING.md +20 -0
  3. data/DESIGN.textile +586 -0
  4. data/MIT-LICENSE +21 -0
  5. data/README.md +793 -0
  6. data/lib/active_model/array_serializer.rb +60 -0
  7. data/lib/active_model/default_serializer.rb +27 -0
  8. data/lib/active_model/serializable.rb +25 -0
  9. data/lib/active_model/serializer.rb +220 -0
  10. data/lib/active_model/serializer/associations.rb +98 -0
  11. data/lib/active_model/serializer/config.rb +31 -0
  12. data/lib/active_model/serializer/generators/serializer/scaffold_controller_generator.rb +14 -0
  13. data/lib/active_model/serializer/generators/serializer/templates/controller.rb +93 -0
  14. data/lib/active_model/serializer/railtie.rb +10 -0
  15. data/lib/active_model/serializer/version.rb +5 -0
  16. data/lib/active_model/serializer_support.rb +5 -0
  17. data/lib/active_model_serializers.rb +33 -0
  18. data/test/coverage_setup.rb +15 -0
  19. data/test/fixtures/active_record.rb +92 -0
  20. data/test/fixtures/poro.rb +64 -0
  21. data/test/integration/active_record/active_record_test.rb +77 -0
  22. data/test/test_app.rb +11 -0
  23. data/test/test_helper.rb +13 -0
  24. data/test/unit/active_model/array_serializer/meta_test.rb +53 -0
  25. data/test/unit/active_model/array_serializer/root_test.rb +102 -0
  26. data/test/unit/active_model/array_serializer/scope_test.rb +24 -0
  27. data/test/unit/active_model/array_serializer/serialization_test.rb +182 -0
  28. data/test/unit/active_model/default_serializer_test.rb +13 -0
  29. data/test/unit/active_model/serializer/associations/build_serializer_test.rb +21 -0
  30. data/test/unit/active_model/serializer/associations_test.rb +19 -0
  31. data/test/unit/active_model/serializer/attributes_test.rb +41 -0
  32. data/test/unit/active_model/serializer/config_test.rb +86 -0
  33. data/test/unit/active_model/serializer/filter_test.rb +49 -0
  34. data/test/unit/active_model/serializer/has_many_test.rb +174 -0
  35. data/test/unit/active_model/serializer/has_one_test.rb +151 -0
  36. data/test/unit/active_model/serializer/meta_test.rb +39 -0
  37. data/test/unit/active_model/serializer/root_test.rb +117 -0
  38. data/test/unit/active_model/serializer/scope_test.rb +49 -0
  39. metadata +127 -0
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2011-2012 José Valim & Yehuda Katz
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
@@ -0,0 +1,793 @@
1
+ [![Build Status](https://api.travis-ci.org/rails-api/active_model_serializers.png)](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)
3
+ [![Coverage Status](https://coveralls.io/repos/rails-api/active_model_serializers/badge.png?branch=master)](https://coveralls.io/r/rails-api/active_model_serializers)
4
+
5
+ # ActiveModel::Serializers
6
+
7
+ # Important Notes for this Rails 2.3 Backport
8
+
9
+ This is a backport of the ActiveModel::Serializers gem to be compatible with
10
+ Rails 2.3. The following features have been removed:
11
+
12
+ * Controller `render` method support. This means you'll need to instantiate
13
+ the serializer yourself and pass it to `render`. E.g. `render :json => FooSerializer.new(foo, scope: user)`
14
+ * Generators are gone as well.
15
+ * Probably won't be supported for a long amount of time. Please do your best to upgrade
16
+ to rails 3.2+ as soon as possible. Feel free to open an issue whenever I run
17
+ too far behind upstream's master branch.
18
+
19
+ ## Rails 2-3- 0.9.0
20
+
21
+ **Rails 2-3 is under development, there are some incompatible changes with the current stable release.**
22
+
23
+ Since `0.8` there is no `options` accessor in the serializers. Some issues and pull-requests are attemps to bring back the ability to use Rails url helpers in the serializers, but nothing is really settled yet.
24
+
25
+ If you want to read the stable documentation visit [0.8 README](https://github.com/rails-api/active_model_serializers/blob/0-8-stable/README.md)
26
+
27
+ ## Purpose
28
+
29
+ `ActiveModel::Serializers` encapsulates the JSON serialization of objects.
30
+ Objects that respond to read\_attribute\_for\_serialization
31
+ (including `ActiveModel` and `ActiveRecord` objects) are supported.
32
+
33
+ Serializers know about both a model and the `current_user`, so you can
34
+ customize serialization based upon whether a user is authorized to see the
35
+ content.
36
+
37
+ In short, **serializers replace hash-driven development with object-oriented
38
+ development.**
39
+
40
+ # Installing
41
+
42
+ The easiest way to install `ActiveModel::Serializers` is to add it to your
43
+ `Gemfile`:
44
+
45
+ ```ruby
46
+ gem "active_model_serializers_rails2.3", git: "https://github.com/fivetanley/active_model_serializers", branch: 'rails-2.3'
47
+ ```
48
+
49
+ Then, install it on the command line:
50
+
51
+ ```
52
+ $ bundle install
53
+ ```
54
+
55
+ #### Ruby 1.8 is no longer supported!
56
+
57
+ 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.
58
+ Versions after 0.9.0 do not support ruby 1.8. To specify version 0.8, include this in your Gemfile:
59
+
60
+ ```ruby
61
+ gem "active_model_serializers", "~> 0.8.0"
62
+ ```
63
+
64
+
65
+ # Creating a Serializer
66
+
67
+ The easiest way to create a new serializer is to generate a new resource, which
68
+ will generate a serializer at the same time:
69
+
70
+ ```
71
+ $ rails g resource post title:string body:string
72
+ ```
73
+
74
+ This will generate a serializer in `app/serializers/post_serializer.rb` for
75
+ your new model. You can also generate a serializer for an existing model with
76
+ the serializer generator:
77
+
78
+ ```
79
+ $ rails g serializer post
80
+ ```
81
+
82
+ ### Support for POROs
83
+
84
+ Currently `ActiveModel::Serializers` expects objects to implement
85
+ read\_attribute\_for\_serialization. That's all you need to do to have
86
+ your POROs supported.
87
+
88
+ # render :json
89
+
90
+ In your controllers, when you use `render :json`, Rails will now first search
91
+ for a serializer for the object and use it if available.
92
+
93
+ ```ruby
94
+ class PostsController < ApplicationController
95
+ def show
96
+ @post = Post.find(params[:id])
97
+ render json: @post
98
+ end
99
+ end
100
+ ```
101
+
102
+ In this case, Rails will look for a serializer named `PostSerializer`, and if
103
+ it exists, use it to serialize the `Post`.
104
+
105
+ This also works with `respond_with`, which uses `to_json` under the hood. Also
106
+ note that any options passed to `render :json` will be passed to your
107
+ serializer and available as `@options` inside.
108
+
109
+ To specify a custom serializer for an object, you can specify the
110
+ serializer when you render the object:
111
+
112
+ ```ruby
113
+ render json: @post, serializer: FancyPostSerializer
114
+ ```
115
+
116
+ ## Arrays
117
+
118
+ In your controllers, when you use `render :json` for an array of objects, AMS will
119
+ use `ActiveModel::ArraySerializer` (included in this project) as the base serializer,
120
+ and the individual `Serializer` for the objects contained in that array.
121
+
122
+ ```ruby
123
+ class PostSerializer < ActiveModel::Serializer
124
+ attributes :title, :body
125
+ end
126
+
127
+ class PostsController < ApplicationController
128
+ def index
129
+ @posts = Post.all
130
+ render json: @posts
131
+ end
132
+ end
133
+ ```
134
+
135
+ Given the example above, the index action will return
136
+
137
+ ```json
138
+ {
139
+ "posts":
140
+ [
141
+ { "title": "Post 1", "body": "Hello!" },
142
+ { "title": "Post 2", "body": "Goodbye!" }
143
+ ]
144
+ }
145
+ ```
146
+
147
+ By default, the root element is the name of the controller. For example, `PostsController`
148
+ generates a root element "posts". To change it:
149
+
150
+ ```ruby
151
+ render json: @posts, root: "some_posts"
152
+ ```
153
+
154
+ You may disable the root element for arrays at the top level, which will result in
155
+ more concise json. See the next section for ways on how to do this. Disabling the
156
+ root element of the array with any of those methods will produce
157
+
158
+ ```json
159
+ [
160
+ { "title": "Post 1", "body": "Hello!" },
161
+ { "title": "Post 2", "body": "Goodbye!" }
162
+ ]
163
+ ```
164
+
165
+ To specify a custom serializer for the items within an array:
166
+
167
+ ```ruby
168
+ render json: @posts, each_serializer: FancyPostSerializer
169
+ ```
170
+
171
+ ## Render independently
172
+
173
+ By default the setting of serializer is in controller as described above which is the
174
+ recommeneded way. However, there may be cases you need to render the json object elsewhere
175
+ say in a helper or a view when controller is only for main object.
176
+
177
+ Then you can render the serialized JSON independently.
178
+
179
+ ```ruby
180
+ def current_user_as_json_helper
181
+ CurrentUserSerializer.new(current_user).to_json
182
+ end
183
+ ```
184
+
185
+ ## Disabling the root element
186
+
187
+ You have 4 options to disable the root element, each with a slightly different scope:
188
+
189
+ #### 1. Disable root globally for all, or per class
190
+
191
+ In an initializer:
192
+
193
+ ```ruby
194
+ # Disable for all serializers (except ArraySerializer)
195
+ ActiveModel::Serializer.root = false
196
+
197
+ # Disable for ArraySerializer
198
+ ActiveModel::ArraySerializer.root = false
199
+ ```
200
+
201
+ #### 2. Disable root per render call in your controller
202
+
203
+ ```ruby
204
+ render json: @posts, root: false
205
+ ```
206
+
207
+ #### 3. Subclass the serializer, and specify using it
208
+
209
+ ```ruby
210
+ class CustomArraySerializer < ActiveModel::ArraySerializer
211
+ self.root = false
212
+ end
213
+
214
+ # controller:
215
+ render json: @posts, serializer: CustomArraySerializer
216
+ ```
217
+
218
+ #### 4. Define default_serializer_options in your controller
219
+
220
+ If you define `default_serializer_options` method in your controller,
221
+ all serializers in actions of this controller and it's children will use them.
222
+ One of the options may be `root: false`
223
+
224
+ ```ruby
225
+ def default_serializer_options
226
+ {
227
+ root: false
228
+ }
229
+ end
230
+ ```
231
+
232
+ ## Getting the old version
233
+
234
+ If you find that your project is already relying on the old rails to_json
235
+ change `render :json` to `render json: @your_object.to_json`.
236
+
237
+ # Attributes and Associations
238
+
239
+ Once you have a serializer, you can specify which attributes and associations
240
+ you would like to include in the serialized form.
241
+
242
+ ```ruby
243
+ class PostSerializer < ActiveModel::Serializer
244
+ attributes :id, :title, :body
245
+ has_many :comments
246
+ end
247
+ ```
248
+
249
+ ## Attributes
250
+
251
+ For specified attributes, a serializer will look up the attribute on the
252
+ object you passed to `render :json`. It uses
253
+ `read_attribute_for_serialization`, which `ActiveRecord` objects implement as a
254
+ regular attribute lookup.
255
+
256
+ Before looking up the attribute on the object, a serializer will check for the
257
+ presence of a method with the name of the attribute. This allows serializers to
258
+ include properties beyond the simple attributes of the model. For example:
259
+
260
+ ```ruby
261
+ class PersonSerializer < ActiveModel::Serializer
262
+ attributes :first_name, :last_name, :full_name
263
+
264
+ def full_name
265
+ "#{object.first_name} #{object.last_name}"
266
+ end
267
+ end
268
+ ```
269
+
270
+ Within a serializer's methods, you can access the object being
271
+ serialized as `object`.
272
+
273
+ Since this shadows any attribute named `object`, you can include them through `object.object`. For example:
274
+
275
+ ```ruby
276
+ class VersionSerializer < ActiveModel::Serializer
277
+ attribute :version_object, key: :object
278
+
279
+ def version_object
280
+ object.object
281
+ end
282
+ end
283
+ ```
284
+
285
+ You can also access the `scope` method, which provides an
286
+ authorization context to your serializer. By default, the context
287
+ is the current user of your application, but this
288
+ [can be customized](#customizing-scope).
289
+
290
+ Serializers provides a method named `filter`, which should return an array
291
+ used to determine what attributes and associations should be included in the output.
292
+ This is typically used to customize output based on `current_user`. For example:
293
+
294
+ ```ruby
295
+ class PostSerializer < ActiveModel::Serializer
296
+ attributes :id, :title, :body, :author
297
+
298
+ def filter(keys)
299
+ if scope.admin?
300
+ keys
301
+ else
302
+ keys - [:author]
303
+ end
304
+ end
305
+ end
306
+ ```
307
+
308
+ And it's also safe to mutate keys argument by doing keys.delete(:author)
309
+ in case you want to avoid creating two extra arrays. Note that if you do an
310
+ in-place modification, you still need to return the modified array.
311
+
312
+ If you would like the key in the outputted JSON to be different from its name
313
+ in ActiveRecord, you can declare the attribute with the different name
314
+ and redefine that method:
315
+
316
+ ```ruby
317
+ class PostSerializer < ActiveModel::Serializer
318
+ # look up subject on the model, but use title in the JSON
319
+ def title
320
+ object.subject
321
+ end
322
+
323
+ attributes :id, :body, :title
324
+ has_many :comments
325
+ end
326
+ ```
327
+
328
+ If you would like to add meta information to the outputted JSON, use the `:meta`
329
+ option:
330
+
331
+ ```ruby
332
+ render json: @posts, serializer: CustomArraySerializer, meta: {total: 10}
333
+ ```
334
+
335
+ The above usage of `:meta` will produce the following:
336
+
337
+ ```json
338
+ {
339
+ "meta": { "total": 10 },
340
+ "posts": [
341
+ { "title": "Post 1", "body": "Hello!" },
342
+ { "title": "Post 2", "body": "Goodbye!" }
343
+ ]
344
+ }
345
+ ```
346
+
347
+ If you would like to change the meta key name you can use the `:meta_key` option:
348
+
349
+ ```ruby
350
+ render json: @posts, serializer: CustomArraySerializer, meta: {total: 10}, meta_key: 'meta_object'
351
+ ```
352
+
353
+ The above usage of `:meta_key` will produce the following:
354
+
355
+ ```json
356
+ {
357
+ "meta_object": { "total": 10 },
358
+ "posts": [
359
+ { "title": "Post 1", "body": "Hello!" },
360
+ { "title": "Post 2", "body": "Goodbye!" }
361
+ ]
362
+ }
363
+ ```
364
+
365
+ When using meta information, your serializer cannot have the `{ root: false }` option, as this would lead to
366
+ invalid JSON. If you do not have a root key, the meta information will be ignored.
367
+
368
+ If you would like direct, low-level control of attribute serialization, you can
369
+ completely override the `attributes` method to return the hash you need:
370
+
371
+ ```ruby
372
+ class PersonSerializer < ActiveModel::Serializer
373
+ attributes :first_name, :last_name
374
+
375
+ def attributes
376
+ hash = super
377
+ if scope.admin?
378
+ hash["ssn"] = object.ssn
379
+ hash["secret"] = object.mothers_maiden_name
380
+ end
381
+ hash
382
+ end
383
+ end
384
+ ```
385
+
386
+ ## Associations
387
+
388
+ For specified associations, the serializer will look up the association and
389
+ then serialize each element of the association. For instance, a `has_many
390
+ :comments` association will create a new `CommentSerializer` for each comment
391
+ and use it to serialize the comment.
392
+
393
+ By default, serializers simply look up the association on the original object.
394
+ You can customize this behavior by implementing a method with the name of the
395
+ association and returning a different Array. Often, you will do this to
396
+ customize the objects returned based on the current user (scope).
397
+
398
+ ```ruby
399
+ class PostSerializer < ActiveModel::Serializer
400
+ attributes :id, :title, :body
401
+ has_many :comments
402
+
403
+ # only let the user see comments he created.
404
+ def comments
405
+ object.comments.where(created_by: scope)
406
+ end
407
+ end
408
+ ```
409
+
410
+ As with attributes, you can change the JSON key that the serializer should
411
+ use for a particular association.
412
+
413
+ ```ruby
414
+ class PostSerializer < ActiveModel::Serializer
415
+ attributes :id, :title, :body
416
+
417
+ # look up comments, but use +my_comments+ as the key in JSON
418
+ has_many :comments, root: :my_comments
419
+ end
420
+ ```
421
+
422
+ Also, as with attributes, serializers will execute a filter method to
423
+ determine which associations should be included in the output. For
424
+ example:
425
+
426
+ ```ruby
427
+ class PostSerializer < ActiveModel::Serializer
428
+ attributes :id, :title, :body
429
+ has_many :comments
430
+
431
+ def filter(keys)
432
+ keys.delete :comments if object.comments_disabled?
433
+ keys
434
+ end
435
+ end
436
+ ```
437
+
438
+ Or ...
439
+
440
+ ```ruby
441
+ class PostSerializer < ActiveModel::Serializer
442
+ attributes :id, :title, :body
443
+ has_one :author
444
+ has_many :comments
445
+
446
+ def filter(keys)
447
+ keys.delete :author unless scope.admin?
448
+ keys.delete :comments if object.comments_disabled?
449
+ keys
450
+ end
451
+ end
452
+ ```
453
+
454
+ 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.:
455
+
456
+ ```ruby
457
+ has_many :comments, serializer: CommentShortSerializer
458
+ has_one :reviewer, polymorphic: true
459
+ ```
460
+
461
+ Serializers are only concerned with multiplicity, and not ownership. `belongs_to` ActiveRecord associations can be included using `has_one` in your serializer.
462
+
463
+ NOTE: polymorphic was removed because was only supported for has\_one
464
+ associations and is in the TODO list of the project.
465
+
466
+ ## Embedding Associations
467
+
468
+ By default, associations will be embedded inside the serialized object. So if
469
+ you have a post, the outputted JSON will look like:
470
+
471
+ ```json
472
+ {
473
+ "post": {
474
+ "id": 1,
475
+ "title": "New post",
476
+ "body": "A body!",
477
+ "comments": [
478
+ { "id": 1, "body": "what a dumb post" }
479
+ ]
480
+ }
481
+ }
482
+ ```
483
+
484
+ This is convenient for simple use-cases, but for more complex clients, it is
485
+ better to supply an Array of IDs for the association. This makes your API more
486
+ flexible from a performance standpoint and avoids wasteful duplication.
487
+
488
+ To embed IDs instead of associations, simply use the `embed` class method:
489
+
490
+ ```ruby
491
+ class PostSerializer < ActiveModel::Serializer
492
+ embed :ids
493
+
494
+ attributes :id, :title, :body
495
+ has_many :comments
496
+ end
497
+ ```
498
+
499
+ Now, any associations will be supplied as an Array of IDs:
500
+
501
+ ```json
502
+ {
503
+ "post": {
504
+ "id": 1,
505
+ "title": "New post",
506
+ "body": "A body!",
507
+ "comment_ids": [ 1, 2, 3 ]
508
+ }
509
+ }
510
+ ```
511
+
512
+ Alternatively, you can choose to embed only the ids or the associated objects per association:
513
+
514
+ ```ruby
515
+ class PostSerializer < ActiveModel::Serializer
516
+ attributes :id, :title, :body
517
+
518
+ has_many :comments, embed: :objects
519
+ has_many :tags, embed: :ids
520
+ end
521
+ ```
522
+
523
+ The JSON will look like this:
524
+
525
+ ```json
526
+ {
527
+ "post": {
528
+ "id": 1,
529
+ "title": "New post",
530
+ "body": "A body!",
531
+ "comments": [
532
+ { "id": 1, "body": "what a dumb post" }
533
+ ],
534
+ "tag_ids": [ 1, 2, 3 ]
535
+ }
536
+ }
537
+ ```
538
+
539
+ In addition to supplying an Array of IDs, you may want to side-load the data
540
+ alongside the main object. This makes it easier to process the entire package
541
+ of data without having to recursively scan the tree looking for embedded
542
+ information. It also ensures that associations that are shared between several
543
+ objects (like tags), are only delivered once for the entire payload.
544
+
545
+ You can specify that the data be included like this:
546
+
547
+ ```ruby
548
+ class PostSerializer < ActiveModel::Serializer
549
+ embed :ids, include: true
550
+
551
+ attributes :id, :title, :body
552
+ has_many :comments
553
+ end
554
+ ```
555
+
556
+ Assuming that the comments also `has_many :tags`, you will get a JSON like
557
+ this:
558
+
559
+ ```json
560
+ {
561
+ "post": {
562
+ "id": 1,
563
+ "title": "New post",
564
+ "body": "A body!",
565
+ "comment_ids": [ 1, 2 ]
566
+ },
567
+ "comments": [
568
+ { "id": 1, "body": "what a dumb post", "tag_ids": [ 1, 2 ] },
569
+ { "id": 2, "body": "i liked it", "tag_ids": [ 1, 3 ] },
570
+ ],
571
+ "tags": [
572
+ { "id": 1, "name": "short" },
573
+ { "id": 2, "name": "whiny" },
574
+ { "id": 3, "name": "happy" }
575
+ ]
576
+ }
577
+ ```
578
+
579
+ When side-loading data, your serializer cannot have the `{ root: false }` option,
580
+ as this would lead to invalid JSON. If you do not have a root key, the `include`
581
+ instruction will be ignored
582
+
583
+ You can also specify a different root for the embedded objects than the key
584
+ used to reference them:
585
+
586
+ ```ruby
587
+ class PostSerializer < ActiveModel::Serializer
588
+ embed :ids, include: true
589
+
590
+ attributes :id, :title, :body
591
+ has_many :comments, key: :comment_ids, root: :comment_objects
592
+ end
593
+ ```
594
+
595
+ This would generate JSON that would look like this:
596
+
597
+ ```json
598
+ {
599
+ "post": {
600
+ "id": 1,
601
+ "title": "New post",
602
+ "body": "A body!",
603
+ "comment_ids": [ 1 ]
604
+ },
605
+ "comment_objects": [
606
+ { "id": 1, "body": "what a dumb post" }
607
+ ]
608
+ }
609
+ ```
610
+
611
+ You can also specify a different attribute to use rather than the ID of the
612
+ objects:
613
+
614
+ ```ruby
615
+ class PostSerializer < ActiveModel::Serializer
616
+ embed :ids, include: true
617
+
618
+ attributes :id, :title, :body
619
+ has_many :comments, embed_key: :external_id
620
+ end
621
+ ```
622
+
623
+ This would generate JSON that would look like this:
624
+
625
+ ```json
626
+ {
627
+ "post": {
628
+ "id": 1,
629
+ "title": "New post",
630
+ "body": "A body!",
631
+ "comment_ids": [ "COMM001" ]
632
+ },
633
+ "comments": [
634
+ { "id": 1, "external_id": "COMM001", "body": "what a dumb post" }
635
+ ]
636
+ }
637
+ ```
638
+
639
+ **NOTE**: The `embed :ids` mechanism is primary useful for clients that process
640
+ data in bulk and load it into a local store. For these clients, the ability to
641
+ easily see all of the data per type, rather than having to recursively scan the
642
+ data looking for information, is extremely useful.
643
+
644
+ If you are mostly working with the data in simple scenarios and manually making
645
+ Ajax requests, you probably just want to use the default embedded behavior.
646
+
647
+ ## Customizing Scope
648
+
649
+ In a serializer, `current_user` is the current authorization scope which the controller
650
+ provides to the serializer when you call `render :json`. By default, this is
651
+ `current_user`, but can be customized in your controller by calling
652
+ `serialization_scope`:
653
+
654
+ ```ruby
655
+ class ApplicationController < ActionController::Base
656
+ serialization_scope :current_admin
657
+ end
658
+ ```
659
+
660
+ The above example will also change the scope from `current_user` to
661
+ `current_admin`.
662
+
663
+ Please note that, until now, `serialization_scope` doesn't accept a second
664
+ object with options for specifying which actions should or should not take a
665
+ given scope in consideration.
666
+
667
+ To be clear, it's not possible, yet, to do something like this:
668
+
669
+ ```ruby
670
+ class SomeController < ApplicationController
671
+ serialization_scope :current_admin, except: [:index, :show]
672
+ end
673
+ ```
674
+
675
+ So, in order to have a fine grained control of what each action should take in
676
+ consideration for its scope, you may use something like this:
677
+
678
+ ```ruby
679
+ class CitiesController < ApplicationController
680
+ serialization_scope nil
681
+
682
+ def index
683
+ @cities = City.all
684
+
685
+ render json: @cities, each_serializer: CitySerializer
686
+ end
687
+
688
+ def show
689
+ @city = City.find(params[:id])
690
+
691
+ render json: @city, scope: current_admin
692
+ end
693
+ end
694
+ ```
695
+
696
+ Assuming that the `current_admin` method needs to make a query in the database
697
+ for the current user, the advantage of this approach is that, by setting
698
+ `serialization_scope` to `nil`, the `index` action no longer will need to make
699
+ that query, only the `show` action will.
700
+
701
+ ## Testing
702
+
703
+ In order to test a Serializer, you can just call `.new` on it, passing the object to serialize:
704
+
705
+ ### MiniTest
706
+
707
+ ```ruby
708
+ class TestPostSerializer < Minitest::Test
709
+ def setup
710
+ @serializer = PostSerializer.new Post.new(id: 123, title: 'some title', body: 'some text')
711
+ end
712
+
713
+ def test_special_json_for_api
714
+ assert_equal '{"post":{"id":123,"title":"some title","body":"some text"}}', @serializer.to_json
715
+ end
716
+ ```
717
+
718
+ ### RSpec
719
+
720
+ ```ruby
721
+ describe PostSerializer do
722
+ it "creates special JSON for the API" do
723
+ serializer = PostSerializer.new Post.new(id: 123, title: 'some title', body: 'some text')
724
+ expect(serializer.to_json).to eql('{"post":{"id":123,"title":"some title","body":"some text"}}')
725
+ end
726
+ end
727
+ ```
728
+
729
+ ## Caching
730
+
731
+ NOTE: This functionality was removed from AMS and it's in the TODO list.
732
+ We need to re-think and re-design the caching strategy for the next
733
+ version of AMS.
734
+
735
+ To cache a serializer, call `cached` and define a `cache_key` method:
736
+
737
+ ```ruby
738
+ class PostSerializer < ActiveModel::Serializer
739
+ cached # enables caching for this serializer
740
+
741
+ attributes :title, :body
742
+
743
+ def cache_key
744
+ [object, scope]
745
+ end
746
+ end
747
+ ```
748
+
749
+ The caching interface uses `Rails.cache` under the hood.
750
+
751
+ # ApplicationSerializer
752
+
753
+ 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```:
754
+
755
+ ```ruby
756
+ class ApplicationSerializer < ActiveModel::Serializer
757
+ end
758
+ ```
759
+
760
+ Any newly generated serializers will automatically descend from ApplicationSerializer.
761
+
762
+ ```
763
+ $ rails g serializer post
764
+ ```
765
+
766
+ now generates:
767
+
768
+ ```ruby
769
+ class PostSerializer < ApplicationSerializer
770
+ attributes :id
771
+ end
772
+ ````
773
+
774
+ # Design and Implementation Guidelines
775
+
776
+ ## Keep it Simple
777
+
778
+ `ActiveModel::Serializers` is capable of producing complex JSON views/large object
779
+ trees, and it may be tempting to design in this way so that your client can make
780
+ fewer requests to get data and so that related querying can be optimized.
781
+ However, keeping things simple in your serializers and controllers may
782
+ significantly reduce complexity and maintenance over the long-term development
783
+ of your application. Please consider reducing the complexity of the JSON views
784
+ you provide via the serializers as you build out your application, so that
785
+ controllers/services can be more easily reused without a lot of complexity
786
+ later.
787
+
788
+ ## Performance
789
+
790
+ As you develop your controllers or other code that utilizes serializers, try to
791
+ avoid n+1 queries by ensuring that data loads in an optimal fashion, e.g. if you
792
+ are using ActiveRecord, you might want to use query includes or joins as needed
793
+ to make the data available that the serializer(s) need.