+
Back to Guides
+
+
Serializers
+
+
Given a serializer class:
+
+
class SomeSerializer < ActiveModel::Serializer
+end
+
+
+
The following methods may be defined in it:
+
+
Attributes
+
+
::attributes
+
+
Serialization of the resource title and body
+
+
| In Serializer | #attributes | |—————————- |————-| | attributes
+:title, :body | { title: 'Some Title', body: 'Some
+Body' } | attributes :title, :body
def
+body "Special #{object.body}" end | { title:
+'Some Title', body: 'Special Some Body' }
+
+
::attribute
+
+
Serialization of the resource title
+
+
| In Serializer | #attributes | |—————————- |————-| | attribute
+:title | { title: 'Some Title' } |
+attribute :title, key: :name | { name: 'Some
+Title' } | attribute :title { 'A Different
+Title'} | { title: 'A Different Title' } |
+attribute :title
def title 'A Different
+Title' end | { title: 'A Different Title' }
+
+
An if or unless option can make an attribute
+conditional. It takes a symbol of a method name on the serializer, or a
+lambda literal.
+
+
e.g.
+
+
attribute :private_data, if: :is_current_user?
+attribute :another_private_data, if: -> { scope.admin? }
+
+def is_current_user?
+ object.id == current_user.id
+end
+
+
+
Associations
+
+
The interface for associations is, generically:
+
+
+association_type(association_name, options, &block)
+
+
+
Where:
+
-
+
association_type may be has_one,
+has_many, belongs_to.
+ -
+
association_name is a method name the serializer calls.
+ -
+
optional: options may be:
+ -
+
key: The name used for the serialized association.
+ -
+
serializer:
+ -
+
if:
+ -
+
unless:
+ -
+
virtual_value:
+ -
+
polymorphic: defines if polymorphic relation type should be
+nested in serialized association.
+ -
+
optional: &block is a context that returns the
+association's attributes.
+ -
+
prevents association_name method from being called.
+ -
+
return value of block is used as the association value.
+ -
+
yields the serializer to the block.
+ -
+
include_data false prevents the data key from
+being rendered in the JSON API relationship.
+
+
+
::has_one
+
+
e.g.
+
+
has_one :bio
+has_one :blog, key: :site
+has_one :maker, virtual_value: { id: 1 }
+
+has_one :blog do |serializer|
+ serializer.cached_blog
+end
+
+def cached_blog
+ cache_store.fetch("cached_blog:#{object.updated_at}") do
+ Blog.find(object.blog_id)
+ end
+end
+
+
+
has_one :blog, if: :show_blog?
+
+def show_blog?
+ scope.admin?
+end
+
+
+
::has_many
+
+
e.g.
+
+
has_many :comments
+has_many :comments, key: :reviews
+has_many :comments, serializer: CommentPreviewSerializer
+has_many :reviews, virtual_value: [{ id: 1 }, { id: 2 }]
+has_many :comments, key: :last_comments do
+ last(1)
+end
+
+
+
::belongs_to
+
+
e.g.
+
+
belongs_to :author, serializer: AuthorPreviewSerializer
+belongs_to :author, key: :writer
+belongs_to :post
+belongs_to :blog
+def blog
+ Blog.new(id: 999, name: 'Custom blog')
+end
+
+
+
Polymorphic Relationships
+
+
Polymorphic relationships are serialized by specifying the relationship,
+like any other association. For example:
+
+
class PictureSerializer < ActiveModel::Serializer
+ has_one :imageable
+end
+
+
+
For more context, see the tests for each adapter.
+
+
Caching
+
+
::cache
+
+
e.g.
+
+
cache key: 'post', expires_in: 0.1, skip_digest: true
+cache expires_in: 1.day, skip_digest: true
+cache key: 'writer', skip_digest: true
+cache only: [:name], skip_digest: true
+cache except: [:content], skip_digest: true
+cache key: 'blog'
+cache only: [:id]
+
+
+
#cache_key
+
+
e.g.
+
+
def cache_key
+ "#{self.class.name.downcase}/#{self.id}"
+end
+
+
+
Other
+
+
::type
+
+
The ::type method defines the JSONAPI type
+that will be rendered for this serializer. It either takes a
+String or Symbol as parameter.
+
+
Note: This method is useful only when using the :json_api
+adapter.
+
+
Examples: ruby class UserProfileSerializer <
+ActiveModel::Serializer type 'profile' end class
+AuthorProfileSerializer < ActiveModel::Serializer type :profile end
+
+
+
With the :json_api adapter, the previous serializers would be
+rendered as:
+
+
{
+ "data": {
+ "id": "1",
+ "type": "profile"
+ }
+}
+
+
+
::link
+
+
link :self do
+ href "https://example.com/link_author/#{object.id}"
+end
+link :author { link_author_url(object) }
+link :link_authors { link_authors_url }
+link :other, 'https://example.com/resource'
+link :posts { link_author_posts_url(object) }
+
+
+
#object
+
+
The object being serialized.
+
+
#root
+
+
PR please :)
+
+
#scope
+
+
Allows you to include in the serializer access to an external method.
+
+
It's intended to provide an authorization context to the serializer, so
+that you may e.g. show an admin all comments on a post, else only published
+comments.
+
-
+
scope is a method on the serializer instance that comes from
+options[:scope]. It may be nil.
+ -
+
scope_name is an option passed to the new serializer
+(options[:scope_name]). The serializer defines a method with
+that name that calls the scope, e.g. def current_user;
+scope; end. Note: it does not define the method if the serializer
+instance responds to it.
+
+
+
That's a lot of words, so here's some examples:
+
+
First, let's assume the serializer is instantiated in the controller,
+since that's the usual scenario. We'll refer to the serialization
+context as controller.
+
+
| options | Serializer#scope | method definition | |——– |
+——————|——————–| | scope: current_user, scope_name:
+:current_user | current_user |
+Serializer#current_user calls
+controller.current_user | scope: view_context,
+scope_name: :view_context | view_context |
+Serializer#view_context calls
+controller.view_context
+
+
We can take advantage of the scope to customize the objects returned based
+on the current user (scope).
+
+
For example, we can limit the posts the current user sees to those they
+created:
+
+
class PostSerializer < ActiveModel::Serializer
+ attributes :id, :title, :body
+
+ has_many :comments do
+ object..where(created_by: current_user)
+ end
+end
+
+
+
Whether you write the method as above or as
+object.comments.where(created_by: scope) is a matter of
+preference (assuming scope_name has been set).
+
+
Controller Authorization Context
+
+
In the controller, the scope/scope_name options are equal to the serialization_scopemethod,
+which is :current_user, by default.
+
+
Specifically, the scope_name is defaulted to
+:current_user, and may be set as serialization_scope
+:view_context. The scope is set to
+send(scope_name) when scope_name is present and
+the controller responds to scope_name.
+
+
Thus, in a serializer, the controller provides current_user as
+the current authorization scope when you call render :json.
+
+
IMPORTANT: Since the scope is set at render, you may want
+to customize it so that current_user isn't called on every
+request. This was also
+a problem in 0.9.
+
+
We can change the scope from current_user to
+view_context.
+
+
class SomeController < ActionController::Base
++ serialization_scope :view_context
+
+ def current_user
+ User.new(id: 2, name: 'Bob', admin: true)
+ end
+
+ def edit
+ user = User.new(id: 1, name: 'Pete')
+ render json: user, serializer: AdminUserSerializer, adapter: :json_api
+ end
+end
+
+
We could then use the controller method view_context in our
+serializer, like so:
+
+
class AdminUserSerializer < ActiveModel::Serializer
+ attributes :id, :name, :can_edit
+
+ def can_edit?
++ view_context.current_user.admin?
+ end
+end
+
+
+
So that when we render the #edit action, we'll get
+
+
{"data":{"id":"1","type":"users","attributes":{"name":"Pete","can_edit":true}}}
+
+
+
Where can_edit is
+view_context.current_user.admin? (true).
+
+
#read_attribute_for_serialization(key)
+
+
The serialized value for a given key. e.g.
+read_attribute_for_serialization(:title) #=> 'Hello
+World'
+
+
#links
+
+
PR please :)
+
+
#json_key
+
+
PR please :)
+
+
Examples
+
+
Given two models, a Post(title: string, body: text) and a
+Comment(name: string, body: text, post_id: integer), you will
+have two serializers:
+
+
class PostSerializer < ActiveModel::Serializer
+ cache key: 'posts', expires_in: 3.hours
+ attributes :title, :body
+
+ has_many :comments
+end
+
+
+
and
+
+
class CommentSerializer < ActiveModel::Serializer
+ attributes :name, :body
+
+ belongs_to :post
+end
+
+
+
Generally speaking, you, as a user of ActiveModelSerializers, will write
+(or generate) these serializer classes.
+
+
More Info
+
+
For more information, see the
+Serializer class on GitHub
+
+
Overriding association methods
+
+
To override an association, call has_many,
+has_one or belongs_to with a block:
+
+
class PostSerializer < ActiveModel::Serializer
+ has_many :comments do
+ object..active
+ end
+end
+
+
+
Overriding attribute methods
+
+
To override an attribute, call attribute with a block:
+
+
class PostSerializer < ActiveModel::Serializer
+ attribute :body do
+ object.body.downcase
+ end
+end
+
+
+
Overriding association serializer lookup
+
+
If you want to define a specific serializer lookup for your associations,
+you can override the ActiveModel::Serializer.serializer_for
+method to return a serializer class based on defined conditions.
+
+
class MySerializer < ActiveModel::Serializer
+ def self.serializer_for(model, options)
+ return SparseAdminSerializer if model.class == 'Admin'
+ super
+ end
+
+ end
+
+