Ensure Model#destroy respects optimistic locking [#1966 state:resolved]

Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
This commit is contained in:
Curtis Hawthorne
2009-03-09 14:56:09 +00:00
committed by Pratik Naik
parent 1e6c50e21b
commit 0d922885fb
2 changed files with 51 additions and 0 deletions

View File

@@ -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
base.lock_optimistically = true
base.alias_method_chain :update, :lock
base.alias_method_chain :destroy, :lock
base.alias_method_chain :attributes_from_column_definition, :lock
class << base
@@ -98,6 +109,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"
end
end
freeze
end
module ClassMethods
DEFAULT_LOCKING_COLUMN = 'lock_version'

View File

@@ -38,6 +38,24 @@ class OptimisticLockingTest < ActiveRecord::TestCase
assert_raise(ActiveRecord::StaleObjectError) { p2.save! }
end
def test_lock_destroy
p1 = Person.find(1)
p2 = Person.find(1)
assert_equal 0, p1.lock_version
assert_equal 0, p2.lock_version
p1.first_name = 'stu'
p1.save!
assert_equal 1, p1.lock_version
assert_equal 0, p2.lock_version
assert_raises(ActiveRecord::StaleObjectError) { p2.destroy }
assert p1.destroy
assert_equal true, p1.frozen?
assert_raises(ActiveRecord::RecordNotFound) { Person.find(1) }
end
def test_lock_repeating
p1 = Person.find(1)
p2 = Person.find(1)