lib/active_support/callbacks.rb in activesupport-6.1.7.10 vs lib/active_support/callbacks.rb in activesupport-7.0.0.alpha1

- old
+ new

@@ -3,10 +3,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/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. # The typical use case is to have a base class define a set of callbacks @@ -274,11 +275,11 @@ 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. @@ -287,25 +288,21 @@ new chain.name, filter, kind, options, chain.config end attr_accessor :kind, :name - attr_reader :chain_config + attr_reader :chain_config, :filter def initialize(name, filter, kind, options, chain_config) @chain_config = chain_config @name = name @kind = kind @filter = filter - @key = compute_identifier filter @if = check_conditionals(options[:if]) @unless = check_conditionals(options[:unless]) end - def filter; @key; end - def raw_filter; @filter; end - def merge_conditional_options(chain, if_option:, unless_option:) options = { if: @if.dup, unless: @unless.dup } @@ -354,30 +351,21 @@ def check_conditionals(conditionals) return EMPTY_ARRAY if conditionals.blank? conditionals = Array(conditionals) - if conditionals.any? { |c| c.is_a?(String) } + if conditionals.any?(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.freeze end - def compute_identifier(filter) - case filter - when ::Proc - filter.object_id - else - filter - end - end - def conditions_lambdas @if.map { |c| CallTemplate.build(c, self).make_lambda } + @unless.map { |c| CallTemplate.build(c, self).inverted_lambda } end end @@ -515,11 +503,11 @@ def invoke_after(arg) @after.each { |a| a.call(arg) } end end - class CallbackChain #:nodoc:# + class CallbackChain # :nodoc:# include Enumerable attr_reader :name, :config def initialize(name, config) @@ -617,11 +605,11 @@ [type, filters, options.dup] end # This is used internally to append, prepend and skip callbacks to the # CallbackChain. - def __update_callbacks(name) #:nodoc: + def __update_callbacks(name) # :nodoc: ([self] + ActiveSupport::DescendantsTracker.descendants(self)).reverse_each do |target| chain = target.get_callbacks name yield target, chain.dup end end @@ -686,14 +674,36 @@ # Skip a previously set callback. Like +set_callback+, <tt>:if</tt> or # <tt>:unless</tt> options may be passed in order to control when the # callback is skipped. # - # class Writer < Person - # skip_callback :validate, :before, :check_membership, if: -> { age > 18 } + # class Writer < PersonRecord + # attr_accessor :age + # skip_callback :save, :before, :saving_message, if: -> { age > 18 } # end # + # When if option returns true, callback is skipped. + # + # writer = Writer.new + # writer.age = 20 + # writer.save + # + # Output: + # - save + # saved + # + # When if option returns false, callback is NOT skipped. + # + # young_writer = Writer.new + # young_writer.age = 17 + # young_writer.save + # + # Output: + # saving... + # - save + # saved + # # An <tt>ArgumentError</tt> will be raised if the callback has not # already been set (unless the <tt>:raise</tt> option is set to <tt>false</tt>). def skip_callback(name, *filter_list, &block) type, filters, options = normalize_callback_params(filter_list, block) @@ -842,21 +852,15 @@ protected def get_callbacks(name) # :nodoc: __callbacks[name.to_sym] end - 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) + def set_callbacks(name, callbacks) # :nodoc: + unless singleton_class.method_defined?(:__callbacks, false) + self.__callbacks = __callbacks.dup 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 + self.__callbacks[name.to_sym] = callbacks + self.__callbacks end end end end