diff --git a/lib/jsonapi/acts_as_resource_controller.rb b/lib/jsonapi/acts_as_resource_controller.rb index 1ef1707c..e87a31d6 100644 --- a/lib/jsonapi/acts_as_resource_controller.rb +++ b/lib/jsonapi/acts_as_resource_controller.rb @@ -164,7 +164,7 @@ def base_url end def resource_klass_name - @resource_klass_name ||= "#{self.class.name.underscore.sub(/_controller$/, '').singularize}_resource".camelize + @resource_klass_name ||= "#{self.class.name.underscore.sub(/_controller$/, '').classify}Resource" end def verify_content_type_header diff --git a/lib/jsonapi/link_builder.rb b/lib/jsonapi/link_builder.rb index 23b94a4d..63b160fc 100644 --- a/lib/jsonapi/link_builder.rb +++ b/lib/jsonapi/link_builder.rb @@ -92,7 +92,7 @@ def build_engine begin unless scopes.empty? - "#{ scopes.first.to_s.camelize }::Engine".safe_constantize + "#{ scopes.first.to_s.classify }::Engine".safe_constantize end # :nocov: diff --git a/lib/jsonapi/relationship.rb b/lib/jsonapi/relationship.rb index 9d7ffc46..bff416da 100644 --- a/lib/jsonapi/relationship.rb +++ b/lib/jsonapi/relationship.rb @@ -155,7 +155,7 @@ class ToOne < Relationship def initialize(name, options = {}) super - @class_name = options.fetch(:class_name, name.to_s.camelize) + @class_name = options.fetch(:class_name, name.to_s.classify) @foreign_key ||= "#{name}_id".to_sym @foreign_key_on = options.fetch(:foreign_key_on, :self) # if parent_resource @@ -231,7 +231,7 @@ class ToMany < Relationship def initialize(name, options = {}) super - @class_name = options.fetch(:class_name, name.to_s.camelize.singularize) + @class_name = options.fetch(:class_name, name.to_s.classify) @foreign_key ||= "#{name.to_s.singularize}_ids".to_sym @reflect = options.fetch(:reflect, true) == true # if parent_resource diff --git a/lib/jsonapi/resource_common.rb b/lib/jsonapi/resource_common.rb index b841b732..9ec4e0d1 100644 --- a/lib/jsonapi/resource_common.rb +++ b/lib/jsonapi/resource_common.rb @@ -561,7 +561,7 @@ def resource_klass_for_model(model) end def _resource_name_from_type(type) - "#{type.to_s.underscore.singularize}_resource".camelize + "#{type.to_s.classify}Resource" end def resource_type_for(model) @@ -578,6 +578,10 @@ def resource_type_for(model) end end + def polymorphic_type_for(model_name) + model_name&.to_s&.classify + end + attr_accessor :_attributes, :_relationships, :_type, @@ -1200,12 +1204,12 @@ def define_relationship_methods(relationship_name, relationship_klass, options) def define_foreign_key_setter(relationship) if relationship.polymorphic? define_on_resource "#{relationship.foreign_key}=" do |v| - _model.method("#{relationship.foreign_key}=").call(v[:id]) - _model.public_send("#{relationship.polymorphic_type}=", v[:type]) + _model.public_send("#{relationship.foreign_key}=", v[:id]) + _model.public_send("#{relationship.polymorphic_type}=", self.class.polymorphic_type_for(v[:type])) end else define_on_resource "#{relationship.foreign_key}=" do |value| - _model.method("#{relationship.foreign_key}=").call(value) + _model.public_send("#{relationship.foreign_key}=", value) end end relationship.foreign_key diff --git a/test/fixtures/active_record.rb b/test/fixtures/active_record.rb index c08aa896..92281e2a 100644 --- a/test/fixtures/active_record.rb +++ b/test/fixtures/active_record.rb @@ -275,6 +275,7 @@ t.string :drive_layout t.string :serial_number t.integer :person_id + t.references :imageable, polymorphic: true, index: true t.timestamps null: false end @@ -734,6 +735,9 @@ class Picture < ActiveRecord::Base class Vehicle < ActiveRecord::Base belongs_to :person + belongs_to :imageable, polymorphic: true + # belongs_to :document, -> { where( pictures: { imageable_type: 'Document' } ) }, foreign_key: 'imageable_id' + # belongs_to :product, -> { where( pictures: { imageable_type: 'Product' } ) }, foreign_key: 'imageable_id' end class Car < Vehicle @@ -743,13 +747,13 @@ class Boat < Vehicle end class Document < ActiveRecord::Base - has_many :pictures, as: :imageable + has_many :pictures, as: :imageable # polymorphic belongs_to :author, class_name: 'Person', foreign_key: 'author_id' has_one :file_properties, as: :fileable end class Product < ActiveRecord::Base - has_many :pictures, as: :imageable + has_many :pictures, as: :imageable # polymorphic belongs_to :designer, class_name: 'Person', foreign_key: 'designer_id' has_one :file_properties, as: :fileable end @@ -1336,6 +1340,7 @@ class VehicleResource < JSONAPI::Resource immutable has_one :person + has_one :imageable, polymorphic: true attributes :make, :model, :serial_number end @@ -1915,6 +1920,8 @@ class PreferencesResource < PreferencesResource; end class SectionResource < SectionResource; end class TagResource < TagResource; end class CommentResource < CommentResource; end + class DocumentResource < DocumentResource; end + class ProductResource < ProductResource; end class VehicleResource < VehicleResource; end class CarResource < CarResource; end class BoatResource < BoatResource; end diff --git a/test/integration/requests/request_test.rb b/test/integration/requests/request_test.rb index aa9989c6..d6bdf7f0 100644 --- a/test/integration/requests/request_test.rb +++ b/test/integration/requests/request_test.rb @@ -359,6 +359,47 @@ def test_post_polymorphic_with_has_many_relationship assert_equal Car, person.vehicles.fourth.class end + def test_post_sti_polymorphic_with_has_one_relationship + post '/cars', params: + { + 'data' => { + 'type' => 'cars', + 'attributes' => { + 'make' => 'Mazda', + 'model' => 'Miata MX5', + 'drive_layout' => 'Front Engine RWD', + 'serial_number' => '32432adfsfdysua', + }, + 'relationships' => { + 'person' => { + 'data' => { + 'type' => 'people', 'id' => '1001', + } + }, + 'imageable' => { + 'data' => { + 'type' => 'products', 'id' => '1', + } + }, + } + } + }.to_json, + headers: { + 'CONTENT_TYPE' => JSONAPI::MEDIA_TYPE, + 'Accept' => JSONAPI::MEDIA_TYPE + } + + assert_jsonapi_response 201 + + body = response.parsed_body + car = Vehicle.find(body.dig("data", "id")) + + assert_equal "Car", car.type + assert_equal "Mazda", car.make + assert_equal Product, car.imageable.class + assert_equal Person, car.person.class + end + def test_post_polymorphic_invalid_with_wrong_type post '/people', params: {