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.
The ActiveModel::ArraySerializer
represent a collection of resources as serializers
and, if there is no serializer, primitives.
The ActiveModel::Adapter
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.
The ActiveModel::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.
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.
However, when a primitive value is an attribute or in a collection, it is not modified.
Internally, if no serializer can be found in the controller, the resource is not decorated by ActiveModelSerializers.
If the collection serializer (ArraySerializer) cannot
identify a serializer for a resource in its collection, it raises NoSerializerError
which is rescued in AcitveModel::Serializer::Reflection#build_association
which sets
the association value directly:
reflection_options[:virtual_value] = association_value.try(:as_json) || association_value
(which is called by the adapter as serializer.associations(*)
.)
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_OPTIONS
. The remaining options are serializer options.
Details:
- ActionController::Serialization
serializable_resource = ActiveModel::SerializableResource.new(resource, options)
1.options
are partitioned intoadapter_opts
and everything else (serializer_opts
). The adapter options keys for the are defined byADAPTER_OPTIONS
.- ActiveModel::SerializableResource
if serializable_resource.serializer?
(there is a serializer for the resource, and an adapter is used.) - Whereserializer?
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)
- from explicit
- 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
1.
serializer_instance = serializer.new(resource, serializer_opts)
2.adapter_instance = ActiveModel::Serializer::Adapter.create(serializer_instance, adapter_opts)
- ActiveModel::Serializer::ArraySerializer#new
- If the
serializer_instance
was aArraySerializer
and the:serializer
serializer_opts is present, then that serializer is passed into each resource. - ActiveModel::Serializer#attributes is used by the adapter to get the attributes for resource as defined by the serializer.
ActiveModelSerializers provides a
ActiveModelSerializers::Model
,
which is a simple serializable PORO (Plain-Old Ruby Object).
ActiveModelSerializers::Model may be used either as a template, or in production code.
class MyModel < ActiveModelSerializers::Model
attr_accessor :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
ActiveModel::SerializableResource.new(MyModel.new(level: 'awesome'), adapter: :json).as_json