diff --git a/lib/active_model_serializers/adapter/json_api/relationship.rb b/lib/active_model_serializers/adapter/json_api/relationship.rb index 45cddb89..e2957958 100644 --- a/lib/active_model_serializers/adapter/json_api/relationship.rb +++ b/lib/active_model_serializers/adapter/json_api/relationship.rb @@ -43,10 +43,16 @@ module ActiveModelSerializers end def data_for_one(association) - if association.belongs_to? && - parent_serializer.object.respond_to?(association.reflection.foreign_key) + if belongs_to_id_on_self?(association) id = parent_serializer.read_attribute_for_serialization(association.reflection.foreign_key) - type = association.reflection.type.to_s + 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 @@ -86,6 +92,11 @@ module ActiveModelSerializers meta = association.meta meta.respond_to?(:call) ? parent_serializer.instance_eval(&meta) : meta end + + def belongs_to_id_on_self?(association) + association.belongs_to? && + parent_serializer.object.respond_to?(association.reflection.foreign_key) + end end end end diff --git a/lib/active_model_serializers/adapter/json_api/resource_identifier.rb b/lib/active_model_serializers/adapter/json_api/resource_identifier.rb index 3a235f2b..3ff7118d 100644 --- a/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +++ b/lib/active_model_serializers/adapter/json_api/resource_identifier.rb @@ -2,34 +2,35 @@ module ActiveModelSerializers module Adapter class JsonApi class ResourceIdentifier - def self.type_for(class_name, serializer_type = nil, transform_options = {}) - if serializer_type - raw_type = serializer_type - else - inflection = - if ActiveModelSerializers.config.jsonapi_resource_type == :singular - :singularize - else - :pluralize - end - - raw_type = class_name.underscore - raw_type = ActiveSupport::Inflector.public_send(inflection, raw_type) - raw_type - .gsub!('/'.freeze, ActiveModelSerializers.config.jsonapi_namespace_separator) - raw_type - end + def self.type_for(serializer, serializer_type = nil, transform_options = {}) + raw_type = serializer_type ? serializer_type : raw_type_from_serializer_object(serializer.object) JsonApi.send(:transform_key_casing!, raw_type, transform_options) end def self.for_type_with_id(type, id, options) return nil if id.blank? + type = inflect_type(type) { id: id.to_s, type: type_for(:no_class_needed, type, options) } end + def self.raw_type_from_serializer_object(object) + class_name = object.class.name # should use model_name + raw_type = class_name.underscore + raw_type = inflect_type(raw_type) + raw_type + .gsub!('/'.freeze, ActiveModelSerializers.config.jsonapi_namespace_separator) + raw_type + end + + def self.inflect_type(type) + singularize = ActiveModelSerializers.config.jsonapi_resource_type == :singular + inflection = singularize ? :singularize : :pluralize + ActiveSupport::Inflector.public_send(inflection, type) + end + # {http://jsonapi.org/format/#document-resource-identifier-objects Resource Identifier Objects} def initialize(serializer, options) @id = id_for(serializer) @@ -48,7 +49,8 @@ module ActiveModelSerializers private def type_for(serializer, transform_options) - self.class.type_for(serializer.object.class.name, serializer._type, transform_options) + serializer_type = serializer._type + self.class.type_for(serializer, serializer_type, transform_options) end def id_for(serializer) diff --git a/test/adapter/polymorphic_test.rb b/test/adapter/polymorphic_test.rb index 87d5ff51..61bad810 100644 --- a/test/adapter/polymorphic_test.rb +++ b/test/adapter/polymorphic_test.rb @@ -165,6 +165,53 @@ module ActiveModel assert_equal(expected, serialization(@picture, :json_api)) end + + def test_json_api_serialization_with_polymorphic_belongs_to + expected = { + data: { + id: '1', + type: 'poly-tags', + attributes: { phrase: 'foo' }, + relationships: { + :"object-tags" => { + data: [ + { id: '1', type: 'object-tags' }, + { id: '5', type: 'object-tags' } + ] + } + } + }, + included: [ + { + id: '1', + type: 'object-tags', + relationships: { + taggable: { + data: { id: '42', type: 'employees' } + } + } + }, + { + id: '42', + type: 'employees' + }, + { + id: '5', + type: 'object-tags', + relationships: { + taggable: { + data: { id: '1', type: 'pictures' } + } + } + }, + { + id: '1', + type: 'pictures' + } + ] + } + assert_equal(expected, tag_serialization(:json_api)) + end end end end diff --git a/test/fixtures/active_record.rb b/test/fixtures/active_record.rb index 9dc3830d..9a0567c2 100644 --- a/test/fixtures/active_record.rb +++ b/test/fixtures/active_record.rb @@ -89,7 +89,7 @@ class ObjectTag < ActiveRecord::Base end class PolymorphicObjectTagSerializer < ActiveModel::Serializer attributes :id - has_many :taggable, serializer: PolymorphicSimpleSerializer, polymorphic: true + belongs_to :taggable, serializer: PolymorphicSimpleSerializer, polymorphic: true end class PolyTag < ActiveRecord::Base @@ -109,5 +109,5 @@ class PolymorphicHasManySerializer < ActiveModel::Serializer end class PolymorphicBelongsToSerializer < ActiveModel::Serializer attributes :id, :title - has_one :imageable, serializer: PolymorphicHasManySerializer, polymorphic: true + belongs_to :imageable, serializer: PolymorphicHasManySerializer, polymorphic: true end