active_model_serializers_rails_2.3 0.9.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
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.