lib/active_support/callbacks.rb in activesupport-7.1.5 vs lib/active_support/callbacks.rb in activesupport-7.2.0.beta1
- old
+ new
@@ -148,141 +148,89 @@
# This can be overridden in ActiveSupport::Callbacks implementors in order
# to provide better debugging/logging.
def halted_callback_hook(filter, name)
end
- module Conditionals # :nodoc:
+ module Conditionals # :nodoc: all
class Value
def initialize(&block)
@block = block
end
def call(target, value); @block.call(value); end
end
end
- module Filters
+ module Filters # :nodoc: all
Environment = Struct.new(:target, :halted, :value)
class Before
- def self.build(callback_sequence, user_callback, user_conditions, chain_config, filter, name)
+ def initialize(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, name)
- else
- halting(callback_sequence, user_callback, halted_lambda, filter, name)
- end
+ @user_callback, @user_conditions, @halted_lambda, @filter, @name = user_callback, user_conditions, halted_lambda, filter, name
+ freeze
end
+ attr_reader :user_callback, :user_conditions, :halted_lambda, :filter, :name
- 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
+ def call(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, name
- end
+ 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, name
end
-
- env
end
+
+ env
end
- private_class_method :halting_and_conditional
- 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, name
- end
- end
-
- env
- end
+ def apply(callback_sequence)
+ callback_sequence.before(self)
end
- private_class_method :halting
end
class After
- def self.build(callback_sequence, user_callback, user_conditions, chain_config)
- if chain_config[:skip_after_callbacks_if_terminated]
- if user_conditions.any?
- halting_and_conditional(callback_sequence, user_callback, user_conditions)
- else
- halting(callback_sequence, user_callback)
- end
- else
- if user_conditions.any?
- conditional callback_sequence, user_callback, user_conditions
- else
- simple callback_sequence, user_callback
- end
- end
+ attr_reader :user_callback, :user_conditions, :halting
+ def initialize(user_callback, user_conditions, chain_config)
+ halting = chain_config[:skip_after_callbacks_if_terminated]
+ @user_callback, @user_conditions, @halting = user_callback, user_conditions, halting
+ freeze
end
- def self.halting_and_conditional(callback_sequence, user_callback, user_conditions)
- callback_sequence.after do |env|
- target = env.target
- value = env.value
- halted = env.halted
+ def call(env)
+ target = env.target
+ value = env.value
+ halted = env.halted
- if !halted && user_conditions.all? { |c| c.call(target, value) }
- user_callback.call target, value
- end
-
- env
+ if (!halted || !@halting) && user_conditions.all? { |c| c.call(target, value) }
+ user_callback.call target, value
end
+
+ env
end
- private_class_method :halting_and_conditional
- def self.halting(callback_sequence, user_callback)
- callback_sequence.after do |env|
- unless env.halted
- user_callback.call env.target, env.value
- end
-
- env
- end
+ def apply(callback_sequence)
+ callback_sequence.after(self)
end
- private_class_method :halting
+ end
- def self.conditional(callback_sequence, user_callback, user_conditions)
- callback_sequence.after do |env|
- target = env.target
- value = env.value
-
- if user_conditions.all? { |c| c.call(target, value) }
- user_callback.call target, value
- end
-
- env
- end
+ class Around
+ def initialize(user_callback, user_conditions)
+ @user_callback, @user_conditions = user_callback, user_conditions
+ freeze
end
- private_class_method :conditional
- def self.simple(callback_sequence, user_callback)
- callback_sequence.after do |env|
- user_callback.call env.target, env.value
-
- env
- end
+ def apply(callback_sequence)
+ callback_sequence.around(@user_callback, @user_conditions)
end
- private_class_method :simple
end
end
- class Callback # :nodoc:#
+ class Callback # :nodoc:
def self.build(chain, filter, kind, options)
if filter.is_a?(String)
raise ArgumentError, <<-MSG.squish
Passing string to define a callback is not supported. See the `.set_callback`
documentation to see supported values.
@@ -300,10 +248,12 @@
@name = name
@kind = kind
@filter = filter
@if = check_conditionals(options[:if])
@unless = check_conditionals(options[:unless])
+
+ compiled
end
def merge_conditional_options(chain, if_option:, unless_option:)
options = {
if: @if.dup,
@@ -327,23 +277,30 @@
else
false
end
end
+ def compiled
+ @compiled ||=
+ begin
+ user_conditions = conditions_lambdas
+ user_callback = CallTemplate.build(@filter, self)
+
+ case kind
+ when :before
+ Filters::Before.new(user_callback.make_lambda, user_conditions, chain_config, @filter, name)
+ when :after
+ Filters::After.new(user_callback.make_lambda, user_conditions, chain_config)
+ when :around
+ Filters::Around.new(user_callback, user_conditions)
+ end
+ end
+ end
+
# Wraps code with filter
def apply(callback_sequence)
- 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, 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
+ compiled.apply(callback_sequence)
end
def current_scopes
Array(chain_config[:scope]).map { |s| public_send(s) }
end
@@ -366,18 +323,20 @@
conditionals.freeze
end
def conditions_lambdas
- @if.map { |c| CallTemplate.build(c, self).make_lambda } +
+ conditions =
+ @if.map { |c| CallTemplate.build(c, self).make_lambda } +
@unless.map { |c| CallTemplate.build(c, self).inverted_lambda }
+ conditions.empty? ? EMPTY_ARRAY : conditions
end
end
# A future invocation of user-supplied code (either as a callback,
# or a condition filter).
- module CallTemplate # :nodoc:
+ module CallTemplate # :nodoc: all
class MethodCall
def initialize(method)
@method_name = method
end
@@ -560,20 +519,22 @@
def initialize(nested = nil, call_template = nil, user_conditions = nil)
@nested = nested
@call_template = call_template
@user_conditions = user_conditions
- @before = []
- @after = []
+ @before = nil
+ @after = nil
end
- def before(&before)
+ def before(before)
+ @before ||= []
@before.unshift(before)
self
end
- def after(&after)
+ def after(after)
+ @after ||= []
@after.push(after)
self
end
def around(call_template, user_conditions)
@@ -593,14 +554,14 @@
def expand_call_template(arg, block)
@call_template.expand(arg.target, arg.value, block)
end
def invoke_before(arg)
- @before.each { |b| b.call(arg) }
+ @before&.each { |b| b.call(arg) }
end
def invoke_after(arg)
- @after.each { |a| a.call(arg) }
+ @after&.each { |a| a.call(arg) }
end
end
class CallbackChain # :nodoc:
include Enumerable