lib/active_support/callbacks.rb in activesupport-6.0.6.1 vs lib/active_support/callbacks.rb in activesupport-6.1.0.rc1
- old
+ new
@@ -2,14 +2,11 @@
require "active_support/concern"
require "active_support/descendants_tracker"
require "active_support/core_ext/array/extract_options"
require "active_support/core_ext/class/attribute"
-require "active_support/core_ext/kernel/reporting"
-require "active_support/core_ext/kernel/singleton_class"
require "active_support/core_ext/string/filters"
-require "active_support/deprecation"
require "thread"
module ActiveSupport
# Callbacks are code hooks that are run at key points in an object's life cycle.
# The typical use case is to have a base class define a set of callbacks
@@ -101,53 +98,54 @@
yield if block_given?
else
env = Filters::Environment.new(self, false, nil)
next_sequence = callbacks.compile
- invoke_sequence = Proc.new do
- skipped = nil
- while true
- current = next_sequence
- current.invoke_before(env)
- if current.final?
- env.value = !env.halted && (!block_given? || yield)
- elsif current.skip?(env)
- (skipped ||= []) << current
- next_sequence = next_sequence.nested
- next
- else
- next_sequence = next_sequence.nested
- begin
- target, block, method, *arguments = current.expand_call_template(env, invoke_sequence)
- target.send(method, *arguments, &block)
- ensure
- next_sequence = current
- end
- end
- current.invoke_after(env)
- skipped.pop.invoke_after(env) while skipped && skipped.first
- break env.value
- end
- end
-
# Common case: no 'around' callbacks defined
if next_sequence.final?
next_sequence.invoke_before(env)
env.value = !env.halted && (!block_given? || yield)
next_sequence.invoke_after(env)
env.value
else
+ invoke_sequence = Proc.new do
+ skipped = nil
+
+ while true
+ current = next_sequence
+ current.invoke_before(env)
+ if current.final?
+ env.value = !env.halted && (!block_given? || yield)
+ elsif current.skip?(env)
+ (skipped ||= []) << current
+ next_sequence = next_sequence.nested
+ next
+ else
+ next_sequence = next_sequence.nested
+ begin
+ target, block, method, *arguments = current.expand_call_template(env, invoke_sequence)
+ target.send(method, *arguments, &block)
+ ensure
+ next_sequence = current
+ end
+ end
+ current.invoke_after(env)
+ skipped.pop.invoke_after(env) while skipped&.first
+ break env.value
+ end
+ end
+
invoke_sequence.call
end
end
end
private
# 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:
class Value
def initialize(&block)
@@ -159,51 +157,50 @@
module Filters
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
halted = env.halted
if !halted && user_conditions.all? { |c| c.call(target, value) }
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
env
end
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
halted = env.halted
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
env
end
@@ -298,12 +295,12 @@
@chain_config = chain_config
@name = name
@kind = kind
@filter = filter
@key = compute_identifier filter
- @if = check_conditionals(Array(options[:if]))
- @unless = check_conditionals(Array(options[:unless]))
+ @if = check_conditionals(options[:if])
+ @unless = check_conditionals(options[:unless])
end
def filter; @key; end
def raw_filter; @filter; end
@@ -337,11 +334,11 @@
user_conditions = conditions_lambdas
user_callback = CallTemplate.build(@filter, self)
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
callback_sequence.around(user_callback, user_conditions)
end
@@ -350,20 +347,26 @@
def current_scopes
Array(chain_config[:scope]).map { |s| public_send(s) }
end
private
+ EMPTY_ARRAY = [].freeze
+ private_constant :EMPTY_ARRAY
+
def check_conditionals(conditionals)
+ return EMPTY_ARRAY if conditionals.blank?
+
+ conditionals = Array(conditionals)
if conditionals.any? { |c| c.is_a?(String) }
raise ArgumentError, <<-MSG.squish
Passing string to be evaluated in :if and :unless conditional
options is not supported. Pass a symbol for an instance method,
or a lambda, proc or block, instead.
MSG
end
- conditionals
+ conditionals.freeze
end
def compute_identifier(filter)
case filter
when ::Proc
@@ -401,25 +404,21 @@
# target.send(method, *arguments, &block)
#
# The actual invocation is left up to the caller to minimize
# call stack pollution.
def expand(target, value, block)
- result = @arguments.map { |arg|
+ expanded = [@override_target || target, @override_block || block, @method_name]
+
+ @arguments.each do |arg|
case arg
- when :value; value
- when :target; target
- when :block; block || raise(ArgumentError)
+ when :value then expanded << value
+ when :target then expanded << target
+ when :block then expanded << (block || raise(ArgumentError))
end
- }
+ end
- result.unshift @method_name
- result.unshift @override_block || block
- result.unshift @override_target || target
-
- # target, block, method, *arguments = result
- # target.send(method, *arguments, &block)
- result
+ expanded
end
# Return a lambda that will make this call when given the input
# values.
def make_lambda
@@ -843,11 +842,21 @@
protected
def get_callbacks(name) # :nodoc:
__callbacks[name.to_sym]
end
- def set_callbacks(name, callbacks) # :nodoc:
- self.__callbacks = __callbacks.merge(name.to_sym => callbacks)
+ if Module.instance_method(:method_defined?).arity == 1 # Ruby 2.5 and older
+ def set_callbacks(name, callbacks) # :nodoc:
+ self.__callbacks = __callbacks.merge(name.to_sym => callbacks)
+ end
+ else # Ruby 2.6 and newer
+ def set_callbacks(name, callbacks) # :nodoc:
+ unless singleton_class.method_defined?(:__callbacks, false)
+ self.__callbacks = __callbacks.dup
+ end
+ self.__callbacks[name.to_sym] = callbacks
+ self.__callbacks
+ end
end
end
end
end