From f87db851c680164b6474d783d5f29b6cb4c013c0 Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Fri, 16 Mar 2007 22:39:01 +0000 Subject: Remove deprecated object transactions. People relying on this functionality should install the object_transactions plugin at http://code.bitsweat.net/svn/object_transactions. Closes #5637 [Koz, Jeremy Kemper] git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@6439 5ecf4fe2-1ee6-0310-87b1-e25e094e27de --- activerecord/CHANGELOG | 2 + activerecord/lib/active_record/transactions.rb | 43 +- activerecord/lib/active_record/vendor/simple.rb | 693 ------------------------ activerecord/test/transactions_test.rb | 20 - 4 files changed, 6 insertions(+), 752 deletions(-) delete mode 100644 activerecord/lib/active_record/vendor/simple.rb diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index cbd928ac65..dd1c1fa0fe 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,7 @@ *SVN* +* Remove deprecated object transactions. People relying on this functionality should install the object_transactions plugin at http://code.bitsweat.net/svn/object_transactions. Closes #5637 [Koz, Jeremy Kemper] + * PostgreSQL: remove DateTime -> Time downcast. Warning: do not enable translate_results for the C bindings if you have timestamps outside Time's domain. [Jeremy Kemper] * find_or_create_by_* takes a hash so you can create with more attributes than are in the method name. For example, Person.find_or_create_by_name(:name => 'Henry', :comments => 'Hi new user!') is equivalent to Person.find_by_name('Henry') || Person.create(:name => 'Henry', :comments => 'Hi new user!'). #7368 [Josh Susser] diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb index d9bfd331c6..a75ccc0203 100644 --- a/activerecord/lib/active_record/transactions.rb +++ b/activerecord/lib/active_record/transactions.rb @@ -1,5 +1,3 @@ -require 'active_record/vendor/simple.rb' -Transaction::Simple.send(:remove_method, :transaction) require 'thread' module ActiveRecord @@ -66,50 +64,17 @@ module ActiveRecord # will happen under the protected cover of a transaction. So you can use validations to check for values that the transaction # depend on or you can raise exceptions in the callbacks to rollback. # - # == Object-level transactions (deprecated) - # - # You can enable object-level transactions for Active Record objects, though. You do this by naming each of the Active Records - # that you want to enable object-level transactions for, like this: - # - # Account.transaction(david, mary) do - # david.withdrawal(100) - # mary.deposit(100) - # end - # - # If the transaction fails, David and Mary will be returned to their - # pre-transactional state. No money will have changed hands in neither - # object nor database. - # - # However, useful state such as validation errors are also rolled back, - # limiting the usefulness of this feature. As such it is deprecated in - # Rails 1.2 and will be removed in the next release. Install the - # object_transactions plugin if you wish to continue using it. - # # == Exception handling # # Also have in mind that exceptions thrown within a transaction block will be propagated (after triggering the ROLLBACK), so you # should be ready to catch those in your application code. - # - # Tribute: Object-level transactions are implemented by Transaction::Simple by Austin Ziegler. module ClassMethods - def transaction(*objects, &block) + def transaction(&block) previous_handler = trap('TERM') { raise TransactionError, "Transaction aborted" } increment_open_transactions begin - unless objects.empty? - ActiveSupport::Deprecation.warn "Object transactions are deprecated and will be removed from Rails 2.0. See http://www.rubyonrails.org/deprecation for details.", caller - objects.each { |o| o.extend(Transaction::Simple) } - objects.each { |o| o.start_transaction } - end - - result = connection.transaction(Thread.current['start_db_transaction'], &block) - - objects.each { |o| o.commit_transaction } - return result - rescue Exception => object_transaction_rollback - objects.each { |o| o.abort_transaction } - raise + connection.transaction(Thread.current['start_db_transaction'], &block) ensure decrement_open_transactions trap('TERM', previous_handler) @@ -128,8 +93,8 @@ module ActiveRecord end end - def transaction(*objects, &block) - self.class.transaction(*objects, &block) + def transaction(&block) + self.class.transaction(&block) end def destroy_with_transactions #:nodoc: diff --git a/activerecord/lib/active_record/vendor/simple.rb b/activerecord/lib/active_record/vendor/simple.rb deleted file mode 100644 index 7ac3cd08ec..0000000000 --- a/activerecord/lib/active_record/vendor/simple.rb +++ /dev/null @@ -1,693 +0,0 @@ -# :title: Transaction::Simple -- Active Object Transaction Support for Ruby -# :main: Transaction::Simple -# -# == Licence -# -# Permission is hereby granted, free of charge, to any person obtaining a -# copy of this software and associated documentation files (the "Software"), -# to deal in the Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, -# and/or sell copies of the Software, and to permit persons to whom the -# Software is furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -# DEALINGS IN THE SOFTWARE. -#-- -# Transaction::Simple -# Simple object transaction support for Ruby -# Version 1.3.0 -# -# Copyright (c) 2003 - 2005 Austin Ziegler -# -# $Id: simple.rb,v 1.5 2005/05/05 16:16:49 austin Exp $ -#++ - # The "Transaction" namespace can be used for additional transaction - # support objects and modules. -module Transaction - # A standard exception for transaction errors. - class TransactionError < StandardError; end - # The TransactionAborted exception is used to indicate when a - # transaction has been aborted in the block form. - class TransactionAborted < Exception; end - # The TransactionCommitted exception is used to indicate when a - # transaction has been committed in the block form. - class TransactionCommitted < Exception; end - - te = "Transaction Error: %s" - - Messages = { - :bad_debug_object => - te % "the transaction debug object must respond to #<<.", - :unique_names => - te % "named transactions must be unique.", - :no_transaction_open => - te % "no transaction open.", - :cannot_rewind_no_transaction => - te % "cannot rewind; there is no current transaction.", - :cannot_rewind_named_transaction => - te % "cannot rewind to transaction %s because it does not exist.", - :cannot_rewind_transaction_before_block => - te % "cannot rewind a transaction started before the execution block.", - :cannot_abort_no_transaction => - te % "cannot abort; there is no current transaction.", - :cannot_abort_transaction_before_block => - te % "cannot abort a transaction started before the execution block.", - :cannot_abort_named_transaction => - te % "cannot abort nonexistant transaction %s.", - :cannot_commit_no_transaction => - te % "cannot commit; there is no current transaction.", - :cannot_commit_transaction_before_block => - te % "cannot commit a transaction started before the execution block.", - :cannot_commit_named_transaction => - te % "cannot commit nonexistant transaction %s.", - :cannot_start_empty_block_transaction => - te % "cannot start a block transaction with no objects.", - :cannot_obtain_transaction_lock => - te % "cannot obtain transaction lock for #%s.", - } - - # = Transaction::Simple for Ruby - # Simple object transaction support for Ruby - # - # == Introduction - # Transaction::Simple provides a generic way to add active transaction - # support to objects. The transaction methods added by this module will - # work with most objects, excluding those that cannot be - # Marshaled (bindings, procedure objects, IO instances, or - # singleton objects). - # - # The transactions supported by Transaction::Simple are not backed - # transactions; they are not associated with any sort of data store. - # They are "live" transactions occurring in memory and in the object - # itself. This is to allow "test" changes to be made to an object - # before making the changes permanent. - # - # Transaction::Simple can handle an "infinite" number of transaction - # levels (limited only by memory). If I open two transactions, commit - # the second, but abort the first, the object will revert to the - # original version. - # - # Transaction::Simple supports "named" transactions, so that multiple - # levels of transactions can be committed, aborted, or rewound by - # referring to the appropriate name of the transaction. Names may be any - # object *except* +nil+. As with Hash keys, String names will be - # duplicated and frozen before using. - # - # Copyright:: Copyright © 2003 - 2005 by Austin Ziegler - # Version:: 1.3.0 - # Licence:: MIT-Style - # - # Thanks to David Black for help with the initial concept that led to - # this library. - # - # == Usage - # include 'transaction/simple' - # - # v = "Hello, you." # -> "Hello, you." - # v.extend(Transaction::Simple) # -> "Hello, you." - # - # v.start_transaction # -> ... (a Marshal string) - # v.transaction_open? # -> true - # v.gsub!(/you/, "world") # -> "Hello, world." - # - # v.rewind_transaction # -> "Hello, you." - # v.transaction_open? # -> true - # - # v.gsub!(/you/, "HAL") # -> "Hello, HAL." - # v.abort_transaction # -> "Hello, you." - # v.transaction_open? # -> false - # - # v.start_transaction # -> ... (a Marshal string) - # v.start_transaction # -> ... (a Marshal string) - # - # v.transaction_open? # -> true - # v.gsub!(/you/, "HAL") # -> "Hello, HAL." - # - # v.commit_transaction # -> "Hello, HAL." - # v.transaction_open? # -> true - # v.abort_transaction # -> "Hello, you." - # v.transaction_open? # -> false - # - # == Named Transaction Usage - # v = "Hello, you." # -> "Hello, you." - # v.extend(Transaction::Simple) # -> "Hello, you." - # - # v.start_transaction(:first) # -> ... (a Marshal string) - # v.transaction_open? # -> true - # v.transaction_open?(:first) # -> true - # v.transaction_open?(:second) # -> false - # v.gsub!(/you/, "world") # -> "Hello, world." - # - # v.start_transaction(:second) # -> ... (a Marshal string) - # v.gsub!(/world/, "HAL") # -> "Hello, HAL." - # v.rewind_transaction(:first) # -> "Hello, you." - # v.transaction_open? # -> true - # v.transaction_open?(:first) # -> true - # v.transaction_open?(:second) # -> false - # - # v.gsub!(/you/, "world") # -> "Hello, world." - # v.start_transaction(:second) # -> ... (a Marshal string) - # v.gsub!(/world/, "HAL") # -> "Hello, HAL." - # v.transaction_name # -> :second - # v.abort_transaction(:first) # -> "Hello, you." - # v.transaction_open? # -> false - # - # v.start_transaction(:first) # -> ... (a Marshal string) - # v.gsub!(/you/, "world") # -> "Hello, world." - # v.start_transaction(:second) # -> ... (a Marshal string) - # v.gsub!(/world/, "HAL") # -> "Hello, HAL." - # - # v.commit_transaction(:first) # -> "Hello, HAL." - # v.transaction_open? # -> false - # - # == Block Usage - # v = "Hello, you." # -> "Hello, you." - # Transaction::Simple.start(v) do |tv| - # # v has been extended with Transaction::Simple and an unnamed - # # transaction has been started. - # tv.transaction_open? # -> true - # tv.gsub!(/you/, "world") # -> "Hello, world." - # - # tv.rewind_transaction # -> "Hello, you." - # tv.transaction_open? # -> true - # - # tv.gsub!(/you/, "HAL") # -> "Hello, HAL." - # # The following breaks out of the transaction block after - # # aborting the transaction. - # tv.abort_transaction # -> "Hello, you." - # end - # # v still has Transaction::Simple applied from here on out. - # v.transaction_open? # -> false - # - # Transaction::Simple.start(v) do |tv| - # tv.start_transaction # -> ... (a Marshal string) - # - # tv.transaction_open? # -> true - # tv.gsub!(/you/, "HAL") # -> "Hello, HAL." - # - # # If #commit_transaction were called without having started a - # # second transaction, then it would break out of the transaction - # # block after committing the transaction. - # tv.commit_transaction # -> "Hello, HAL." - # tv.transaction_open? # -> true - # tv.abort_transaction # -> "Hello, you." - # end - # v.transaction_open? # -> false - # - # == Named Transaction Usage - # v = "Hello, you." # -> "Hello, you." - # v.extend(Transaction::Simple) # -> "Hello, you." - # - # v.start_transaction(:first) # -> ... (a Marshal string) - # v.transaction_open? # -> true - # v.transaction_open?(:first) # -> true - # v.transaction_open?(:second) # -> false - # v.gsub!(/you/, "world") # -> "Hello, world." - # - # v.start_transaction(:second) # -> ... (a Marshal string) - # v.gsub!(/world/, "HAL") # -> "Hello, HAL." - # v.rewind_transaction(:first) # -> "Hello, you." - # v.transaction_open? # -> true - # v.transaction_open?(:first) # -> true - # v.transaction_open?(:second) # -> false - # - # v.gsub!(/you/, "world") # -> "Hello, world." - # v.start_transaction(:second) # -> ... (a Marshal string) - # v.gsub!(/world/, "HAL") # -> "Hello, HAL." - # v.transaction_name # -> :second - # v.abort_transaction(:first) # -> "Hello, you." - # v.transaction_open? # -> false - # - # v.start_transaction(:first) # -> ... (a Marshal string) - # v.gsub!(/you/, "world") # -> "Hello, world." - # v.start_transaction(:second) # -> ... (a Marshal string) - # v.gsub!(/world/, "HAL") # -> "Hello, HAL." - # - # v.commit_transaction(:first) # -> "Hello, HAL." - # v.transaction_open? # -> false - # - # == Thread Safety - # Threadsafe version of Transaction::Simple and - # Transaction::Simple::Group exist; these are loaded from - # 'transaction/simple/threadsafe' and - # 'transaction/simple/threadsafe/group', respectively, and are - # represented in Ruby code as Transaction::Simple::ThreadSafe and - # Transaction::Simple::ThreadSafe::Group, respectively. - # - # == Contraindications - # While Transaction::Simple is very useful, it has some severe - # limitations that must be understood. Transaction::Simple: - # - # * uses Marshal. Thus, any object which cannot be Marshaled - # cannot use Transaction::Simple. In my experience, this affects - # singleton objects more often than any other object. It may be that - # Ruby 2.0 will solve this problem. - # * does not manage resources. Resources external to the object and its - # instance variables are not managed at all. However, all instance - # variables and objects "belonging" to those instance variables are - # managed. If there are object reference counts to be handled, - # Transaction::Simple will probably cause problems. - # * is not inherently thread-safe. In the ACID ("atomic, consistent, - # isolated, durable") test, Transaction::Simple provides CD, but it is - # up to the user of Transaction::Simple to provide isolation and - # atomicity. Transactions should be considered "critical sections" in - # multi-threaded applications. If thread safety and atomicity is - # absolutely required, use Transaction::Simple::ThreadSafe, which uses - # a Mutex object to synchronize the accesses on the object during the - # transaction operations. - # * does not necessarily maintain Object#__id__ values on rewind or - # abort. This may change for future versions that will be Ruby 1.8 or - # better *only*. Certain objects that support #replace will maintain - # Object#__id__. - # * Can be a memory hog if you use many levels of transactions on many - # objects. - # - module Simple - TRANSACTION_SIMPLE_VERSION = '1.3.0' - - # Sets the Transaction::Simple debug object. It must respond to #<<. - # Sets the transaction debug object. Debugging will be performed - # automatically if there's a debug object. The generic transaction - # error class. - def self.debug_io=(io) - if io.nil? - @tdi = nil - @debugging = false - else - unless io.respond_to?(:<<) - raise TransactionError, Messages[:bad_debug_object] - end - @tdi = io - @debugging = true - end - end - - # Returns +true+ if we are debugging. - def self.debugging? - @debugging - end - - # Returns the Transaction::Simple debug object. It must respond to - # #<<. - def self.debug_io - @tdi ||= "" - @tdi - end - - # If +name+ is +nil+ (default), then returns +true+ if there is - # currently a transaction open. - # - # If +name+ is specified, then returns +true+ if there is currently a - # transaction that responds to +name+ open. - def transaction_open?(name = nil) - if name.nil? - if Transaction::Simple.debugging? - Transaction::Simple.debug_io << "Transaction " << - "[#{(@__transaction_checkpoint__.nil?) ? 'closed' : 'open'}]\n" - end - return (not @__transaction_checkpoint__.nil?) - else - if Transaction::Simple.debugging? - Transaction::Simple.debug_io << "Transaction(#{name.inspect}) " << - "[#{(@__transaction_checkpoint__.nil?) ? 'closed' : 'open'}]\n" - end - return ((not @__transaction_checkpoint__.nil?) and @__transaction_names__.include?(name)) - end - end - - # Returns the current name of the transaction. Transactions not - # explicitly named are named +nil+. - def transaction_name - if @__transaction_checkpoint__.nil? - raise TransactionError, Messages[:no_transaction_open] - end - if Transaction::Simple.debugging? - Transaction::Simple.debug_io << "#{'|' * @__transaction_level__} " << - "Transaction Name: #{@__transaction_names__[-1].inspect}\n" - end - if @__transaction_names__[-1].kind_of?(String) - @__transaction_names__[-1].dup - else - @__transaction_names__[-1] - end - end - - # Starts a transaction. Stores the current object state. If a - # transaction name is specified, the transaction will be named. - # Transaction names must be unique. Transaction names of +nil+ will be - # treated as unnamed transactions. - def start_transaction(name = nil) - @__transaction_level__ ||= 0 - @__transaction_names__ ||= [] - - if name.nil? - @__transaction_names__ << nil - ss = "" if Transaction::Simple.debugging? - else - if @__transaction_names__.include?(name) - raise TransactionError, Messages[:unique_names] - end - name = name.dup.freeze if name.kind_of?(String) - @__transaction_names__ << name - ss = "(#{name.inspect})" if Transaction::Simple.debugging? - end - - @__transaction_level__ += 1 - - if Transaction::Simple.debugging? - Transaction::Simple.debug_io << "#{'>' * @__transaction_level__} " << - "Start Transaction#{ss}\n" - end - - @__transaction_checkpoint__ = Marshal.dump(self) - end - - # Rewinds the transaction. If +name+ is specified, then the - # intervening transactions will be aborted and the named transaction - # will be rewound. Otherwise, only the current transaction is rewound. - def rewind_transaction(name = nil) - if @__transaction_checkpoint__.nil? - raise TransactionError, Messages[:cannot_rewind_no_transaction] - end - - # Check to see if we are trying to rewind a transaction that is - # outside of the current transaction block. - if @__transaction_block__ and name - nix = @__transaction_names__.index(name) + 1 - if nix < @__transaction_block__ - raise TransactionError, Messages[:cannot_rewind_transaction_before_block] - end - end - - if name.nil? - __rewind_this_transaction - ss = "" if Transaction::Simple.debugging? - else - unless @__transaction_names__.include?(name) - raise TransactionError, Messages[:cannot_rewind_named_transaction] % name.inspect - end - ss = "(#{name})" if Transaction::Simple.debugging? - - while @__transaction_names__[-1] != name - @__transaction_checkpoint__ = __rewind_this_transaction - if Transaction::Simple.debugging? - Transaction::Simple.debug_io << "#{'|' * @__transaction_level__} " << - "Rewind Transaction#{ss}\n" - end - @__transaction_level__ -= 1 - @__transaction_names__.pop - end - __rewind_this_transaction - end - if Transaction::Simple.debugging? - Transaction::Simple.debug_io << "#{'|' * @__transaction_level__} " << - "Rewind Transaction#{ss}\n" - end - self - end - - # Aborts the transaction. Resets the object state to what it was - # before the transaction was started and closes the transaction. If - # +name+ is specified, then the intervening transactions and the named - # transaction will be aborted. Otherwise, only the current transaction - # is aborted. - # - # If the current or named transaction has been started by a block - # (Transaction::Simple.start), then the execution of the block will be - # halted with +break+ +self+. - def abort_transaction(name = nil) - if @__transaction_checkpoint__.nil? - raise TransactionError, Messages[:cannot_abort_no_transaction] - end - - # Check to see if we are trying to abort a transaction that is - # outside of the current transaction block. Otherwise, raise - # TransactionAborted if they are the same. - if @__transaction_block__ and name - nix = @__transaction_names__.index(name) + 1 - if nix < @__transaction_block__ - raise TransactionError, Messages[:cannot_abort_transaction_before_block] - end - - raise TransactionAborted if @__transaction_block__ == nix - end - - raise TransactionAborted if @__transaction_block__ == @__transaction_level__ - - if name.nil? - __abort_transaction(name) - else - unless @__transaction_names__.include?(name) - raise TransactionError, Messages[:cannot_abort_named_transaction] % name.inspect - end - __abort_transaction(name) while @__transaction_names__.include?(name) - end - self - end - - # If +name+ is +nil+ (default), the current transaction level is - # closed out and the changes are committed. - # - # If +name+ is specified and +name+ is in the list of named - # transactions, then all transactions are closed and committed until - # the named transaction is reached. - def commit_transaction(name = nil) - if @__transaction_checkpoint__.nil? - raise TransactionError, Messages[:cannot_commit_no_transaction] - end - @__transaction_block__ ||= nil - - # Check to see if we are trying to commit a transaction that is - # outside of the current transaction block. Otherwise, raise - # TransactionCommitted if they are the same. - if @__transaction_block__ and name - nix = @__transaction_names__.index(name) + 1 - if nix < @__transaction_block__ - raise TransactionError, Messages[:cannot_commit_transaction_before_block] - end - - raise TransactionCommitted if @__transaction_block__ == nix - end - - raise TransactionCommitted if @__transaction_block__ == @__transaction_level__ - - if name.nil? - ss = "" if Transaction::Simple.debugging? - __commit_transaction - if Transaction::Simple.debugging? - Transaction::Simple.debug_io << "#{'<' * @__transaction_level__} " << - "Commit Transaction#{ss}\n" - end - else - unless @__transaction_names__.include?(name) - raise TransactionError, Messages[:cannot_commit_named_transaction] % name.inspect - end - ss = "(#{name})" if Transaction::Simple.debugging? - - while @__transaction_names__[-1] != name - if Transaction::Simple.debugging? - Transaction::Simple.debug_io << "#{'<' * @__transaction_level__} " << - "Commit Transaction#{ss}\n" - end - __commit_transaction - end - if Transaction::Simple.debugging? - Transaction::Simple.debug_io << "#{'<' * @__transaction_level__} " << - "Commit Transaction#{ss}\n" - end - __commit_transaction - end - - self - end - - # Alternative method for calling the transaction methods. An optional - # name can be specified for named transaction support. - # - # #transaction(:start):: #start_transaction - # #transaction(:rewind):: #rewind_transaction - # #transaction(:abort):: #abort_transaction - # #transaction(:commit):: #commit_transaction - # #transaction(:name):: #transaction_name - # #transaction:: #transaction_open? - def transaction(action = nil, name = nil) - case action - when :start - start_transaction(name) - when :rewind - rewind_transaction(name) - when :abort - abort_transaction(name) - when :commit - commit_transaction(name) - when :name - transaction_name - when nil - transaction_open?(name) - end - end - - # Allows specific variables to be excluded from transaction support. - # Must be done after extending the object but before starting the - # first transaction on the object. - # - # vv.transaction_exclusions << "@io" - def transaction_exclusions - @transaction_exclusions ||= [] - end - - class << self - def __common_start(name, vars, &block) - if vars.empty? - raise TransactionError, Messages[:cannot_start_empty_block_transaction] - end - - if block - begin - vlevel = {} - - vars.each do |vv| - vv.extend(Transaction::Simple) - vv.start_transaction(name) - vlevel[vv.__id__] = vv.instance_variable_get(:@__transaction_level__) - vv.instance_variable_set(:@__transaction_block__, vlevel[vv.__id__]) - end - - yield(*vars) - rescue TransactionAborted - vars.each do |vv| - if name.nil? and vv.transaction_open? - loop do - tlevel = vv.instance_variable_get(:@__transaction_level__) || -1 - vv.instance_variable_set(:@__transaction_block__, -1) - break if tlevel < vlevel[vv.__id__] - vv.abort_transaction if vv.transaction_open? - end - elsif vv.transaction_open?(name) - vv.instance_variable_set(:@__transaction_block__, -1) - vv.abort_transaction(name) - end - end - rescue TransactionCommitted - nil - ensure - vars.each do |vv| - if name.nil? and vv.transaction_open? - loop do - tlevel = vv.instance_variable_get(:@__transaction_level__) || -1 - break if tlevel < vlevel[vv.__id__] - vv.instance_variable_set(:@__transaction_block__, -1) - vv.commit_transaction if vv.transaction_open? - end - elsif vv.transaction_open?(name) - vv.instance_variable_set(:@__transaction_block__, -1) - vv.commit_transaction(name) - end - end - end - else - vars.each do |vv| - vv.extend(Transaction::Simple) - vv.start_transaction(name) - end - end - end - private :__common_start - - def start_named(name, *vars, &block) - __common_start(name, vars, &block) - end - - def start(*vars, &block) - __common_start(nil, vars, &block) - end - end - - def __abort_transaction(name = nil) #:nodoc: - @__transaction_checkpoint__ = __rewind_this_transaction - - if name.nil? - ss = "" if Transaction::Simple.debugging? - else - ss = "(#{name.inspect})" if Transaction::Simple.debugging? - end - - if Transaction::Simple.debugging? - Transaction::Simple.debug_io << "#{'<' * @__transaction_level__} " << - "Abort Transaction#{ss}\n" - end - @__transaction_level__ -= 1 - @__transaction_names__.pop - if @__transaction_level__ < 1 - @__transaction_level__ = 0 - @__transaction_names__ = [] - end - end - - TRANSACTION_CHECKPOINT = "@__transaction_checkpoint__" #:nodoc: - SKIP_TRANSACTION_VARS = [TRANSACTION_CHECKPOINT, "@__transaction_level__"] #:nodoc: - - def __rewind_this_transaction #:nodoc: - rr = Marshal.restore(@__transaction_checkpoint__) - - begin - self.replace(rr) if respond_to?(:replace) - rescue - nil - end - - rr.instance_variables.each do |vv| - next if SKIP_TRANSACTION_VARS.include?(vv) - next if self.transaction_exclusions.include?(vv) - if respond_to?(:instance_variable_get) - instance_variable_set(vv, rr.instance_variable_get(vv)) - else - instance_eval(%q|#{vv} = rr.instance_eval("#{vv}")|) - end - end - - new_ivar = instance_variables - rr.instance_variables - SKIP_TRANSACTION_VARS - new_ivar.each do |vv| - if respond_to?(:instance_variable_set) - instance_variable_set(vv, nil) - else - instance_eval(%q|#{vv} = nil|) - end - end - - if respond_to?(:instance_variable_get) - rr.instance_variable_get(TRANSACTION_CHECKPOINT) - else - rr.instance_eval(TRANSACTION_CHECKPOINT) - end - end - - def __commit_transaction #:nodoc: - if respond_to?(:instance_variable_get) - @__transaction_checkpoint__ = Marshal.restore(@__transaction_checkpoint__).instance_variable_get(TRANSACTION_CHECKPOINT) - else - @__transaction_checkpoint__ = Marshal.restore(@__transaction_checkpoint__).instance_eval(TRANSACTION_CHECKPOINT) - end - - @__transaction_level__ -= 1 - @__transaction_names__.pop - - if @__transaction_level__ < 1 - @__transaction_level__ = 0 - @__transaction_names__ = [] - end - end - - private :__abort_transaction - private :__rewind_this_transaction - private :__commit_transaction - end -end diff --git a/activerecord/test/transactions_test.rb b/activerecord/test/transactions_test.rb index f20c763268..098e01c945 100644 --- a/activerecord/test/transactions_test.rb +++ b/activerecord/test/transactions_test.rb @@ -86,26 +86,6 @@ class TransactionTest < Test::Unit::TestCase assert Topic.find(2).approved?, "Second should still be approved" end - def test_failing_with_object_rollback - assert !@first.approved?, "First should be unapproved initially" - - begin - assert_deprecated /Object transactions/ do - Topic.transaction(@first, @second) do - @first.approved = true - @second.approved = false - @first.save - @second.save - raise "Bad things!" - end - end - rescue - # caught it - end - - assert !@first.approved?, "First shouldn't have been approved" - assert @second.approved?, "Second should still be approved" - end def test_callback_rollback_in_save add_exception_raising_after_save_callback_to_topic -- cgit v1.2.3