Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

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

Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
  • Loading branch information...
commit 0d922885fb54c19f04680482f024452859218910 1 parent 1e6c50e
Curtis Hawthorne authored lifo committed
33 activerecord/lib/active_record/locking/optimistic.rb
View
@@ -23,6 +23,16 @@ module Locking
# 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 @@ def self.included(base) #:nodoc:
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 @@ def update_with_lock(attribute_names = @attributes.keys) #:nodoc:
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"

would be nice to have more information about the object in stale:

raise ActiveRecord::StaleObjectError, “Attempted to delete a stale object: #{self.class.name}”

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ end
+ end
+
+ freeze
+ end
+
module ClassMethods
DEFAULT_LOCKING_COLUMN = 'lock_version'
18 activerecord/test/cases/locking_test.rb
View
@@ -38,6 +38,24 @@ def test_lock_existing
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)

1 comment on commit 0d92288

alexander rakoczy

Awesome.

Georg Leciejewski

would be nice to have more information about the object in stale:

raise ActiveRecord::StaleObjectError, “Attempted to delete a stale object: #{self.class.name}”

Please sign in to comment.
Something went wrong with that request. Please try again.