aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
authorJon Leighton <j@jonathanleighton.com>2011-01-09 16:06:14 +0000
committerAaron Patterson <aaron.patterson@gmail.com>2011-01-11 13:45:07 -0800
commit1d6e2184283d15d20ed3102ca462d905e5efa73d (patch)
treeafcdf32df5f755de5e47616319a46d96b867f4c9 /activerecord
parent4e19ec566c9132b85fdf0ff13a328238a6aca591 (diff)
downloadrails-1d6e2184283d15d20ed3102ca462d905e5efa73d.tar.gz
rails-1d6e2184283d15d20ed3102ca462d905e5efa73d.tar.bz2
rails-1d6e2184283d15d20ed3102ca462d905e5efa73d.zip
When assigning a has_one, if anything fails, the assignment should be rolled back entirely
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/lib/active_record/associations/has_one_association.rb42
-rw-r--r--activerecord/test/cases/associations/has_one_associations_test.rb7
2 files changed, 28 insertions, 21 deletions
diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb
index 693c80163a..f60547c22c 100644
--- a/activerecord/lib/active_record/associations/has_one_association.rb
+++ b/activerecord/lib/active_record/associations/has_one_association.rb
@@ -19,23 +19,25 @@ module ActiveRecord
raise_on_type_mismatch(record) unless record.nil?
load_target
- if @target && @target != record
- remove_target!(@reflection.options[:dependent])
- end
+ @reflection.klass.transaction do
+ if @target && @target != record
+ remove_target!(@reflection.options[:dependent])
+ end
+
+ if record
+ set_inverse_instance(record)
+ set_owner_attributes(record)
- if record
- set_owner_attributes(record)
- set_inverse_instance(record)
+ if @owner.persisted? && save && !record.save
+ nullify_owner_attributes(record)
+ set_owner_attributes(@target)
+ raise RecordNotSaved, "Failed to save the new associated #{@reflection.name}."
+ end
+ end
end
@target = record
loaded
-
- if @owner.persisted? && record && save
- unless record.save
- raise RecordNotSaved, "Failed to save the new associated #{@reflection.name}."
- end
- end
end
private
@@ -59,17 +61,19 @@ module ActiveRecord
if [:delete, :destroy].include?(method)
@target.send(method)
else
- @target[@reflection.foreign_key] = nil
+ nullify_owner_attributes(@target)
- if @target.persisted? && @owner.persisted?
- unless @target.save
- @target[@reflection.foreign_key] = @target.send("#{@reflection.foreign_key}_was")
- raise RecordNotSaved, "Failed to remove the existing associated #{@reflection.name}. " +
- "The record failed to save when after its foreign key was set to nil."
- end
+ if @target.persisted? && @owner.persisted? && !@target.save
+ set_owner_attributes(@target)
+ raise RecordNotSaved, "Failed to remove the existing associated #{@reflection.name}. " +
+ "The record failed to save when after its foreign key was set to nil."
end
end
end
+
+ def nullify_owner_attributes(record)
+ record[@reflection.foreign_key] = nil
+ end
end
end
end
diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb
index b9719fa983..925b76b901 100644
--- a/activerecord/test/cases/associations/has_one_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_associations_test.rb
@@ -6,6 +6,7 @@ require 'models/ship'
require 'models/pirate'
class HasOneAssociationsTest < ActiveRecord::TestCase
+ self.use_transactional_fixtures = false unless supports_savepoints?
fixtures :accounts, :companies, :developers, :projects, :developers_projects, :ships, :pirates
def setup
@@ -317,7 +318,9 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
assert_raise(ActiveRecord::RecordNotSaved) do
pirate.ship = new_ship
end
- assert_equal new_ship, pirate.ship
- assert_equal pirate.id, new_ship.pirate_id
+ assert_equal ships(:black_pearl), pirate.ship
+ assert_equal pirate.id, pirate.ship.pirate_id
+ assert_equal pirate.id, ships(:black_pearl).reload.pirate_id
+ assert_nil new_ship.pirate_id
end
end