class ActiveModelSerializers::Adapter::JsonApi

Attributes

fieldset[R]

Public Class Methods

default_key_transform() click to toggle source
# File lib/active_model_serializers/adapter/json_api.rb, line 38
def self.default_key_transform
  :dash
end
fragment_cache(cached_hash, non_cached_hash, root = true) click to toggle source
# File lib/active_model_serializers/adapter/json_api.rb, line 42
def self.fragment_cache(cached_hash, non_cached_hash, root = true)
  core_cached       = cached_hash.first
  core_non_cached   = non_cached_hash.first
  no_root_cache     = cached_hash.delete_if { |key, _value| key == core_cached[0] }
  no_root_non_cache = non_cached_hash.delete_if { |key, _value| key == core_non_cached[0] }
  cached_resource   = (core_cached[1]) ? core_cached[1].deep_merge(core_non_cached[1]) : core_non_cached[1]
  hash = root ? { root => cached_resource } : cached_resource

  hash.deep_merge no_root_non_cache.deep_merge no_root_cache
end
new(serializer, options = {}) click to toggle source
# File lib/active_model_serializers/adapter/json_api.rb, line 53
def initialize(serializer, options = {})
  super
  @include_directive = JSONAPI::IncludeDirective.new(options[:include], allow_wildcard: true)
  @fieldset = options[:fieldset] || ActiveModel::Serializer::Fieldset.new(options.delete(:fields))
end

Public Instance Methods

failure_document() click to toggle source

{jsonapi.org/format/#errors JSON API Errors} TODO: look into caching definition:

 toplevel_errors array (required)
 toplevel_meta
 toplevel_jsonapi

structure:

{
  errors: toplevel_errors,
  meta: toplevel_meta,
  jsonapi: toplevel_jsonapi
}.reject! {|_,v| v.nil? }

prs:

https://github.com/rails-api/active_model_serializers/pull/1004
# File lib/active_model_serializers/adapter/json_api.rb, line 174
def failure_document
  hash = {}
  # PR Please :)
  # Jsonapi.add!(hash)

  # toplevel_errors
  # definition:
  #   array of unique items of type 'error'
  # structure:
  #   [
  #     error,
  #     error
  #   ]
  if serializer.respond_to?(:each)
    hash[:errors] = serializer.flat_map do |error_serializer|
      Error.resource_errors(error_serializer, instance_options)
    end
  else
    hash[:errors] = Error.resource_errors(serializer, instance_options)
  end
  hash
end
fragment_cache(cached_hash, non_cached_hash) click to toggle source
# File lib/active_model_serializers/adapter/json_api.rb, line 70
def fragment_cache(cached_hash, non_cached_hash)
  root = !instance_options.include?(:include)
  self.class.fragment_cache(cached_hash, non_cached_hash, root)
end
serializable_hash(*) click to toggle source

{jsonapi.org/format/#crud Requests are transactional, i.e. success or failure} {jsonapi.org/format/#document-top-level data and errors MUST NOT coexist in the same document.}

# File lib/active_model_serializers/adapter/json_api.rb, line 61
def serializable_hash(*)
  document = if serializer.success?
               success_document
             else
               failure_document
             end
  self.class.transform_key_casing!(document, instance_options)
end
success_document() click to toggle source

{jsonapi.org/format/#document-top-level Primary data} definition:

 toplevel_data (required)
 toplevel_included
 toplevel_meta
 toplevel_links
 toplevel_jsonapi

structure:

{
  data: toplevel_data,
  included: toplevel_included,
  meta: toplevel_meta,
  links: toplevel_links,
  jsonapi: toplevel_jsonapi
}.reject! {|_,v| v.nil? }

rubocop:disable Metrics/CyclomaticComplexity

# File lib/active_model_serializers/adapter/json_api.rb, line 91
def success_document
  is_collection = serializer.respond_to?(:each)
  serializers = is_collection ? serializer : [serializer]
  primary_data, included = resource_objects_for(serializers)

  hash = {}
  # toplevel_data
  # definition:
  #   oneOf
  #     resource
  #     array of unique items of type 'resource'
  #     null
  #
  # description:
  #   The document's "primary data" is a representation of the resource or collection of resources
  #   targeted by a request.
  #
  #   Singular: the resource object.
  #
  #   Collection: one of an array of resource objects, an array of resource identifier objects, or
  #   an empty array ([]), for requests that target resource collections.
  #
  #   None: null if the request is one that might correspond to a single resource, but doesn't currently.
  # structure:
  #  if serializable_resource.resource?
  #    resource
  #  elsif serializable_resource.collection?
  #    [
  #      resource,
  #      resource
  #    ]
  #  else
  #    nil
  #  end
  hash[:data] = is_collection ? primary_data : primary_data[0]
  # toplevel_included
  #   alias included
  # definition:
  #   array of unique items of type 'resource'
  #
  # description:
  #   To reduce the number of HTTP requests, servers **MAY** allow
  #   responses that include related resources along with the requested primary
  #   resources. Such responses are called "compound documents".
  # structure:
  #     [
  #       resource,
  #       resource
  #     ]
  hash[:included] = included if included.any?

  Jsonapi.add!(hash)

  if instance_options[:links]
    hash[:links] ||= {}
    hash[:links].update(instance_options[:links])
  end

  if is_collection && serializer.paginated?
    hash[:links] ||= {}
    hash[:links].update(pagination_links_for(serializer))
  end

  hash[:meta] = instance_options[:meta] unless instance_options[:meta].blank?

  hash
end

Private Instance Methods

attributes_for(serializer, fields) click to toggle source

{jsonapi.org/format/#document-resource-object-attributes Document Resource Object Attributes} attributes definition:

JSON Object

patternProperties:

^(?!relationships$|links$)\\w[-\\w_]*$

description:

Members of the attributes object ("attributes") represent information about the resource
object in which it's defined.
Attributes may contain any valid JSON value

structure:

{
  foo: 'bar'
}
# File lib/active_model_serializers/adapter/json_api.rb, line 296
def attributes_for(serializer, fields)
  serializer.attributes(fields).except(:id)
end
data_for(serializer, include_slice) click to toggle source
# File lib/active_model_serializers/adapter/json_api.rb, line 338
def data_for(serializer, include_slice)
  data = serializer.fetch(self) do
    resource_object = ResourceIdentifier.new(serializer, instance_options).as_json
    break nil if resource_object.nil?

    requested_fields = fieldset && fieldset.fields_for(resource_object[:type])
    attributes = attributes_for(serializer, requested_fields)
    resource_object[:attributes] = attributes if attributes.any?
    resource_object
  end
  data.tap do |resource_object|
    next if resource_object.nil?
    # NOTE(BF): the attributes are cached above, separately from the relationships, below.
    requested_associations = fieldset.fields_for(resource_object[:type]) || '*'
    relationships = relationships_for(serializer, requested_associations, include_slice)
    resource_object[:relationships] = relationships if relationships.any?
  end
end
meta_for(serializer) click to toggle source

{jsonapi.org/format/#document-meta Docment Meta}

# File lib/active_model_serializers/adapter/json_api.rb, line 529
def meta_for(serializer)
  Meta.new(serializer).as_json
end
process_relationship(serializer, include_slice) click to toggle source
# File lib/active_model_serializers/adapter/json_api.rb, line 269
def process_relationship(serializer, include_slice)
  if serializer.respond_to?(:each)
    serializer.each { |s| process_relationship(s, include_slice) }
    return
  end
  return unless serializer && serializer.object
  return unless process_resource(serializer, false, include_slice)

  process_relationships(serializer, include_slice)
end
process_relationships(serializer, include_slice) click to toggle source
# File lib/active_model_serializers/adapter/json_api.rb, line 262
def process_relationships(serializer, include_slice)
  serializer.associations(include_slice).each do |association|
    # TODO(BF): Process relationship without evaluating lazy_association
    process_relationship(association.lazy_association.serializer, include_slice[association.key])
  end
end
process_resource(serializer, primary, include_slice = {}) click to toggle source
# File lib/active_model_serializers/adapter/json_api.rb, line 248
def process_resource(serializer, primary, include_slice = {})
  resource_identifier = ResourceIdentifier.new(serializer, instance_options).as_json
  return false unless @resource_identifiers.add?(resource_identifier)

  resource_object = resource_object_for(serializer, include_slice)
  if primary
    @primary << resource_object
  else
    @included << resource_object
  end

  true
end
relationships_for(serializer, requested_associations, include_slice) click to toggle source

{jsonapi.org/format/#document-resource-object-relationships Document Resource Object Relationship} relationships definition:

JSON Object

patternProperties:

^\\w[-\\w_]*$"

properties:

data : relationshipsData
links
meta

description:

Members of the relationships object ("relationships") represent references from the
resource object in which it's defined to other resource objects."

structure:

{
  links: links,
  meta: meta,
  data: relationshipsData
}.reject! {|_,v| v.nil? }

prs:

links
  [x] https://github.com/rails-api/active_model_serializers/pull/1454
meta
  [x] https://github.com/rails-api/active_model_serializers/pull/1454
polymorphic
  [ ] https://github.com/rails-api/active_model_serializers/pull/1420

relationshipsData definition:

oneOf
  relationshipToOne
  relationshipToMany

description:

Member, whose value represents "resource linkage"

structure:

if has_one?
  relationshipToOne
else
  relationshipToMany
end

definition:

anyOf
  null
  linkage

relationshipToOne description:

References to other resource objects in a to-one ("relationship"). Relationships can be
specified by including a member in a resource's links object.

None: Describes an empty to-one relationship.

structure:

if has_related?
  linkage
else
  nil
end

relationshipToMany definition:

array of unique items of type 'linkage'

description:

An array of objects each containing "type" and "id" members for to-many relationships

structure:

[
  linkage,
  linkage
]

prs:

polymorphic
  [ ] https://github.com/rails-api/active_model_serializers/pull/1282

linkage definition:

type (required) : String
id   (required) : String
meta

description:

The "type" and "id" to non-empty members.

structure:

{
  type: 'required-type',
  id: 'required-id',
  meta: meta
}.reject! {|_,v| v.nil? }
# File lib/active_model_serializers/adapter/json_api.rb, line 452
def relationships_for(serializer, requested_associations, include_slice)
  include_directive = JSONAPI::IncludeDirective.new(
    requested_associations,
    allow_wildcard: true
  )
  serializer.associations(include_directive, include_slice).each_with_object({}) do |association, hash|
    hash[association.key] = Relationship.new(serializer, instance_options, association).as_json
  end
end
resource_object_for(serializer, include_slice = {}) click to toggle source

{jsonapi.org/format/#document-resource-objects Document Resource Objects}

# File lib/active_model_serializers/adapter/json_api.rb, line 301
def resource_object_for(serializer, include_slice = {})
  resource_object = data_for(serializer, include_slice)

  # toplevel_links
  # definition:
  #   allOf
  #      ☐ links
  #      ☐ pagination
  #
  # description:
  #  Link members related to the primary data.
  # structure:
  #   links.merge!(pagination)
  # prs:
  #   https://github.com/rails-api/active_model_serializers/pull/1247
  #   https://github.com/rails-api/active_model_serializers/pull/1018
  if (links = links_for(serializer)).any?
    resource_object ||= {}
    resource_object[:links] = links
  end

  # toplevel_meta
  #   alias meta
  # definition:
  #   meta
  # structure
  #   {
  #     :'git-ref' => 'abc123'
  #   }
  if (meta = meta_for(serializer)).present?
    resource_object ||= {}
    resource_object[:meta] = meta
  end

  resource_object
end
resource_objects_for(serializers) click to toggle source

{jsonapi.org/format/#document-resource-objects Primary data} resource definition:

JSON Object

properties:

type (required) : String
id   (required) : String
attributes
relationships
links
meta

description:

"Resource objects" appear in a JSON API document to represent resources

structure:

{
  type: 'admin--some-user',
  id: '1336',
  attributes: attributes,
  relationships: relationships,
  links: links,
  meta: meta,
}.reject! {|_,v| v.nil? }

prs:

type
  https://github.com/rails-api/active_model_serializers/pull/1122
  [x] https://github.com/rails-api/active_model_serializers/pull/1213
  https://github.com/rails-api/active_model_serializers/pull/1216
  https://github.com/rails-api/active_model_serializers/pull/1029
links
  [x] https://github.com/rails-api/active_model_serializers/pull/1246
  [x] url helpers https://github.com/rails-api/active_model_serializers/issues/1269
meta
  [x] https://github.com/rails-api/active_model_serializers/pull/1340
# File lib/active_model_serializers/adapter/json_api.rb, line 238
def resource_objects_for(serializers)
  @primary = []
  @included = []
  @resource_identifiers = Set.new
  serializers.each { |serializer| process_resource(serializer, true, @include_directive) }
  serializers.each { |serializer| process_relationships(serializer, @include_directive) }

  [@primary, @included]
end