提交 7afbc89c 编写于 作者: O Olek Janiszewski

Add ActiveRecord::Base#with_lock

Add a `with_lock` method to ActiveRecord objects, which starts
a transaction, locks the object (pessimistically) and yields to the block.
The method takes one (optional) parameter and passes it to `lock!`.

Before:

    class Order < ActiveRecord::Base
      def cancel!
        transaction do
          lock!
          # ... cancelling logic
        end
      end
    end

After:

    class Order < ActiveRecord::Base
      def cancel!
        with_lock do
          # ... cancelling logic
        end
      end
    end
上级 423b2626
......@@ -72,6 +72,33 @@
## Rails 3.2.0 (unreleased) ##
* Added a `with_lock` method to ActiveRecord objects, which starts
a transaction, locks the object (pessimistically) and yields to the block.
The method takes one (optional) parameter and passes it to `lock!`.
Before:
class Order < ActiveRecord::Base
def cancel!
transaction do
lock!
# ... cancelling logic
end
end
end
After:
class Order < ActiveRecord::Base
def cancel!
with_lock do
# ... cancelling logic
end
end
end
*Olek Janiszewski*
* 'on' and 'ON' boolean columns values are type casted to true
*Santiago Pastorino*
......@@ -82,7 +109,7 @@
Example:
rake db:migrate SCOPE=blog
*Piotr Sarnacki*
*Piotr Sarnacki*
* Migrations copied from engines are now scoped with engine's name,
for example 01_create_posts.blog.rb. *Piotr Sarnacki*
......
......@@ -38,6 +38,18 @@ module Locking
# account2.save!
# end
#
# You can start a transaction and acquire the lock in one go by calling
# <tt>with_lock</tt> with a block. The block is called from within
# a transaction, the object is already locked. Example:
#
# account = Account.first
# account.with_lock do
# # This block is called within a transaction,
# # account is already locked.
# account.balance -= 100
# account.save!
# end
#
# Database-specific information on row locking:
# MySQL: http://dev.mysql.com/doc/refman/5.1/en/innodb-locking-reads.html
# PostgreSQL: http://www.postgresql.org/docs/current/interactive/sql-select.html#SQL-FOR-UPDATE-SHARE
......@@ -50,6 +62,16 @@ def lock!(lock = true)
reload(:lock => lock) if persisted?
self
end
# Wraps the passed block in a transaction, locking the object
# before yielding. You pass can the SQL locking clause
# as argument (see <tt>lock!</tt>).
def with_lock(lock = true)
transaction do
lock!(lock)
yield
end
end
end
end
end
......@@ -388,6 +388,26 @@ def test_sane_lock_method
end
end
def test_with_lock_commits_transaction
person = Person.find 1
person.with_lock do
person.first_name = 'fooman'
person.save!
end
assert_equal 'fooman', person.reload.first_name
end
def test_with_lock_rolls_back_transaction
person = Person.find 1
old = person.first_name
person.with_lock do
person.first_name = 'fooman'
person.save!
raise 'oops'
end rescue nil
assert_equal old, person.reload.first_name
end
if current_adapter?(:PostgreSQLAdapter, :OracleAdapter)
def test_no_locks_no_wait
first, second = duel { Person.find 1 }
......
......@@ -692,6 +692,17 @@ Item.transaction do
end
</ruby>
If you already have an instance of your model, you can start a transaction and acquire the lock in one go using the following code:
<ruby>
item = Item.first
item.with_lock do
# This block is called within a transaction,
# item is already locked.
item.increment!(:views)
end
</ruby>
h3. Joining Tables
Active Record provides a finder method called +joins+ for specifying +JOIN+ clauses on the resulting SQL. There are multiple ways to use the +joins+ method.
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册