From 38c1d5a70b482631391142bb4275ba58eada66cd Mon Sep 17 00:00:00 2001 From: Joshua Young Date: Thu, 11 Jul 2024 11:22:53 +0530 Subject: [PATCH] [Fix #52304] Avoid computing `klass` if reflection is a `belongs_to` in `ActiveRecord::AutosaveAssociation#inverse_belongs_to_association_for` --- .../lib/active_record/autosave_association.rb | 3 +- .../test/cases/autosave_association_test.rb | 24 ++++++++- activerecord/test/models/chef.rb | 52 +++++++++++++++++++ activerecord/test/models/drink_designer.rb | 1 + 4 files changed, 77 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index a0926471b5..95ad3a013d 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -467,7 +467,8 @@ def save_has_one_association(reflection) end def inverse_belongs_to_association_for(reflection, record) - reflection.inverse_of && + !reflection.belongs_to? && + reflection.inverse_of && reflection.inverse_of.belongs_to? && record.association(reflection.inverse_of.name) end diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb index 2f99d067f1..8e57efa809 100644 --- a/activerecord/test/cases/autosave_association_test.rb +++ b/activerecord/test/cases/autosave_association_test.rb @@ -305,7 +305,7 @@ def test_callbacks_firing_order_on_save end def test_callbacks_on_child_when_parent_autosaves_child - eye = Eye.create(iris: Iris.new) + eye = Eye.create!(iris: Iris.new) assert_equal 1, eye.iris.before_validation_callbacks_counter assert_equal 1, eye.iris.before_create_callbacks_counter assert_equal 1, eye.iris.before_save_callbacks_counter @@ -314,8 +314,18 @@ def test_callbacks_on_child_when_parent_autosaves_child assert_equal 1, eye.iris.after_save_callbacks_counter end + def test_callbacks_on_child_when_parent_autosaves_polymorphic_child_with_inverse_of + drink_designer = DrinkDesigner.create!(chef: ChefWithPolymorphicInverseOf.new) + assert_equal 1, drink_designer.chef.before_validation_callbacks_counter + assert_equal 1, drink_designer.chef.before_create_callbacks_counter + assert_equal 1, drink_designer.chef.before_save_callbacks_counter + assert_equal 1, drink_designer.chef.after_validation_callbacks_counter + assert_equal 1, drink_designer.chef.after_create_callbacks_counter + assert_equal 1, drink_designer.chef.after_save_callbacks_counter + end + def test_callbacks_on_child_when_child_autosaves_parent - iris = Iris.create(eye: Eye.new) + iris = Iris.create!(eye: Eye.new) assert_equal 1, iris.before_validation_callbacks_counter assert_equal 1, iris.before_create_callbacks_counter assert_equal 1, iris.before_save_callbacks_counter @@ -324,6 +334,16 @@ def test_callbacks_on_child_when_child_autosaves_parent assert_equal 1, iris.after_save_callbacks_counter end + def test_callbacks_on_child_when_polymorphic_child_with_inverse_of_autosaves_parent + chef = ChefWithPolymorphicInverseOf.create!(employable: DrinkDesigner.new) + assert_equal 1, chef.before_validation_callbacks_counter + assert_equal 1, chef.before_create_callbacks_counter + assert_equal 1, chef.before_save_callbacks_counter + assert_equal 1, chef.after_validation_callbacks_counter + assert_equal 1, chef.after_create_callbacks_counter + assert_equal 1, chef.after_save_callbacks_counter + end + def test_foreign_key_attribute_is_not_set_unless_changed eye = Eye.create!(iris_with_read_only_foreign_key_attributes: { color: "honey" }) assert_nothing_raised do diff --git a/activerecord/test/models/chef.rb b/activerecord/test/models/chef.rb index ff528644bc..f894cdbeab 100644 --- a/activerecord/test/models/chef.rb +++ b/activerecord/test/models/chef.rb @@ -8,3 +8,55 @@ class Chef < ActiveRecord::Base class ChefList < Chef belongs_to :employable_list, polymorphic: true end + +class ChefWithPolymorphicInverseOf < Chef + attr_reader :before_validation_callbacks_counter + attr_reader :before_create_callbacks_counter + attr_reader :before_save_callbacks_counter + + attr_reader :after_validation_callbacks_counter + attr_reader :after_create_callbacks_counter + attr_reader :after_save_callbacks_counter + + belongs_to :employable, polymorphic: true, inverse_of: :chef + accepts_nested_attributes_for :employable + + before_validation :update_before_validation_counter + before_create :update_before_create_counter + before_save :update_before_save_counter + + after_validation :update_after_validation_counter + after_create :update_after_create_counter + after_save :update_after_save_counter + + private + def update_before_validation_counter + @before_validation_callbacks_counter ||= 0 + @before_validation_callbacks_counter += 1 + end + + def update_before_create_counter + @before_create_callbacks_counter ||= 0 + @before_create_callbacks_counter += 1 + end + + def update_before_save_counter + @before_save_callbacks_counter ||= 0 + @before_save_callbacks_counter += 1 + end + + def update_after_validation_counter + @after_validation_callbacks_counter ||= 0 + @after_validation_callbacks_counter += 1 + end + + def update_after_create_counter + @after_create_callbacks_counter ||= 0 + @after_create_callbacks_counter += 1 + end + + def update_after_save_counter + @after_save_callbacks_counter ||= 0 + @after_save_callbacks_counter += 1 + end +end diff --git a/activerecord/test/models/drink_designer.rb b/activerecord/test/models/drink_designer.rb index fe7f5c9e03..8f41106f78 100644 --- a/activerecord/test/models/drink_designer.rb +++ b/activerecord/test/models/drink_designer.rb @@ -2,6 +2,7 @@ class DrinkDesigner < ActiveRecord::Base has_one :chef, as: :employable + accepts_nested_attributes_for :chef end class DrinkDesignerWithPolymorphicDependentNullifyChef < ActiveRecord::Base