# 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