Skip to content

Commit c6eee64

Browse files
committed
Final attempt.
1 parent 45d3bb9 commit c6eee64

File tree

1 file changed

+42
-44
lines changed

1 file changed

+42
-44
lines changed

README.textile

Lines changed: 42 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ This guide describes how to use Active Model serializers to build non-trivial JS
1111
This guide covers an intermediate topic and assumes familiarity with Rails conventions. It is suitable for applications that expose a
1212
JSON API that may return different results based on the authorization status of the user.
1313

14-
endprologue.
15-
1614
h3. Serialization
1715

1816
By default, Active Record objects can serialize themselves into JSON by using the `to_json` method. This method takes a series of additional
@@ -37,7 +35,7 @@ h3. The Most Basic Serializer
3735

3836
A basic serializer is a simple Ruby object named after the model class it is serializing.
3937

40-
<code><pre>
38+
<pre><code>
4139
class PostSerializer
4240
def initialize(post, scope)
4341
@post, @scope = post, scope
@@ -47,22 +45,22 @@ class PostSerializer
4745
{ post: { title: @post.name, body: @post.body } }
4846
end
4947
end
50-
</pre></code>
48+
</code></pre>
5149

5250
A serializer is initialized with two parameters: the model object it should serialize and an authorization scope. By default, the
5351
authorization scope is the current user (+current_user+) but you can use a different object if you want. The serializer also
5452
implements an +as_json+ method, which returns a Hash that will be sent to the JSON encoder.
5553

5654
Rails will transparently use your serializer when you use +render :json+ in your controller.
5755

58-
<code><pre>
56+
<pre><code>
5957
class PostsController < ApplicationController
6058
def show
6159
@post = Post.find(params[:id])
6260
render json: @post
6361
end
6462
end
65-
</pre></code>
63+
</code></pre>
6664

6765
Because +respond_with+ uses +render :json+ under the hood for JSON requests, Rails will automatically use your serializer when
6866
you use +respond_with+ as well.
@@ -72,7 +70,7 @@ h4. +serializable_hash+
7270
In general, you will want to implement +serializable_hash+ and +as_json+ to allow serializers to embed associated content
7371
directly. The easiest way to implement these two methods is to have +as_json+ call +serializable_hash+ and insert the root.
7472

75-
<code><pre>
73+
<pre><code>
7674
class PostSerializer
7775
def initialize(post, scope)
7876
@post, @scope = post, scope
@@ -86,14 +84,14 @@ class PostSerializer
8684
{ post: serializable_hash }
8785
end
8886
end
89-
</pre></code>
87+
</code></pre>
9088

9189
h4. Authorization
9290

9391
Let's update our serializer to include the email address of the author of the post, but only if the current user has superuser
9492
access.
9593

96-
<code><pre>
94+
<pre><code>
9795
class PostSerializer
9896
def initialize(post, scope)
9997
@post, @scope = post, scope
@@ -122,14 +120,14 @@ private
122120
@scope.superuser?
123121
end
124122
end
125-
</pre></code>
123+
</code></pre>
126124

127125
h4. Testing
128126

129127
One benefit of encapsulating our objects this way is that it becomes extremely straight-forward to test the serialization
130128
logic in isolation.
131129

132-
<code><pre>
130+
<pre><code>
133131
require "ostruct"
134132

135133
class PostSerializerTest < ActiveSupport::TestCase
@@ -159,7 +157,7 @@ class PostSerializerTest < ActiveSupport::TestCase
159157
assert_empty hash
160158
end
161159
end
162-
</pre></code>
160+
</code></pre>
163161

164162
It's important to note that serializer objects define a clear interface specifically for serializing an existing object.
165163
In this case, the serializer expects to receive a post object with +name+, +body+ and +email+ attributes and an authorization
@@ -170,7 +168,7 @@ the serializer doesn't need to concern itself with how the authorization scope d
170168
whether it is set. In general, you should document these requirements in your serializer files and programatically via tests.
171169
The documentation library +YARD+ provides excellent tools for describing this kind of requirement:
172170

173-
<code><pre>
171+
<pre><code>
174172
class PostSerializer
175173
# @param [~body, ~title, ~email] post the post to serialize
176174
# @param [~super] scope the authorization scope for this serializer
@@ -180,7 +178,7 @@ class PostSerializer
180178

181179
# ...
182180
end
183-
</pre></code>
181+
</code></pre>
184182

185183
h3. Attribute Sugar
186184

@@ -191,7 +189,7 @@ For example, you will sometimes want to simply include a number of existing attr
191189
JSON. In the above example, the +title+ and +body+ attributes were always included in the JSON. Let's see how to use
192190
+ActiveModel::Serializer+ to simplify our post serializer.
193191

194-
<code><pre>
192+
<pre><code>
195193
class PostSerializer < ActiveModel::Serializer
196194
attributes :title, :body
197195

@@ -214,7 +212,7 @@ private
214212
@scope.superuser?
215213
end
216214
end
217-
</pre></code>
215+
</code></pre>
218216

219217
First, we specified the list of included attributes at the top of the class. This will create an instance method called
220218
+attributes+ that extracts those attributes from the post model.
@@ -225,7 +223,7 @@ Next, we use the attributes methood in our +serializable_hash+ method, which all
225223
earlier. We could also eliminate the +as_json+ method, as +ActiveModel::Serializer+ provides a default +as_json+ method for
226224
us that calls our +serializable_hash+ method and inserts a root. But we can go a step further!
227225

228-
<code><pre>
226+
<pre><code>
229227
class PostSerializer < ActiveModel::Serializer
230228
attributes :title, :body
231229

@@ -240,7 +238,7 @@ private
240238
@scope.superuser?
241239
end
242240
end
243-
</pre></code>
241+
</code></pre>
244242

245243
The superclass provides a default +initialize+ method as well as a default +serializable_hash+ method, which uses
246244
+attributes+. We can call +super+ to get the hash based on the attributes we declared, and then add in any additional
@@ -253,7 +251,7 @@ h3. Associations
253251
In most JSON APIs, you will want to include associated objects with your serialized object. In this case, let's include
254252
the comments with the current post.
255253

256-
<code><pre>
254+
<pre><code>
257255
class PostSerializer < ActiveModel::Serializer
258256
attributes :title, :body
259257
has_many :comments
@@ -269,11 +267,11 @@ private
269267
@scope.superuser?
270268
end
271269
end
272-
</pre></code>
270+
</code></pre>
273271

274272
The default +serializable_hash+ method will include the comments as embedded objects inside the post.
275273

276-
<code><pre>
274+
<pre><code>
277275
{
278276
post: {
279277
title: "Hello Blog!",
@@ -286,14 +284,14 @@ The default +serializable_hash+ method will include the comments as embedded obj
286284
]
287285
}
288286
}
289-
</pre></code>
287+
</code></pre>
290288

291289
Rails uses the same logic to generate embedded serializations as it does when you use +render :json+. In this case,
292290
because you didn't define a +CommentSerializer+, Rails used the default +as_json+ on your comment object.
293291

294292
If you define a serializer, Rails will automatically instantiate it with the existing authorization scope.
295293

296-
<code><pre>
294+
<pre><code>
297295
class CommentSerializer
298296
def initialize(comment, scope)
299297
@comment, @scope = comment, scope
@@ -307,26 +305,26 @@ class CommentSerializer
307305
{ comment: serializable_hash }
308306
end
309307
end
310-
</pre></code>
308+
</code></pre>
311309

312310
If we define the above comment serializer, the outputted JSON will change to:
313311

314-
<code><pre>
312+
<pre><code>
315313
{
316314
post: {
317315
title: "Hello Blog!",
318316
body: "This is my first post. Isn't it fabulous!",
319317
comments: [{ title: "Awesome" }]
320318
}
321319
}
322-
</pre></code>
320+
</code></pre>
323321

324322
Let's imagine that our comment system allows an administrator to kill a comment, and we only want to allow
325323
users to see the comments they're entitled to see. By default, +has_many :comments+ will simply use the
326324
+comments+ accessor on the post object. We can override the +comments+ accessor to limit the comments used
327325
to just the comments we want to allow for the current user.
328326

329-
<code><pre>
327+
<pre><code>
330328
class PostSerializer < ActiveModel::Serializer
331329
attributes :title. :body
332330
has_many :comments
@@ -346,7 +344,7 @@ private
346344
@scope.superuser?
347345
end
348346
end
349-
</pre></code>
347+
</code></pre>
350348

351349
+ActiveModel::Serializer+ will still embed the comments, but this time it will use just the comments
352350
for the current user.
@@ -361,7 +359,7 @@ build up the hash manually.
361359

362360
For example, let's say our front-end expects the posts and comments in the following format:
363361

364-
<code><pre>
362+
<pre><code>
365363
{
366364
post: {
367365
id: 1
@@ -382,11 +380,11 @@ For example, let's say our front-end expects the posts and comments in the follo
382380
}
383381
]
384382
}
385-
</pre></code>
383+
</code></pre>
386384

387385
We could achieve this with a custom +as_json+ method. We will also need to define a serializer for comments.
388386

389-
<code><pre>
387+
<pre><code>
390388
class CommentSerializer < ActiveModel::Serializer
391389
attributes :id, :title, :body
392390

@@ -422,7 +420,7 @@ private
422420
@scope.superuser?
423421
end
424422
end
425-
</pre></code>
423+
</code></pre>
426424

427425
Here, we used two convenience methods: +associations+ and +association_ids+. The first,
428426
+associations+, creates a hash of all of the define associations, using their defined
@@ -444,7 +442,7 @@ For instance, we might want to provide the full comment when it is requested dir
444442
but only its title when requested as part of the post. To achieve this, you can define
445443
a serializer for associated objects nested inside the main serializer.
446444

447-
<code><pre>
445+
<pre><code>
448446
class PostSerializer < ActiveModel::Serializer
449447
class CommentSerializer < ActiveModel::Serializer
450448
attributes :id, :title
@@ -453,7 +451,7 @@ class PostSerializer < ActiveModel::Serializer
453451
# same as before
454452
# ...
455453
end
456-
</pre></code>
454+
</code></pre>
457455

458456
In other words, if a +PostSerializer+ is trying to serialize comments, it will first
459457
look for +PostSerializer::CommentSerializer+ before falling back to +CommentSerializer+
@@ -470,11 +468,11 @@ its +current_user+ method and pass that along to the serializer's initializer.
470468
If you want to change that behavior, simply use the +serialization_scope+ class
471469
method.
472470

473-
<code><pre>
471+
<pre><code>
474472
class PostsController < ApplicationController
475473
serialization_scope :current_app
476474
end
477-
</pre></code>
475+
</code></pre>
478476

479477
You can also implement an instance method called (no surprise) +serialization_scope+,
480478
which allows you to define a dynamic authorization scope based on the current request.
@@ -491,19 +489,19 @@ outside a request.
491489
For instance, if you want to generate the JSON representation of a post for a user outside
492490
of a request:
493491

494-
<code><pre>
492+
<pre><code>
495493
user = get_user # some logic to get the user in question
496494
PostSerializer.new(post, user).to_json # reliably generate JSON output
497-
</pre></code>
495+
</code></pre>
498496

499497
If you want to generate JSON for an anonymous user, you should be able to use whatever
500498
technique you use in your application to generate anonymous users outside of a request.
501499
Typically, that means creating a new user and not saving it to the database:
502500

503-
<code><pre>
501+
<pre><code>
504502
user = User.new # create a new anonymous user
505503
PostSerializer.new(post, user).to_json
506-
</pre></code>
504+
</code></pre>
507505

508506
In general, the better you encapsulate your authorization logic, the more easily you
509507
will be able to use the serializer outside of the context of a request. For instance,
@@ -521,7 +519,7 @@ as the root).
521519

522520
For example, an Array of post objects would serialize as:
523521

524-
<code><pre>
522+
<pre><code>
525523
{
526524
posts: [
527525
{
@@ -533,12 +531,12 @@ For example, an Array of post objects would serialize as:
533531
}
534532
]
535533
}
536-
</pre></code>
534+
</code></pre>
537535

538536
If you want to change the behavior of serialized Arrays, you need to create
539537
a custom Array serializer.
540538

541-
<code><pre>
539+
<pre><code>
542540
class ArraySerializer < ActiveModel::ArraySerializer
543541
def serializable_array
544542
serializers.map do |serializer|
@@ -552,7 +550,7 @@ class ArraySerializer < ActiveModel::ArraySerializer
552550
hash
553551
end
554552
end
555-
</pre></code>
553+
</code></pre>
556554

557555
When generating embedded associations using the +associations+ helper inside a
558556
regular serializer, it will create a new <code>ArraySerializer</code> with the

0 commit comments

Comments
 (0)