Fix: Inconsistent Polymorphic Association Autosave

This commit is contained in:
Joshua Young 2022-06-08 19:20:28 +10:00
parent e6d4ac0e50
commit 589321d99a
5 changed files with 58 additions and 1 deletions

@ -463,7 +463,8 @@ def save_has_one_association(reflection)
# If the record is new or it has changed, returns true.
def _record_changed?(reflection, record, key)
record.new_record? ||
association_foreign_key_changed?(reflection, record, key) ||
(association_foreign_key_changed?(reflection, record, key) ||
inverse_polymorphic_association_changed?(reflection, record)) ||
record.will_save_change_to_attribute?(reflection.foreign_key)
end
@ -473,6 +474,14 @@ def association_foreign_key_changed?(reflection, record, key)
record._has_attribute?(reflection.foreign_key) && record._read_attribute(reflection.foreign_key) != key
end
def inverse_polymorphic_association_changed?(reflection, record)
return false unless reflection.inverse_of&.polymorphic?
class_name = record._read_attribute(reflection.inverse_of.foreign_type)
reflection.active_record != record.class.polymorphic_class_for(class_name)
end
# Saves the associated record if it's new or <tt>:autosave</tt> is enabled.
#
# In addition, it will destroy the association if it was marked for destruction.

@ -36,6 +36,9 @@
require "models/reply"
require "models/attachment"
require "models/translation"
require "models/chef"
require "models/cake_designer"
require "models/drink_designer"
class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase
def test_autosave_works_even_when_other_callbacks_update_the_parent_model
@ -1276,6 +1279,8 @@ def destroy(*args)
end
class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
fixtures :chefs, :cake_designers, :drink_designers
self.use_transactional_tests = false unless supports_savepoints?
def setup
@ -1416,6 +1421,34 @@ def test_mark_for_destruction_is_ignored_without_autosave_true
assert_not_predicate ship, :valid?
end
def test_recognises_inverse_polymorphic_association_changes_with_same_foreign_key
chef_a = chefs(:gordon_ramsay)
chef_b = chefs(:marco_pierre_white)
cake_designer_a = cake_designers(:flora) # id: 1
cake_designer_a.update!(chef: chef_a)
cake_designer_b = cake_designers(:frosty) # id: 3
cake_designer_b.update!(chef: chef_b)
drink_designer_a = drink_designers(:turner) # id: 1
drink_designer_b = drink_designers(:sparrow) # id: 2
swap_chefs(cake_designer_b, drink_designer_b)
assert_predicate cake_designer_b.reload.chef, :present?
assert_not_predicate drink_designer_b.reload.chef, :present?
swap_chefs(cake_designer_a, drink_designer_a)
assert_predicate cake_designer_a.reload.chef, :present?
assert_not_predicate drink_designer_a.reload.chef, :present?
end
private
def swap_chefs(cake_designer, drink_designer)
drink_designer.chef = cake_designer.chef
drink_designer.save!
cake_designer.save!
end
end
class TestAutosaveAssociationOnAHasOneThroughAssociation < ActiveRecord::TestCase

@ -0,0 +1,5 @@
flora:
id: 1
frosty:
id: 2

5
activerecord/test/fixtures/chefs.yml vendored Normal file

@ -0,0 +1,5 @@
gordon_ramsay:
id: 1
marco_pierre_white:
id: 2

@ -0,0 +1,5 @@
turner:
id: 1
sparrow:
id: 3