ActiveModelSerializers
Build Status | |
Code Quality | |
Issue Stats | Pulse |
About
ActiveModelSerializers brings convention over configuration to your JSON generation.
ActiveModelSerializers works through two components: serializers and adapters.
Serializers describe which attributes and relationships should be serialized.
Adapters describe how attributes and relationships should be serialized.
SerializableResource co-ordinates the resource, Adapter and Serializer to produce the
resource serialization. The serialization has the #as_json
, #to_json
and #serializable_hash
methods used by the Rails JSON Renderer. (SerializableResource actually delegates
these methods to the adapter.)
By default ActiveModelSerializers will use the Attributes Adapter (no JSON root). But we strongly advise you to use JsonApi Adapter, which follows 1.0 of the format specified in jsonapi.org/format. Check how to change the adapter in the sections below.
0.10.x
is not backward compatible with 0.9.x
nor 0.8.x
.
0.10.x
is based on the 0.8.0
code, but with a more flexible
architecture. We'd love your help. Learn how you can help here.
Installation
Add this line to your application's Gemfile:
gem 'active_model_serializers', '~> 0.10.0'
And then execute:
$ bundle
Getting Started
See Getting Started for the nuts and bolts.
More information is available in the Guides and High-level behavior.
Getting Help
If you find a bug, please report an Issue and see our contributing guide.
If you have a question, please post to Stack Overflow.
If you'd like to chat, we have a community slack.
Thanks!
Documentation
If you're reading this at https://github.com/rails-api/active_model_serializers you are
reading documentation for our master
, which may include features that have not
been released yet. Please see below for the documentation relevant to you.
- 0.10 (0-10-stable) Documentation
- 0.10.10 (latest release) Documentation
- 0.9 (0-9-stable) Documentation
- 0.8 (0-8-stable) Documentation
High-level behavior
Choose an adapter from adapters:
ActiveModelSerializers.config.adapter = :json_api # Default: `:attributes`
Given a serializable model:
# either
class SomeResource < ActiveRecord::Base
# columns: title, body
end
# or
class SomeResource < ActiveModelSerializers::Model
attributes :title, :body
end
And initialized as:
resource = SomeResource.new(title: 'ActiveModelSerializers', body: 'Convention over configuration')
Given a serializer for the serializable model:
class SomeSerializer < ActiveModel::Serializer
attribute :title, key: :name
attributes :body
end
The model can be serialized as:
= {}
serialization = ActiveModelSerializers::SerializableResource.new(resource, )
serialization.to_json
serialization.as_json
SerializableResource delegates to the adapter, which it builds as:
= {}
adapter = ActiveModelSerializers::Adapter.create(serializer, )
adapter.to_json
adapter.as_json
adapter.serializable_hash
The adapter formats the serializer's attributes and associations (a.k.a. includes):
= {}
serializer = SomeSerializer.new(resource, )
serializer.attributes
serializer.associations
Architecture
This section focuses on architecture the 0.10.x version of ActiveModelSerializers. If you are interested in the architecture of the 0.8 or 0.9 versions, please refer to the 0.8 README or 0.9 README.
The original design is also available here.
ActiveModel::Serializer
An ActiveModel::Serializer
wraps a serializable resource
and exposes an attributes
method, among a few others.
It allows you to specify which attributes and associations should be represented in the serializatation of the resource.
It requires an adapter to transform its attributes into a JSON document; it cannot be serialized itself.
It may be useful to think of it as a
presenter.
ActiveModel::CollectionSerializer
The ActiveModel::CollectionSerializer
represents a collection of resources as serializers
and, if there is no serializer, primitives.
ActiveModelSerializers::Adapter::Base
The ActiveModelSerializers::Adapter::Base
describes the structure of the JSON document generated from a
serializer. For example, the Attributes
example represents each serializer as its
unmodified attributes. The JsonApi
adapter represents the serializer as a JSON
API document.
ActiveModelSerializers::SerializableResource
The ActiveModelSerializers::SerializableResource
acts to coordinate the serializer(s) and adapter
to an object that responds to to_json
, and as_json
. It is used in the controller to
encapsulate the serialization resource when rendered. However, it can also be used on its own
to serialize a resource outside of a controller, as well.
Primitive handling
Definitions: A primitive is usually a String or Array. There is no serializer
defined for them; they will be serialized when the resource is converted to JSON (as_json
or
to_json
). (The below also applies for any object with no serializer.)
- ActiveModelSerializers doesn't handle primitives passed to
render json:
at all.
Internally, if no serializer can be found in the controller, the resource is not decorated by ActiveModelSerializers.
- However, when a primitive value is an attribute or in a collection, it is not modified.
When serializing a collection and the collection serializer (CollectionSerializer) cannot
identify a serializer for a resource in its collection, it throws :no_serializer
.
For example, when caught by Reflection#build_association
, and the association value is set directly:
[:virtual_value] = association_value.try(:as_json) || association_value
(which is called by the adapter as serializer.associations(*)
.)
How options are parsed
High-level overview:
- For a collection
:serializer
specifies the collection serializer and:each_serializer
specifies the serializer for each resource in the collection.
- For a single resource, the
:serializer
option is the resource serializer. - Options are partitioned in serializer options and adapter options. Keys for adapter options are specified by
ADAPTER_OPTION_KEYS
. The remaining options are serializer options.
Details:
- ActionController::Serialization
serializable_resource = ActiveModelSerializers::SerializableResource.new(resource, options)
options
are partitioned intoadapter_opts
and everything else (serializer_opts
). Theadapter_opts
keys are defined inActiveModelSerializers::SerializableResource::ADAPTER_OPTION_KEYS
.
- ActiveModelSerializers::SerializableResource
if serializable_resource.serializer?
(there is a serializer for the resource, and an adapter is used.)- Where
serializer?
isuse_adapter? && !!(serializer)
- Where
use_adapter?
: 'True when no explicit adapter given, or explicit value is truthy (non-nil); False when explicit adapter is falsy (nil or false)' - Where
serializer
: - from explicit
:serializer
option, else - implicitly from resource
ActiveModel::Serializer.serializer_for(resource)
- Where
- A side-effect of checking
serializer
is:- The
:serializer
option is removed from the serializer_opts hash - If the
:each_serializer
option is present, it is removed from the serializer_opts hash and set as the:serializer
option
- The
- The serializer and adapter are created as
serializer_instance = serializer.new(resource, serializer_opts)
adapter_instance = ActiveModel::Serializer::Adapter.create(serializer_instance, adapter_opts)
- ActiveModel::Serializer::CollectionSerializer#new
- If the
serializer_instance
was aCollectionSerializer
and the:serializer
serializer_opts is present, then that serializer is passed into each resource.
- If the
- ActiveModel::Serializer#attributes is used by the adapter to get the attributes for resource as defined by the serializer.
(In Rails, the options
are also passed to the as_json(options)
or to_json(options)
methods on the resource serialization by the Rails JSON renderer. They are, therefore, important
to know about, but not part of ActiveModelSerializers.)
What does a 'serializable resource' look like?
ActiveModelSerializers provides a
ActiveModelSerializers::Model
,
which is a simple serializable PORO (Plain-Old Ruby Object).
ActiveModelSerializers::Model
may be used either as a reference implementation, or in production code.
class MyModel < ActiveModelSerializers::Model
attributes :id, :name, :level
end
The default serializer for MyModel
would be MyModelSerializer
whether MyModel is an
ActiveRecord::Base object or not.
Outside of the controller the rules are exactly the same as for records. For example:
render json: MyModel.new(level: 'awesome'), adapter: :json
would be serialized the same as
ActiveModelSerializers::SerializableResource.new(MyModel.new(level: 'awesome'), adapter: :json).as_json
Semantic Versioning
This project adheres to semver
Contributing
See CONTRIBUTING.md