From ebd7cc6f459e43aa03a6b8095266888909e0ee4d Mon Sep 17 00:00:00 2001 From: Chris Thompson Date: Thu, 7 Mar 2013 14:43:00 -0700 Subject: [PATCH] Fix #8856 Ensure has_one association=(associate) triggers save. activerecord/lib/active_record/associations.rb states: # [association=(associate)] # Assigns the associate object, extracts the primary key, sets it as the foreign key, # and saves the associate object. Since commit 42dd5d9f2976677a4bf22347f2dde1a8135dfbb4 to fix #7191, this is no longer the case if the associate has changed, but is the same object. For example: # Pirate has_one :ship pirate = Pirate.create!(catchphrase: "A Pirate") ship = pirate.build_ship(name: 'old name') ship.save! ship.name = 'new name' pirate.ship = ship That last line should trigger a save. Although we are not changing the association, the associate (ship) has changed. --- activerecord/CHANGELOG.md | 6 ++++++ .../associations/has_one_association.rb | 5 ++--- .../associations/has_one_associations_test.rb | 16 ++++++++++++++++ 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 80e4c54897..37b5224aec 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,9 @@ +* Trigger a save on `has_one association=(associate)` when the associate contents have changed. + + Fix #8856. + + *Chris Thompson* + * Abort a rake task when missing db/structure.sql like `db:schema:load` task. *kennyj* diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb index 98bd010f70..920038a543 100644 --- a/activerecord/lib/active_record/associations/has_one_association.rb +++ b/activerecord/lib/active_record/associations/has_one_association.rb @@ -25,9 +25,8 @@ module ActiveRecord raise_on_type_mismatch!(record) if record load_target - # If target and record are nil, or target is equal to record, - # we don't need to have transaction. - if (target || record) && target != record + return self.target if !(target || record) + if (target != record) || record.changed? transaction_if(save) do remove_target!(options[:dependent]) if target && !target.destroyed? diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb index 4ed09a3bf7..0e48fbca9c 100644 --- a/activerecord/test/cases/associations/has_one_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_associations_test.rb @@ -522,4 +522,20 @@ class HasOneAssociationsTest < ActiveRecord::TestCase account = Account.find(2) assert_queries { company.account = account } end + + def test_has_one_assignment_triggers_save_on_change + pirate = Pirate.create!(catchphrase: "Don' botharrr talkin' like one, savvy?") + ship = pirate.build_ship(name: 'old name') + ship.save! + + ship.name = 'new name' + assert ship.changed? + assert_queries(2) do + # One query for updating name and second query for updating pirate_id + pirate.ship = ship + end + + assert_equal 'new name', pirate.ship.reload.name + end + end