Guides > Architecture

Architecture

This focuses on 0.10.x version. For the architectures of 0.8 or 0.9 versions, please refer to 0.8 README or 0.9 README. The original design is also available here.

ActiveModel::Serializer

  • Wraps a serializable resource and exposes an attributes method, among a few others
  • Allows specifying which attributes and associations should be represented in the serializatation of the resource
  • Requires an adapter to transform its attributes into a JSON document; it cannot be serialized itself
  • Think of it as a case-specific presenter

ActiveModel::ArraySerializer

A collection of resources as serializers or as primitives if there are no serializers

ActiveModel::Adapter

Defines the structure of the JSON document to be generated from a serializer. For example:

  • Attributes Adapter represents each serializer as its unmodified attributes
  • JsonApi Adapter represents the serializer as a JSON API document

ActiveModelSerializers::SerializableResource

  • Acts to coordinate the serializer(s) and adapter to an object that responds to to_json, and as_json
  • Used in the controller to encapsulate the serialization resource when rendered
  • Can be used on its own to serialize a resource outside of a controller
  • Characteristics
    • An ActiveRecord::Base object
    • Any Ruby object that passes the Lint code

ActiveModelSerializers::Model

ActiveModelSerializers provides a ActiveModelSerializers::Model as a simple serializable PORO (Plain-Old Ruby Object) that can 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

is equivelant to

ActiveModelSerializers::SerializableResource.new(MyModel.new(level: 'awesome'), adapter: :json).as_json

Primitive Handling

  • 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. 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(*).

Parsing Options

Summary

  • For a single resource the :serializer option is the resource serializer
  • For a collection
    • :serializer specifies the collection serializer
    • :each_serializer specifies the serializer for each resource in the collection.
  • Options are partitioned in Serializer Options and Adapter Options
  • Keys for Adapter Options are specified by

Details

  1. ActionController::Serialization

    1. serializable_resource = ActiveModelSerializers::SerializableResource.new(resource, options)

      1. options are partitioned into adapter_opts and everything else (serializer_opts).
      2. The adapter_opts keys are defined in ActiveModelSerializers::SerializableResource::ADAPTER_OPTION_KEYS.
  2. ActiveModelSerializers::SerializableResource

    1. if serializable_resource.serializer? (there is a serializer for the resource, and an adapter is used.)

      • Where serializer? is use_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)
    2. 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
    3. 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)
  3. ActiveModel::Serializer::ArraySerializer#new

    1. If the serializer_instance was a ArraySerializer and the :serializer serializer_opts
    2. is present, then that serializer is passed into each resource.
  4. ActiveModel::Serializer#attributes is used by the adapter to get the attributes for

  5. Resource as defined by the serializer.

results matching ""

    No results matching ""