From 4a1bb9d0ce985fd105f930078a733601b29ef8a4 Mon Sep 17 00:00:00 2001 From: Arthur Neves Date: Thu, 12 Feb 2015 23:06:44 -0500 Subject: Add transaction callbacks that wont enroll to the transaction. Add after_commit_without_transaction_enrollment and after_rollback_without_transaction_enrollment private callbacks so we can create after_commit and after_rollback callbacks without having the records automatic enrolled in the transaction. [fixes #18904] --- activerecord/lib/active_record/transactions.rb | 22 +++++++- .../test/cases/transaction_callbacks_test.rb | 59 ++++++++++++++++++++++ 2 files changed, 79 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb index dd405c7796..e6580c9930 100644 --- a/activerecord/lib/active_record/transactions.rb +++ b/activerecord/lib/active_record/transactions.rb @@ -7,6 +7,8 @@ module ActiveRecord included do define_callbacks :commit, :rollback, + :commit_without_transaction_enrollment, + :rollback_without_transaction_enrollment, scope: [:kind, :name] end @@ -233,6 +235,16 @@ module ActiveRecord set_callback(:rollback, :after, *args, &block) end + def after_commit_without_transaction_enrollment(*args, &block) # :nodoc: + set_options_for_callbacks!(args) + set_callback(:commit_without_transaction_enrollment, :after, *args, &block) + end + + def after_rollback_without_transaction_enrollment(*args, &block) # :nodoc: + set_options_for_callbacks!(args) + set_callback(:rollback_without_transaction_enrollment, :after, *args, &block) + end + def raise_in_transactional_callbacks ActiveSupport::Deprecation.warn('ActiveRecord::Base.raise_in_transactional_callbacks is deprecated and will be removed without replacement.') true @@ -301,7 +313,10 @@ module ActiveRecord # Ensure that it is not called if the object was never persisted (failed create), # but call it after the commit of a destroyed object. def committed!(should_run_callbacks: true) #:nodoc: - _run_commit_callbacks if should_run_callbacks && destroyed? || persisted? + if should_run_callbacks && destroyed? || persisted? + _run_commit_without_transaction_enrollment_callbacks + _run_commit_callbacks + end ensure force_clear_transaction_record_state end @@ -309,7 +324,10 @@ module ActiveRecord # Call the +after_rollback+ callbacks. The +force_restore_state+ argument indicates if the record # state should be rolled back to the beginning or just to the last savepoint. def rolledback!(force_restore_state: false, should_run_callbacks: true) #:nodoc: - _run_rollback_callbacks if should_run_callbacks + if should_run_callbacks + _run_rollback_without_transaction_enrollment_callbacks + _run_rollback_callbacks + end ensure restore_transaction_record_state(force_restore_state) clear_transaction_record_state diff --git a/activerecord/test/cases/transaction_callbacks_test.rb b/activerecord/test/cases/transaction_callbacks_test.rb index f185cda263..60b0fd4112 100644 --- a/activerecord/test/cases/transaction_callbacks_test.rb +++ b/activerecord/test/cases/transaction_callbacks_test.rb @@ -400,3 +400,62 @@ class CallbacksOnMultipleActionsTest < ActiveRecord::TestCase assert_equal [:update_and_destroy, :create_and_destroy], topic.history end end + + +class TransactionEnrollmentCallbacksTest < ActiveRecord::TestCase + + class TopicWithoutTransactionalEnrollmentCallbacks < ActiveRecord::Base + self.table_name = :topics + + after_commit_without_transaction_enrollment { |r| r.history << :commit } + after_rollback_without_transaction_enrollment { |r| r.history << :rollback } + + def history + @history ||= [] + end + end + + def setup + @topic = TopicWithoutTransactionalEnrollmentCallbacks.create! + end + + def test_commit_dont_enroll_transaction + @topic.transaction do + @topic.content = 'foo' + @topic.save! + end + assert @topic.history.empty? + end + + def test_commit_enrollment_transaction_when_call_add + @topic.transaction do + 2.times do + @topic.content = 'foo' + @topic.save! + end + @topic.class.connection.add_transaction_record(@topic) + end + assert_equal [:commit], @topic.history + end + + def test_rollback_dont_enroll_transaction + @topic.transaction do + @topic.content = 'foo' + @topic.save! + raise ActiveRecord::Rollback + end + assert @topic.history.empty? + end + + def test_rollback_enrollment_transaction_when_call_add + @topic.transaction do + 2.times do + @topic.content = 'foo' + @topic.save! + end + @topic.class.connection.add_transaction_record(@topic) + raise ActiveRecord::Rollback + end + assert_equal [:rollback], @topic.history + end +end -- cgit v1.2.3