From f43e06157fb35d8a68ac634ff89c65c1df27a0cc Mon Sep 17 00:00:00 2001 From: Fabien Jakimowicz Date: Thu, 24 Jun 2010 05:03:49 +0200 Subject: [PATCH] add before_write and after_write callbacks --- activerecord/lib/active_record/callbacks.rb | 46 ++++++++++++-------- activerecord/test/cases/callbacks_test.rb | 60 ++++++++++++++++++++++++--- activerecord/test/cases/lifecycle_test.rb | 4 +- 3 files changed, 84 insertions(+), 26 deletions(-) diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb index e375037..b9903e4 100644 --- a/activerecord/lib/active_record/callbacks.rb +++ b/activerecord/lib/active_record/callbacks.rb @@ -7,21 +7,23 @@ module ActiveRecord # before they're validated (by overwriting +before_validation+). As an example of the callbacks initiated, consider # the Base#save call for a new record: # - # * (-) save - # * (-) valid - # * (1) before_validation - # * (2) before_validation_on_create - # * (-) validate - # * (-) validate_on_create - # * (3) after_validation - # * (4) after_validation_on_create - # * (5) before_save - # * (6) before_create - # * (-) create - # * (7) after_create - # * (8) after_save - # - # That's a total of eight callbacks, which gives you immense power to react and prepare for each state in the + # * (-) save + # * (-) valid + # * (1) before_validation + # * (2) before_validation_on_create + # * (-) validate + # * (-) validate_on_create + # * (3) after_validation + # * (4) after_validation_on_create + # * (5) before_write + # * (6) before_save + # * (7) before_create + # * (-) create + # * (8) after_create + # * (9) after_save + # * (10) after_write + # + # That's a total of ten callbacks, which gives you immense power to react and prepare for each state in the # Active Record lifecycle. The sequence for calling Base#save an existing record is similar, except that each # _on_create callback is replaced by the corresponding _on_update callback. # @@ -214,7 +216,7 @@ module ActiveRecord CALLBACKS = %w( after_find after_initialize before_save after_save before_create after_create before_update after_update before_validation after_validation before_validation_on_create after_validation_on_create before_validation_on_update - after_validation_on_update before_destroy after_destroy + after_validation_on_update before_destroy after_destroy before_write after_write ) def self.included(base) #:nodoc: @@ -233,6 +235,9 @@ module ActiveRecord # Is called after the object has been instantiated by a call to Base.new. #def after_initialize() end + + # Is called _before_ every write operation on database (+create+, +update+ or +destroy+) + def before_write() end # Is called _before_ Base.save (regardless of whether it's a +create+ or +update+ save). def before_save() end @@ -246,9 +251,10 @@ module ActiveRecord # end def after_save() end def create_or_update_with_callbacks #:nodoc: - return false if callback(:before_save) == false + return false if callback(:before_write) == false or callback(:before_save) == false if result = create_or_update_without_callbacks callback(:after_save) + callback(:after_write) end result end @@ -333,11 +339,15 @@ module ActiveRecord # end def after_destroy() end def destroy_with_callbacks #:nodoc: - return false if callback(:before_destroy) == false + return false if callback(:before_write) == false or callback(:before_destroy) == false result = destroy_without_callbacks callback(:after_destroy) + callback(:after_write) result end + + # Is called _after_ a write operation on database (+create+, +update+ or +destroy+) + def after_write() end private def callback(method) diff --git a/activerecord/test/cases/callbacks_test.rb b/activerecord/test/cases/callbacks_test.rb index 95fddae..c0f7fc5 100644 --- a/activerecord/test/cases/callbacks_test.rb +++ b/activerecord/test/cases/callbacks_test.rb @@ -66,6 +66,12 @@ class RecursiveCallbackDeveloper < ActiveRecord::Base after_save :on_after_save attr_reader :on_before_save_called, :on_after_save_called + + def on_before_write + @on_before_write_called ||= 0 + @on_before_write_called += 1 + save unless @on_before_write_called > 1 + end def on_before_save @on_before_save_called ||= 0 @@ -78,6 +84,13 @@ class RecursiveCallbackDeveloper < ActiveRecord::Base @on_after_save_called += 1 save unless @on_after_save_called > 1 end + + def on_after_write + @on_after_write_called ||= 0 + @on_after_write_called += 1 + save unless @on_after_write_called > 1 + end + end class ImmutableDeveloper < ActiveRecord::Base @@ -122,18 +135,20 @@ end class CallbackCancellationDeveloper < ActiveRecord::Base set_table_name 'developers' - attr_reader :after_save_called, :after_create_called, :after_update_called, :after_destroy_called - attr_accessor :cancel_before_save, :cancel_before_create, :cancel_before_update, :cancel_before_destroy + attr_reader :after_save_called, :after_create_called, :after_update_called, :after_destroy_called, :after_write_called + attr_accessor :cancel_before_write, :cancel_before_save, :cancel_before_create, :cancel_before_update, :cancel_before_destroy def before_save; !@cancel_before_save; end def before_create; !@cancel_before_create; end def before_update; !@cancel_before_update; end def before_destroy; !@cancel_before_destroy; end + def before_write; !@cancel_before_write; end def after_save; @after_save_called = true; end def after_update; @after_update_called = true; end def after_create; @after_create_called = true; end def after_destroy; @after_destroy_called = true; end + def after_write; @after_write_called = true; end end class CallbacksTest < ActiveRecord::TestCase @@ -244,6 +259,10 @@ class CallbacksTest < ActiveRecord::TestCase [ :after_validation_on_create, :proc ], [ :after_validation_on_create, :object ], [ :after_validation_on_create, :block ], + [ :before_write, :string ], + [ :before_write, :proc ], + [ :before_write, :object ], + [ :before_write, :block ], [ :before_save, :string ], [ :before_save, :proc ], [ :before_save, :object ], @@ -259,7 +278,11 @@ class CallbacksTest < ActiveRecord::TestCase [ :after_save, :string ], [ :after_save, :proc ], [ :after_save, :object ], - [ :after_save, :block ] + [ :after_save, :block ], + [ :after_write, :string ], + [ :after_write, :proc ], + [ :after_write, :object ], + [ :after_write, :block ] ], david.history end @@ -291,6 +314,10 @@ class CallbacksTest < ActiveRecord::TestCase [ :after_validation_on_update, :proc ], [ :after_validation_on_update, :object ], [ :after_validation_on_update, :block ], + [ :before_write, :string ], + [ :before_write, :proc ], + [ :before_write, :object ], + [ :before_write, :block ], [ :before_save, :string ], [ :before_save, :proc ], [ :before_save, :object ], @@ -306,7 +333,11 @@ class CallbacksTest < ActiveRecord::TestCase [ :after_save, :string ], [ :after_save, :proc ], [ :after_save, :object ], - [ :after_save, :block ] + [ :after_save, :block ], + [ :after_write, :string ], + [ :after_write, :proc ], + [ :after_write, :object ], + [ :after_write, :block ] ], david.history end @@ -322,6 +353,10 @@ class CallbacksTest < ActiveRecord::TestCase [ :after_initialize, :proc ], [ :after_initialize, :object ], [ :after_initialize, :block ], + [ :before_write, :string ], + [ :before_write, :proc ], + [ :before_write, :object ], + [ :before_write, :block ], [ :before_destroy, :string ], [ :before_destroy, :proc ], [ :before_destroy, :object ], @@ -329,7 +364,11 @@ class CallbacksTest < ActiveRecord::TestCase [ :after_destroy, :string ], [ :after_destroy, :proc ], [ :after_destroy, :object ], - [ :after_destroy, :block ] + [ :after_destroy, :block ], + [ :after_write, :string ], + [ :after_write, :proc ], + [ :after_write, :object ], + [ :after_write, :block ] ], david.history end @@ -348,6 +387,14 @@ class CallbacksTest < ActiveRecord::TestCase ], david.history end + def test_before_write_returning_false + someone = CallbackCancellationDeveloper.new + someone.cancel_before_write = true + assert someone.valid? + assert !someone.save + assert_save_callbacks_not_called(someone) + end + def test_before_save_returning_false david = ImmutableDeveloper.find(1) assert david.valid? @@ -366,7 +413,7 @@ class CallbacksTest < ActiveRecord::TestCase assert !someone.save assert_save_callbacks_not_called(someone) end - + def test_before_create_returning_false someone = CallbackCancellationDeveloper.new someone.cancel_before_create = true @@ -395,6 +442,7 @@ class CallbacksTest < ActiveRecord::TestCase end def assert_save_callbacks_not_called(someone) + assert !someone.after_write_called assert !someone.after_save_called assert !someone.after_create_called assert !someone.after_update_called diff --git a/activerecord/test/cases/lifecycle_test.rb b/activerecord/test/cases/lifecycle_test.rb index 54fb3d8..5ef11b2 100644 --- a/activerecord/test/cases/lifecycle_test.rb +++ b/activerecord/test/cases/lifecycle_test.rb @@ -82,7 +82,7 @@ class LifecycleTest < ActiveRecord::TestCase assert_equal original_count - (1 + topic_to_be_destroyed.replies.size), Topic.count end - def test_after_save + def test_after_write ActiveRecord::Base.observers = :topic_manual_observer ActiveRecord::Base.instantiate_observers @@ -91,7 +91,7 @@ class LifecycleTest < ActiveRecord::TestCase topic.save assert TopicManualObserver.instance.has_been_notified? - assert_equal :after_save, TopicManualObserver.instance.callbacks.last["callback_method"] + assert_equal :after_write, TopicManualObserver.instance.callbacks.last["callback_method"] end def test_observer_update_on_save -- 1.7.1