class ActiveModelSerializers::Adapter::JsonApi
def self.default_key_transform
def self.default_key_transform :dash end
def self.fragment_cache(cached_hash, non_cached_hash, root = true)
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
def attributes_for(serializer, fields)
foo: 'bar'
{
structure:
Attributes may contain any valid JSON value
object in which it's defined.
Members of the attributes object ("attributes") represent information about the resource
description:
^(?!relationships$|links$)\\w[-\\w_]*$
patternProperties:
JSON Object
definition:
attributes
{http://jsonapi.org/format/#document-resource-object-attributes Document Resource Object Attributes}
def attributes_for(serializer, fields) serializer.attributes(fields).except(:id) end
def failure_document
prs:
}.reject! {|_,v| v.nil? }
jsonapi: toplevel_jsonapi
meta: toplevel_meta,
errors: toplevel_errors,
{
structure:
☐ toplevel_jsonapi
☐ toplevel_meta
☑ toplevel_errors array (required)
definition:
TODO: look into caching
{http://jsonapi.org/format/#errors JSON API Errors}
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
def fragment_cache(cached_hash, non_cached_hash)
def fragment_cache(cached_hash, non_cached_hash) root = !instance_options.include?(:include) self.class.fragment_cache(cached_hash, non_cached_hash, root) end
def initialize(serializer, options = {})
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
def links_for(serializer)
related: link
self: 'http://example.com/etc',
{
structure:
itself.
a client to remove an `author` from an `article` without deleting the people resource
URL allows the client to directly manipulate the relationship. For example, it would allow
A `self` member’s value is a URL for the relationship itself (a "relationship URL"). This
in a resource's links object.
Relationships may be to-one or to-many. Relationships can be specified by including a member
A resource object **MAY** contain references to other resource objects ("relationships").
description:
related : link
self : URI
properties:
JSON Object
definition:
links
{http://jsonapi.org/format/#document-links Document Links}
def links_for(serializer) serializer._links.each_with_object({}) do |(name, value), hash| result = Link.new(serializer, value).as_json hash[name] = result if result end end
def meta_for(serializer)
def meta_for(serializer) Meta.new(serializer).as_json end
def pagination_links_for(serializer)
prs:
end
nil
else
'http://example.com/some-page?page[number][x]'
if has_page?
structure:
The
description:
null
URI
oneOf
definition:
pageObject
}
next: pageObject
prev: pageObject,
last: pageObject,
first: pageObject,
{
structure:
next : pageObject
prev : pageObject
last : pageObject
first : pageObject
definition:
pagination
{http://jsonapi.org/format/#fetching-pagination Pagination Links}
def pagination_links_for(serializer) PaginationLinks.new(serializer.object, instance_options).as_json end
def process_relationship(serializer, include_slice)
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
def process_relationships(serializer, include_slice)
def process_relationships(serializer, include_slice) serializer.associations(include_slice).each do |association| process_relationship(association.serializer, include_slice[association.key]) end end
def process_resource(serializer, primary, include_slice = {})
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
def relationships_for(serializer, requested_associations, include_slice)
meta: meta
id: 'required-id',
type: 'required-type',
{
structure:
The "type" and "id" to non-empty members.
description:
meta
id (required) : String
type (required) : String
definition:
linkage
[ ] https://github.com/rails-api/active_model_serializers/pull/1282
polymorphic
prs:
]
linkage
linkage,
[
structure:
An array of objects each containing "type" and "id" members for to-many relationships
description:
array of unique items of type 'linkage'
definition:
relationshipToMany
end
nil
else
linkage
if has_related?
structure:
None: Describes an empty to-one relationship.
specified by including a member in a resource's links object.
References to other resource objects in a to-one ("relationship"). Relationships can be
description:
relationshipToOne
linkage
null
anyOf
definition:
end
relationshipToMany
else
relationshipToOne
if has_one?
structure:
Member, whose value represents "resource linkage"
description:
relationshipToMany
relationshipToOne
oneOf
definition:
relationshipsData
[ ] https://github.com/rails-api/active_model_serializers/pull/1420
polymorphic
[x] https://github.com/rails-api/active_model_serializers/pull/1454
meta
[x] https://github.com/rails-api/active_model_serializers/pull/1454
links
prs:
}.reject! {|_,v| v.nil? }
data: relationshipsData
meta: meta,
links: links,
{
structure:
resource object in which it's defined to other resource objects."
Members of the relationships object ("relationships") represent references from the
description:
meta
links
data : relationshipsData
properties:
^\\w[-\\w_]*$"
patternProperties:
JSON Object
definition:
relationships
{http://jsonapi.org/format/#document-resource-object-relationships Document Resource Object Relationship}
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
def resource_object_for(serializer, include_slice = {})
def resource_object_for(serializer, include_slice = {}) resource_object = serializer.fetch(self) do resource_object = ResourceIdentifier.new(serializer, instance_options).as_json 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 requested_associations = fieldset.fields_for(resource_object[:type]) || '*' relationships = relationships_for(serializer, requested_associations, include_slice) resource_object[:relationships] = relationships if relationships.any? links = links_for(serializer) # 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 resource_object[:links] = links if links.any? # toplevel_meta # alias meta # definition: # meta # structure # { # :'git-ref' => 'abc123' # } meta = meta_for(serializer) resource_object[:meta] = meta unless meta.blank? resource_object end
def resource_objects_for(serializers)
meta
[x] url helpers https://github.com/rails-api/active_model_serializers/issues/1269
[x] https://github.com/rails-api/active_model_serializers/pull/1246
links
https://github.com/rails-api/active_model_serializers/pull/1029
https://github.com/rails-api/active_model_serializers/pull/1216
[x] https://github.com/rails-api/active_model_serializers/pull/1213
https://github.com/rails-api/active_model_serializers/pull/1122
type
prs:
}.reject! {|_,v| v.nil? }
meta: meta,
links: links,
relationships: relationships,
attributes: attributes,
id: '1336',
type: 'admin--some-user',
{
structure:
"Resource objects" appear in a JSON API document to represent resources
description:
meta
links
relationships
attributes
id (required) : String
type (required) : String
properties:
JSON Object
definition:
resource
{http://jsonapi.org/format/#document-resource-objects Primary data}
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
def serializable_hash(*)
{http://jsonapi.org/format/#crud Requests are transactional, i.e. success or failure}
def serializable_hash(*) document = if serializer.success? success_document else failure_document end self.class.transform_key_casing!(document, instance_options) end
def success_document
}.reject! {|_,v| v.nil? }
jsonapi: toplevel_jsonapi
links: toplevel_links,
meta: toplevel_meta,
included: toplevel_included,
data: toplevel_data,
{
structure:
☑ toplevel_jsonapi
☑ toplevel_links
☑ toplevel_meta
☐ toplevel_included
☐ toplevel_data (required)
definition:
{http://jsonapi.org/format/#document-top-level Primary data}
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