aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel
diff options
context:
space:
mode:
Diffstat (limited to 'activemodel')
-rw-r--r--activemodel/CHANGELOG.md11
-rw-r--r--activemodel/lib/active_model/callbacks.rb3
-rw-r--r--activemodel/lib/active_model/validations/callbacks.rb5
-rw-r--r--activemodel/test/cases/callbacks_test.rb16
-rw-r--r--activemodel/test/cases/validations/callbacks_test.rb20
5 files changed, 44 insertions, 11 deletions
diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md
index b86e988841..d643a08235 100644
--- a/activemodel/CHANGELOG.md
+++ b/activemodel/CHANGELOG.md
@@ -1 +1,12 @@
+* Change the way in which callback chains can be halted.
+
+ The preferred method to halt a callback chain from now on is to explicitly
+ `throw(:abort)`.
+ In the past, returning `false` in an ActiveModel or ActiveModel::Validations
+ `before_` callback had the side effect of halting the callback chain.
+ This is not recommended anymore and, depending on the value of the
+ `config.active_support.halt_callback_chains_on_return_false` option, will
+ either not work at all or display a deprecation warning.
+
+
Please check [4-2-stable](https://github.com/rails/rails/blob/4-2-stable/activemodel/CHANGELOG.md) for previous changes.
diff --git a/activemodel/lib/active_model/callbacks.rb b/activemodel/lib/active_model/callbacks.rb
index b3d70dc515..6214802074 100644
--- a/activemodel/lib/active_model/callbacks.rb
+++ b/activemodel/lib/active_model/callbacks.rb
@@ -6,7 +6,7 @@ module ActiveModel
# Provides an interface for any class to have Active Record like callbacks.
#
# Like the Active Record methods, the callback chain is aborted as soon as
- # one of the methods in the chain returns +false+.
+ # one of the methods throws +:abort+.
#
# First, extend ActiveModel::Callbacks from the class you are creating:
#
@@ -103,7 +103,6 @@ module ActiveModel
def define_model_callbacks(*callbacks)
options = callbacks.extract_options!
options = {
- terminator: ->(_,result) { result == false },
skip_after_callbacks_if_terminated: true,
scope: [:kind, :name],
only: [:before, :around, :after]
diff --git a/activemodel/lib/active_model/validations/callbacks.rb b/activemodel/lib/active_model/validations/callbacks.rb
index 25ccabd66b..4b58ef66e3 100644
--- a/activemodel/lib/active_model/validations/callbacks.rb
+++ b/activemodel/lib/active_model/validations/callbacks.rb
@@ -15,15 +15,14 @@ module ActiveModel
# after_validation :do_stuff_after_validation
# end
#
- # Like other <tt>before_*</tt> callbacks if +before_validation+ returns
- # +false+ then <tt>valid?</tt> will not be called.
+ # Like other <tt>before_*</tt> callbacks if +before_validation+ throws
+ # +:abort+ then <tt>valid?</tt> will not be called.
module Callbacks
extend ActiveSupport::Concern
included do
include ActiveSupport::Callbacks
define_callbacks :validation,
- terminator: ->(_,result) { result == false },
skip_after_callbacks_if_terminated: true,
scope: [:kind, :name]
end
diff --git a/activemodel/test/cases/callbacks_test.rb b/activemodel/test/cases/callbacks_test.rb
index 2ac681b8d8..85455c112c 100644
--- a/activemodel/test/cases/callbacks_test.rb
+++ b/activemodel/test/cases/callbacks_test.rb
@@ -33,11 +33,13 @@ class CallbacksTest < ActiveModel::TestCase
def initialize(options = {})
@callbacks = []
@valid = options[:valid]
- @before_create_returns = options[:before_create_returns]
+ @before_create_returns = options.fetch(:before_create_returns, true)
+ @before_create_throws = options[:before_create_throws]
end
def before_create
@callbacks << :before_create
+ throw(@before_create_throws) if @before_create_throws
@before_create_returns
end
@@ -62,10 +64,18 @@ class CallbacksTest < ActiveModel::TestCase
assert_equal model.callbacks.last, :final_callback
end
- test "the callback chain is halted when a before callback returns false" do
+ test "the callback chain is halted when a before callback returns false (deprecated)" do
model = ModelCallbacks.new(before_create_returns: false)
+ assert_deprecated do
+ model.create
+ assert_equal model.callbacks.last, :before_create
+ end
+ end
+
+ test "the callback chain is halted when a callback throws :abort" do
+ model = ModelCallbacks.new(before_create_throws: :abort)
model.create
- assert_equal model.callbacks.last, :before_create
+ assert_equal model.callbacks, [:before_create]
end
test "after callbacks are not executed if the block returns false" do
diff --git a/activemodel/test/cases/validations/callbacks_test.rb b/activemodel/test/cases/validations/callbacks_test.rb
index 5d6d48b824..4b0dd58efb 100644
--- a/activemodel/test/cases/validations/callbacks_test.rb
+++ b/activemodel/test/cases/validations/callbacks_test.rb
@@ -30,11 +30,16 @@ class DogWithTwoValidators < Dog
before_validation { self.history << 'before_validation_marker2' }
end
-class DogBeforeValidatorReturningFalse < Dog
+class DogDeprecatedBeforeValidatorReturningFalse < Dog
before_validation { false }
before_validation { self.history << 'before_validation_marker2' }
end
+class DogBeforeValidatorThrowingAbort < Dog
+ before_validation { throw :abort }
+ before_validation { self.history << 'before_validation_marker2' }
+end
+
class DogAfterValidatorReturningFalse < Dog
after_validation { false }
after_validation { self.history << 'after_validation_marker' }
@@ -86,13 +91,22 @@ class CallbacksWithMethodNamesShouldBeCalled < ActiveModel::TestCase
assert_equal ['before_validation_marker1', 'before_validation_marker2'], d.history
end
- def test_further_callbacks_should_not_be_called_if_before_validation_returns_false
- d = DogBeforeValidatorReturningFalse.new
+ def test_further_callbacks_should_not_be_called_if_before_validation_throws_abort
+ d = DogBeforeValidatorThrowingAbort.new
output = d.valid?
assert_equal [], d.history
assert_equal false, output
end
+ def test_deprecated_further_callbacks_should_not_be_called_if_before_validation_returns_false
+ d = DogDeprecatedBeforeValidatorReturningFalse.new
+ assert_deprecated do
+ output = d.valid?
+ assert_equal [], d.history
+ assert_equal false, output
+ end
+ end
+
def test_further_callbacks_should_be_called_if_after_validation_returns_false
d = DogAfterValidatorReturningFalse.new
d.valid?