Skip to content

Commit

Permalink
fix: format model polymorphic type from resource object type (#1435)
Browse files Browse the repository at this point in the history
* test: failing request posting sti with polymorphic has one
* fix: polymorphic resource assignment
* Add polymorphic_type_for method
* Favor classify over singularize.camelize

---------

Co-authored-by: lgebhardt <[email protected]>
  • Loading branch information
bf4 and lgebhardt committed Apr 18, 2024
1 parent b0137ba commit cc65781
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 10 deletions.
2 changes: 1 addition & 1 deletion lib/jsonapi/acts_as_resource_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion lib/jsonapi/link_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
4 changes: 2 additions & 2 deletions lib/jsonapi/relationship.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
12 changes: 8 additions & 4 deletions lib/jsonapi/resource_common.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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,
Expand Down Expand Up @@ -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
Expand Down
11 changes: 9 additions & 2 deletions test/fixtures/active_record.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -1338,6 +1342,7 @@ class VehicleResource < JSONAPI::Resource
immutable

has_one :person
has_one :imageable, polymorphic: true
attributes :make, :model, :serial_number
end

Expand Down Expand Up @@ -1917,6 +1922,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
Expand Down
41 changes: 41 additions & 0 deletions test/integration/requests/request_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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:
{
Expand Down

0 comments on commit cc65781

Please sign in to comment.