From 7e06494e32f944f8c99d7d21e17224509332ee6b Mon Sep 17 00:00:00 2001 From: Curtis Hawthorne Date: Sun, 21 Feb 2010 22:47:30 -0500 Subject: Destroy respects optimistic locking. Now works with :dependent => :destroy and includes unit tests for that case. Also includes better error messages when updating/deleting stale objects. [#1966 state:committed] Signed-off-by: Jeremy Kemper --- .../lib/active_record/locking/optimistic.rb | 35 +++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/locking/optimistic.rb') diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb index 9044ca418b..60ad23f38c 100644 --- a/activerecord/lib/active_record/locking/optimistic.rb +++ b/activerecord/lib/active_record/locking/optimistic.rb @@ -23,6 +23,16 @@ module ActiveRecord # p2.first_name = "should fail" # p2.save # Raises a ActiveRecord::StaleObjectError # + # Optimistic locking will also check for stale data when objects are destroyed. Example: + # + # p1 = Person.find(1) + # p2 = Person.find(1) + # + # p1.first_name = "Michael" + # p1.save + # + # p2.destroy # Raises a ActiveRecord::StaleObjectError + # # You're then responsible for dealing with the conflict by rescuing the exception and either rolling back, merging, # or otherwise apply the business logic needed to resolve the conflict. # @@ -39,6 +49,7 @@ module ActiveRecord self.lock_optimistically = true alias_method_chain :update, :lock + alias_method_chain :destroy, :lock alias_method_chain :attributes_from_column_definition, :lock class << self @@ -88,7 +99,7 @@ module ActiveRecord unless affected_rows == 1 - raise ActiveRecord::StaleObjectError, "Attempted to update a stale object" + raise ActiveRecord::StaleObjectError, "Attempted to update a stale object: #{self.class.name}" end affected_rows @@ -100,6 +111,28 @@ module ActiveRecord end end + def destroy_with_lock #:nodoc: + return destroy_without_lock unless locking_enabled? + + unless new_record? + lock_col = self.class.locking_column + previous_value = send(lock_col).to_i + + affected_rows = connection.delete( + "DELETE FROM #{self.class.quoted_table_name} " + + "WHERE #{connection.quote_column_name(self.class.primary_key)} = #{quoted_id} " + + "AND #{self.class.quoted_locking_column} = #{quote_value(previous_value)}", + "#{self.class.name} Destroy" + ) + + unless affected_rows == 1 + raise ActiveRecord::StaleObjectError, "Attempted to delete a stale object: #{self.class.name}" + end + end + + freeze + end + module ClassMethods DEFAULT_LOCKING_COLUMN = 'lock_version' -- cgit v1.2.3