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