From 5e1e138d47566a34407e1dc4a5e8e99275a3df7e Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Sun, 29 Oct 2017 17:13:34 -0500 Subject: [PATCH 1/4] Refactor ResourceIdentifier --- .../adapter/json_api/resource_identifier.rb | 32 ++++++++----------- 1 file changed, 14 insertions(+), 18 deletions(-) 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..1c29263c 100644 --- a/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +++ b/lib/active_model_serializers/adapter/json_api/resource_identifier.rb @@ -2,23 +2,8 @@ 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 @@ -30,6 +15,17 @@ module ActiveModelSerializers } end + def self.raw_type_from_serializer_object(object) + class_name = object.class.name # should use model_name + serializer_type = class_name.underscore + singularize = ActiveModelSerializers.config.jsonapi_resource_type == :singular + inflection = singularize ? :singularize : :pluralize + serializer_type = ActiveSupport::Inflector.public_send(inflection, serializer_type) + serializer_type + .gsub!('/'.freeze, ActiveModelSerializers.config.jsonapi_namespace_separator) + serializer_type + end + # {http://jsonapi.org/format/#document-resource-identifier-objects Resource Identifier Objects} def initialize(serializer, options) @id = id_for(serializer) @@ -48,7 +44,7 @@ module ActiveModelSerializers private def type_for(serializer, transform_options) - self.class.type_for(serializer.object.class.name, serializer._type, transform_options) + self.class.type_for(serializer, serializer._type, transform_options) end def id_for(serializer) From 0fcb8a6cce90b51451519692d7cd716752bb8bdf Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Sun, 29 Oct 2017 19:26:14 -0500 Subject: [PATCH 2/4] Fix polymorphic belongs_to tests; passes on v0.10.5 --- test/adapter/polymorphic_test.rb | 47 ++++++++++++++++++++++++++++++++ test/fixtures/active_record.rb | 4 +-- 2 files changed, 49 insertions(+), 2 deletions(-) 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 From cf29db34c64ee7bd7ca73b97b23169fcac9c8dc8 Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Sun, 29 Oct 2017 19:40:46 -0500 Subject: [PATCH 3/4] Fix JSON:API polymorphic type regression from v0.10.5 --- .../adapter/json_api/relationship.rb | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/active_model_serializers/adapter/json_api/relationship.rb b/lib/active_model_serializers/adapter/json_api/relationship.rb index 45cddb89..5d06e546 100644 --- a/lib/active_model_serializers/adapter/json_api/relationship.rb +++ b/lib/active_model_serializers/adapter/json_api/relationship.rb @@ -46,7 +46,14 @@ module ActiveModelSerializers if association.belongs_to? && parent_serializer.object.respond_to?(association.reflection.foreign_key) id = parent_serializer.read_attribute_for_serialization(association.reflection.foreign_key) - type = association.reflection.type.to_s + 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. + serializer = association.lazy_association.serializer + type = serializer.json_key + else + type = 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 From a0de45a4d8ca2727b148a6c5456bf6db80152f6d Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Sun, 29 Oct 2017 19:40:04 -0500 Subject: [PATCH 4/4] Fix JSON:API: for_type_and_id should always inflect_type Should Serializer._type ever be inflected? Right now, it won't be, but association.serializer._type will be inflected. That's the current behavior. --- .../adapter/json_api/relationship.rb | 24 +++++++++++-------- .../adapter/json_api/resource_identifier.rb | 18 +++++++++----- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/lib/active_model_serializers/adapter/json_api/relationship.rb b/lib/active_model_serializers/adapter/json_api/relationship.rb index 5d06e546..e2957958 100644 --- a/lib/active_model_serializers/adapter/json_api/relationship.rb +++ b/lib/active_model_serializers/adapter/json_api/relationship.rb @@ -43,17 +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) - 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. - serializer = association.lazy_association.serializer - type = serializer.json_key - else - type = association.reflection.type.to_s - end + 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 @@ -93,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 1c29263c..3ff7118d 100644 --- a/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +++ b/lib/active_model_serializers/adapter/json_api/resource_identifier.rb @@ -9,6 +9,7 @@ module ActiveModelSerializers 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) @@ -17,13 +18,17 @@ module ActiveModelSerializers def self.raw_type_from_serializer_object(object) class_name = object.class.name # should use model_name - serializer_type = class_name.underscore + 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 - serializer_type = ActiveSupport::Inflector.public_send(inflection, serializer_type) - serializer_type - .gsub!('/'.freeze, ActiveModelSerializers.config.jsonapi_namespace_separator) - serializer_type + ActiveSupport::Inflector.public_send(inflection, type) end # {http://jsonapi.org/format/#document-resource-identifier-objects Resource Identifier Objects} @@ -44,7 +49,8 @@ module ActiveModelSerializers private def type_for(serializer, transform_options) - self.class.type_for(serializer, serializer._type, transform_options) + serializer_type = serializer._type + self.class.type_for(serializer, serializer_type, transform_options) end def id_for(serializer)