require 'transaction/simple' unless defined?(Transaction::Simple) module ActiveRecord # Implements object-level transaction support for Active Record using the # Transaction::Simple library by Austin Ziegler. # # This feature was included in Active Record up to version 1.2. It was # converted into a plugin since it's not commonly needed. # # Pass the objects you wish to participate in the transaction to the # transaction method on an Active Record class or instance. If the # transaction commits, the objects changes are retained; if the transaction # rolls back, the objects are returned to the state when the transaction # began. # # Account.transaction(david, mary) do # david.withdrawal(100) # mary.deposit(100) # end # # If the withdrawal or deposit fails, both David and Mary will be returned # to their pre-transactional state. No money will have changed hands in the # objects or in the database. # # Typically, object-level transactions are inappropriate because you need # the affected objects in their 'dirty' state in order to understand why the # transaction failed and to report errors. Particularly, rolling back an # object-level transaction removes all traces of the affected objects' # validation errors. # # Limitations: # * Object state is tracked using Marshal.dump; not all objects may be marshaled. # * Object-level transactions are not thread-safe. module ObjectTransactions def self.included(base) # Rails still includes the object transactions feature. Installing this # plugin removes the deprecation warning. if base.instance_methods.include?('object_transactions_deprecation_warning') base.send :remove_method, :object_transactions_deprecation_warning base.send :include, RemoveDeprecationWarning else super base.alias_method_chain :transaction, :object_transactions base.extend ClassMethods class << base alias_method_chain :transaction, :object_transactions end end end # Wrap the original transation instance method with one that takes # *objects args. def transaction_with_object_transactions(*objects, &block) self.class.transaction(*objects, &block) end module ClassMethods # Wrap the original transaction class method with one that manages # an object-level transaction in addition to the database transaction. # If an exception is caught, the object's state is rolled back. def transaction_with_object_transactions(*objects, &block) objects.each { |o| o.extend Transaction::Simple } objects.each &:start_transaction transaction_without_object_transactions(&block) objects.each &:commit_transaction rescue Exception => object_transaction_rollback objects.each &:abort_transaction end end module RemoveDeprecationWarning #:nodoc: def object_transactions_deprecation_warning # No warning. end end end end