lib/active_support/callbacks.rb in activesupport-7.0.8.6 vs lib/active_support/callbacks.rb in activesupport-7.1.0.beta1
- old
+ new
@@ -7,11 +7,13 @@
require "active_support/core_ext/string/filters"
require "active_support/core_ext/object/blank"
require "thread"
module ActiveSupport
- # Callbacks are code hooks that are run at key points in an object's life cycle.
+ # = Active Support \Callbacks
+ #
+ # \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
# relevant to the other functionality it supplies, so that subclasses can
# install callbacks that enhance or modify the base functionality without
# needing to override or redefine methods of the base class.
#
@@ -66,11 +68,11 @@
included do
extend ActiveSupport::DescendantsTracker
class_attribute :__callbacks, instance_writer: false, default: {}
end
- CALLBACK_FILTER_TYPES = [:before, :after, :around]
+ CALLBACK_FILTER_TYPES = [:before, :after, :around].freeze
# Runs the callbacks for the given event.
#
# Calls the before and around callbacks in the order they were set, yields
# the block (if given one), and then runs the after callbacks in reverse
@@ -90,19 +92,20 @@
# user code, it has an additional design goal of minimizing its impact on
# the visible call stack. An exception from inside a :before or :after
# callback can be as noisy as it likes -- but when control has passed
# smoothly through and into the supplied block, we want as little evidence
# as possible that we were here.
- def run_callbacks(kind)
+ def run_callbacks(kind, type = nil)
callbacks = __callbacks[kind.to_sym]
if callbacks.empty?
yield if block_given?
else
env = Filters::Environment.new(self, false, nil)
- next_sequence = callbacks.compile
+ next_sequence = callbacks.compile(type)
+
# 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)
@@ -610,46 +613,60 @@
@config = {
scope: [:kind],
terminator: default_terminator
}.merge!(config)
@chain = []
- @callbacks = nil
+ @all_callbacks = nil
+ @single_callbacks = {}
@mutex = Mutex.new
end
def each(&block); @chain.each(&block); end
def index(o); @chain.index(o); end
def empty?; @chain.empty?; end
def insert(index, o)
- @callbacks = nil
+ @all_callbacks = nil
+ @single_callbacks.clear
@chain.insert(index, o)
end
def delete(o)
- @callbacks = nil
+ @all_callbacks = nil
+ @single_callbacks.clear
@chain.delete(o)
end
def clear
- @callbacks = nil
+ @all_callbacks = nil
+ @single_callbacks.clear
@chain.clear
self
end
def initialize_copy(other)
- @callbacks = nil
+ @all_callbacks = nil
+ @single_callbacks = {}
@chain = other.chain.dup
@mutex = Mutex.new
end
- def compile
- @callbacks || @mutex.synchronize do
- final_sequence = CallbackSequence.new
- @callbacks ||= @chain.reverse.inject(final_sequence) do |callback_sequence, callback|
- callback.apply callback_sequence
+ def compile(type)
+ if type.nil?
+ @all_callbacks || @mutex.synchronize do
+ final_sequence = CallbackSequence.new
+ @all_callbacks ||= @chain.reverse.inject(final_sequence) do |callback_sequence, callback|
+ callback.apply(callback_sequence)
+ end
end
+ else
+ @single_callbacks[type] || @mutex.synchronize do
+ final_sequence = CallbackSequence.new
+ @single_callbacks[type] ||= @chain.reverse.inject(final_sequence) do |callback_sequence, callback|
+ type == callback.kind ? callback.apply(callback_sequence) : callback_sequence
+ end
+ end
end
end
def append(*callbacks)
callbacks.each { |c| append_one(c) }
@@ -662,23 +679,26 @@
protected
attr_reader :chain
private
def append_one(callback)
- @callbacks = nil
+ @all_callbacks = nil
+ @single_callbacks.clear
remove_duplicates(callback)
@chain.push(callback)
end
def prepend_one(callback)
- @callbacks = nil
+ @all_callbacks = nil
+ @single_callbacks.clear
remove_duplicates(callback)
@chain.unshift(callback)
end
def remove_duplicates(callback)
- @callbacks = nil
+ @all_callbacks = nil
+ @single_callbacks.clear
@chain.delete_if { |c| callback.duplicates?(c) }
end
def default_terminator
Proc.new do |target, result_lambda|
@@ -701,10 +721,10 @@
end
# This is used internally to append, prepend and skip callbacks to the
# CallbackChain.
def __update_callbacks(name) # :nodoc:
- ([self] + self.descendants).reverse_each do |target|
+ self.descendants.prepend(self).reverse_each do |target|
chain = target.get_callbacks name
yield target, chain.dup
end
end