mirror of https://github.com/rails/rails
`ActiveSupport::Calbacks#halted_callback_hook` receive callback name:
- The `halted_callback_hook` method is called whenever the `terminator` halt the callback execution. Usually, this translate to when a `before` callback throw an `:abort`. <details> <summary> Example </summary> ```ruby class Foo include ActiveSupport::Callbacks define_callbacks :save set_callback(:save, :before) { throw(:abort) } def run run_callbacks(:save) do 'hello' end end def halted_callback_hook(filter) # filter is the proc passed to `set_callback` above end end ``` </details> ### Problem When a class has multiple callbacks, (i.e. `save`, `validate` ...), it's impossible to tell in the halted_callback_hook which type of callback halted the execution. This is useful to take different action based on the callback. <details> <summary> Use Case </summary> ```ruby class Foo include ActiveSupport::Callbacks define_callbacks :save define_callbacks :validate set_callback(:save, :before) { throw(:abort) } set_callback(:validate, :before) { throw(:abort) } def run run_callbacks(:validate) do ... end run_callbacks(:save) do ... end end def halted_callback_hook(filter) Rails.logger.warn("Couldn't save the record, the ??? callback halted the execution") end end ``` </details> ### Solution Allow `halted_callback_hook` to receive a second argument which is the name of the callback being run.
This commit is contained in:
parent
51d73fb84b
commit
06dd162fb3
|
@ -72,7 +72,7 @@ module ActionController
|
|||
|
||||
private
|
||||
# A hook invoked every time a before callback is halted.
|
||||
def halted_callback_hook(filter)
|
||||
def halted_callback_hook(filter, _)
|
||||
ActiveSupport::Notifications.instrument("halted_callback.action_controller", filter: filter)
|
||||
end
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ module ActiveJob
|
|||
"#{operation}.active_job", payload.merge(adapter: queue_adapter, job: self), &enhanced_block
|
||||
end
|
||||
|
||||
def halted_callback_hook(_)
|
||||
def halted_callback_hook(*)
|
||||
super
|
||||
@_halted_callback_hook_called = true
|
||||
end
|
||||
|
|
|
@ -1,3 +1,26 @@
|
|||
* [Breaking change] `ActiveSupport::Callbacks#halted_callback_hook` now receive a 2nd argument:
|
||||
|
||||
`ActiveSupport::Callbacks#halted_callback_hook` now receive the name of the callback
|
||||
being halted as second argument.
|
||||
This change will allow you to differentiate which callbacks halted the chain
|
||||
and act accordingly.
|
||||
|
||||
```ruby
|
||||
class Book < ApplicationRecord
|
||||
before_save { throw(:abort) }
|
||||
before_create { throw(:abort) }
|
||||
|
||||
def halted_callback_hook(filter, callback_name)
|
||||
Rails.logger.info("Book couldn't be #{callback_name}d")
|
||||
end
|
||||
|
||||
Book.create # => "Book couldn't be created"
|
||||
book.save # => "Book couldn't be saved"
|
||||
end
|
||||
```
|
||||
|
||||
*Edouard Chin*
|
||||
|
||||
* Support `prepend` with `ActiveSupport::Concern`.
|
||||
|
||||
Allows a module with `extend ActiveSupport::Concern` to be prepended.
|
||||
|
@ -14,7 +37,7 @@
|
|||
prepend Imposter
|
||||
end
|
||||
|
||||
Class methods are prepended to the base class, concerning is also
|
||||
Class methods are prepended to the base class, concerning is also
|
||||
updated: `concerning :Imposter, prepend: true do`.
|
||||
|
||||
*Jason Karns, Elia Schito*
|
||||
|
|
|
@ -143,7 +143,7 @@ module ActiveSupport
|
|||
# A hook invoked every time a before callback is halted.
|
||||
# This can be overridden in ActiveSupport::Callbacks implementors in order
|
||||
# to provide better debugging/logging.
|
||||
def halted_callback_hook(filter)
|
||||
def halted_callback_hook(filter, name)
|
||||
end
|
||||
|
||||
module Conditionals # :nodoc:
|
||||
|
@ -159,17 +159,17 @@ module ActiveSupport
|
|||
Environment = Struct.new(:target, :halted, :value)
|
||||
|
||||
class Before
|
||||
def self.build(callback_sequence, user_callback, user_conditions, chain_config, filter)
|
||||
def self.build(callback_sequence, user_callback, user_conditions, chain_config, filter, name)
|
||||
halted_lambda = chain_config[:terminator]
|
||||
|
||||
if user_conditions.any?
|
||||
halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter)
|
||||
halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter, name)
|
||||
else
|
||||
halting(callback_sequence, user_callback, halted_lambda, filter)
|
||||
halting(callback_sequence, user_callback, halted_lambda, filter, name)
|
||||
end
|
||||
end
|
||||
|
||||
def self.halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter)
|
||||
def self.halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter, name)
|
||||
callback_sequence.before do |env|
|
||||
target = env.target
|
||||
value = env.value
|
||||
|
@ -179,7 +179,7 @@ module ActiveSupport
|
|||
result_lambda = -> { user_callback.call target, value }
|
||||
env.halted = halted_lambda.call(target, result_lambda)
|
||||
if env.halted
|
||||
target.send :halted_callback_hook, filter
|
||||
target.send :halted_callback_hook, filter, name
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -188,7 +188,7 @@ module ActiveSupport
|
|||
end
|
||||
private_class_method :halting_and_conditional
|
||||
|
||||
def self.halting(callback_sequence, user_callback, halted_lambda, filter)
|
||||
def self.halting(callback_sequence, user_callback, halted_lambda, filter, name)
|
||||
callback_sequence.before do |env|
|
||||
target = env.target
|
||||
value = env.value
|
||||
|
@ -197,9 +197,8 @@ module ActiveSupport
|
|||
unless halted
|
||||
result_lambda = -> { user_callback.call target, value }
|
||||
env.halted = halted_lambda.call(target, result_lambda)
|
||||
|
||||
if env.halted
|
||||
target.send :halted_callback_hook, filter
|
||||
target.send :halted_callback_hook, filter, name
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -337,7 +336,7 @@ module ActiveSupport
|
|||
|
||||
case kind
|
||||
when :before
|
||||
Filters::Before.build(callback_sequence, user_callback.make_lambda, user_conditions, chain_config, @filter)
|
||||
Filters::Before.build(callback_sequence, user_callback.make_lambda, user_conditions, chain_config, @filter, name)
|
||||
when :after
|
||||
Filters::After.build(callback_sequence, user_callback.make_lambda, user_conditions, chain_config)
|
||||
when :around
|
||||
|
|
|
@ -609,7 +609,7 @@ module CallbacksTest
|
|||
set_callback :save, :after, :third
|
||||
end
|
||||
|
||||
attr_reader :history, :saved, :halted
|
||||
attr_reader :history, :saved, :halted, :callback_name
|
||||
def initialize
|
||||
@history = []
|
||||
end
|
||||
|
@ -639,8 +639,9 @@ module CallbacksTest
|
|||
end
|
||||
end
|
||||
|
||||
def halted_callback_hook(filter)
|
||||
def halted_callback_hook(filter, name)
|
||||
@halted = filter
|
||||
@callback_name = name
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -823,6 +824,7 @@ module CallbacksTest
|
|||
terminator = CallbackTerminator.new
|
||||
terminator.save
|
||||
assert_equal :second, terminator.halted
|
||||
assert_equal :save, terminator.callback_name
|
||||
end
|
||||
|
||||
def test_block_never_called_if_terminated
|
||||
|
|
|
@ -127,6 +127,25 @@ which formats your action accepts, i.e.
|
|||
format.any(:xml, :json) { render request.format.to_sym => @people }
|
||||
```
|
||||
|
||||
### `ActiveSupport::Callbacks#halted_callback_hook` now receive a second argument
|
||||
|
||||
Active Support allows you to override the `halted_callback_hook` whenever a callback
|
||||
halts the chain. This method now receive a second argument which is the name of the callback being halted.
|
||||
If you have classes that override this method, make sure it accepts two arguments. Note that this is a breaking
|
||||
change without a prior deprecation cycle (for performance reasons).
|
||||
|
||||
Example:
|
||||
|
||||
```ruby
|
||||
class Book < ApplicationRecord
|
||||
before_save { throw(:abort) }
|
||||
before_create { throw(:abort) }
|
||||
|
||||
def halted_callback_hook(filter, callback_name) # => This method now accepts 2 arguments instead of 1
|
||||
Rails.logger.info("Book couldn't be #{callback_name}d")
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
Upgrading from Rails 5.2 to Rails 6.0
|
||||
-------------------------------------
|
||||
|
|
Loading…
Reference in New Issue