lib/active_model_serializers/adapter/json_api/relationship.rb
# frozen_string_literal: true
module ActiveModelSerializers
module Adapter
class JsonApi
class Relationship
# {http://jsonapi.org/format/#document-resource-object-related-resource-links Document Resource Object Related Resource Links}
# {http://jsonapi.org/format/#document-links Document Links}
# {http://jsonapi.org/format/#document-resource-object-linkage Document Resource Relationship Linkage}
# {http://jsonapi.org/format/#document-meta Document Meta}
def initialize(parent_serializer, serializable_resource_options, association)
@parent_serializer = parent_serializer
@association = association
@serializable_resource_options = serializable_resource_options
end
def as_json
hash = {}
hash[:data] = data_for(association) if association.include_data?
links = links_for(association)
hash[:links] = links if links.any?
meta = meta_for(association)
hash[:meta] = meta if meta
hash[:meta] = {} if hash.empty?
hash
end
protected
attr_reader :parent_serializer, :serializable_resource_options, :association
private
# TODO(BF): Avoid db hit on belong_to_ releationship by using foreign_key on self
def data_for(association)
if association.collection?
data_for_many(association)
else
data_for_one(association)
end
end
def data_for_one(association)
if belongs_to_id_on_self?(association)
id = parent_serializer.read_attribute_for_serialization(association.reflection.foreign_key)
type =
if association.polymorphic?
# We can't infer resource type for polymorphic relationships from the serializer.
# We can ONLY know a polymorphic resource type by inspecting each resource.
association.lazy_association.serializer.json_key
else
association.reflection.type.to_s
end
ResourceIdentifier.for_type_with_id(type, id, serializable_resource_options)
else
# TODO(BF): Process relationship without evaluating lazy_association
serializer = association.lazy_association.serializer
if (virtual_value = association.virtual_value)
virtual_value
elsif serializer && association.object
ResourceIdentifier.new(serializer, serializable_resource_options).as_json
else
nil
end
end
end
def data_for_many(association)
# TODO(BF): Process relationship without evaluating lazy_association
collection_serializer = association.lazy_association.serializer
if collection_serializer.respond_to?(:each)
collection_serializer.map do |serializer|
ResourceIdentifier.new(serializer, serializable_resource_options).as_json
end
elsif (virtual_value = association.virtual_value)
virtual_value
else
[]
end
end
def links_for(association)
association.links.each_with_object({}) do |(key, value), hash|
result = Link.new(parent_serializer, value).as_json
hash[key] = result if result
end
end
def meta_for(association)
meta = association.meta
meta.respond_to?(:call) ? parent_serializer.instance_eval(&meta) : meta
end
def belongs_to_id_on_self?(association)
parent_serializer.config.jsonapi_use_foreign_key_on_belongs_to_relationship &&
association.belongs_to? &&
parent_serializer.object.respond_to?(association.reflection.foreign_key)
end
end
end
end
end