[Back to Guides](../README.md)
# Serializers
Given a serializer class:
```ruby
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.
```ruby
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.
```ruby
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
```
```ruby
has_one :blog, if: :show_blog?
# you can also use a string or lambda
# has_one :blog, if: 'scope.admin?'
# has_one :blog, if: -> (serializer) { serializer.scope.admin? }
# has_one :blog, if: -> { scope.admin? }
def show_blog?
scope.admin?
end
```
#### ::has_many
e.g.
```ruby
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.
```ruby
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:
```ruby
class PictureSerializer < ActiveModel::Serializer
has_one :imageable
end
```
You can specify the serializers by [overriding serializer_for](serializers.md#overriding-association-serializer-lookup). For more context about polymorphic relationships, see the [tests](../../test/adapter/polymorphic_test.rb) for each adapter.
### Caching
#### ::cache
e.g.
```ruby
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.
```ruby
# Uses a custom non-time-based cache key
def cache_key
"#{self.class.name.downcase}/#{self.id}"
end
```
### Other
#### ::type
When using the `:json_api` adapter, the `::type` method defines the JSONAPI [type](http://jsonapi.org/format/#document-resource-object-identification) that will be rendered for this serializer.
When using the `:json` adapter, the `::type` method defines the name of the root element.
It either takes a `String` or `Symbol` as parameter.
Note: This method is useful only when using the `:json_api` or `:json` adapter.
Examples:
```ruby
class UserProfileSerializer < ActiveModel::Serializer
type 'profile'
attribute :name
end
class AuthorProfileSerializer < ActiveModel::Serializer
type :profile
attribute :name
end
```
With the `:json_api` adapter, the previous serializers would be rendered as:
``` json
{
"data": {
"id": "1",
"type": "profile",
"attributes": {
"name": "Julia"
}
}
}
```
With the `:json` adapter, the previous serializer would be rendered as:
``` json
{
"profile": {
"name": "Julia"
}
}
```
#### ::link
```ruby
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
Resource root which is included in `JSON` adapter. As you can see at [Adapters Document](adapters.md), `Attribute` adapter (default) and `JSON API` adapter does not include root at top level.
By default, the resource root comes from the `model_name` of the serialized object's class.
There are several ways to specify root:
* [Overriding the root key](rendering.md#overriding-the-root-key)
* [Setting `type`](serializers.md#type)
* Specifying the `root` option, e.g. `root: 'specific_name'`, during the serializer's initialization:
```ruby
ActiveModelSerializers::SerializableResource.new(foo, root: 'bar')
```
#### #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:
```ruby
class PostSerializer < ActiveModel::Serializer
attributes :id, :title, :body
# scope comments to those created_by the current user
has_many :comments do
object.comments.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_scope`method](https://github.com/rails-api/active_model_serializers/blob/d02cd30fe55a3ea85e1d351b6e039620903c1871/lib/action_controller/serialization.rb#L13-L20),
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](https://github.com/rails-api/active_model_serializers/pull/1252#issuecomment-159810477)
in [`0.9`](https://github.com/rails-api/active_model_serializers/tree/0-9-stable#customizing-scope).
We can change the scope from `current_user` to `view_context`.
```diff
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:
```diff
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
```json
{"data":{"id":"1","type":"users","attributes":{"name":"Pete","can_edit":true}}}
```
Where `can_edit` is `view_context.current_user.admin?` (true).
You can also tell what to set as `serialization_scope` for specific actions.
For example, use `admin_user` only for `Admin::PostSerializer` and `current_user` for rest.
```ruby
class PostsController < ActionController::Base
before_action only: :edit do
self.class.serialization_scope :admin_user
end
def show
render json: @post, serializer: PostSerializer
end
def edit
@post.save
render json: @post, serializer: Admin::PostSerializer
end
private
def admin_user
User.new(id: 2, name: 'Bob', admin: true)
end
def current_user
User.new(id: 2, name: 'Bob', admin: false)
end
end
```
#### #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:
```ruby
class PostSerializer < ActiveModel::Serializer
cache key: 'posts', expires_in: 3.hours
attributes :title, :body
has_many :comments
end
```
and
```ruby
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](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model/serializer.rb)
## Overriding association methods
To override an association, call `has_many`, `has_one` or `belongs_to` with a block:
```ruby
class PostSerializer < ActiveModel::Serializer
has_many :comments do
object.comments.active
end
end
```
## Overriding attribute methods
To override an attribute, call `attribute` with a block:
```ruby
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.
```ruby
class MySerializer < ActiveModel::Serializer
def self.serializer_for(model, options)
return SparseAdminSerializer if model.class == 'Admin'
super
end
# the rest of the serializer
end
```