module ActiveSupport::Callbacks::ClassMethods

def __update_callbacks(name) #:nodoc:

:nodoc:
CallbackChain.
This is used internally to append, prepend and skip callbacks to the
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

def define_callbacks(*names)

`!`, `?` or `=`.
NOTE: +method_name+ passed to `define_model_callbacks` must not end with

would call Audit#save.

define_callbacks :save, scope: [:name]

A declaration like

which callbacks are being defined.
callback (before/after/around) and +:name+ refers to the method on
and +:name+ have special meanings: +:kind+ refers to the kind of
case "kind" is "before" and "name" is "save". In this context +:kind+
by calling #{kind}_#{name} on the given instance. In this
would trigger Audit#before_save instead. That's constructed

define_callbacks :save, scope: [:kind, :name]

Audit#before will be called. On the other hand
In the above case whenever you save an account the method

end
end
end
puts 'save in main'
run_callbacks :save do
def save

set_callback :save, :before, Audit.new
define_callbacks :save

include ActiveSupport::Callbacks
class Account

end
end
puts 'Audit: before_save is called'
def before_save(caller)

end
puts 'Audit: before is called'
def before(caller)
class Audit

object is used as a callback.
* :scope - Indicates which methods should be executed when an

option is specified.
terminated or not. This option makes sense only when :terminator
default after callbacks are executed no matter if callback chain was
callbacks should be terminated by the :terminator option. By
* :skip_after_callbacks_if_terminated - Determines if after

The default terminator halts the chain when a callback throws +:abort+.

any successive before and around callback is not executed.
In this example, if any before validate callbacks returns +false+,

define_callbacks :validate, terminator: ->(target, result_lambda) { result_lambda.call == false }

to the terminator lambda.
The current object and the result lambda of the callback will be provided
This should be a lambda to be executed.
being called and the event from being triggered.
callback chain, preventing following before and around callbacks from
* :terminator - Determines when a before filter will halt the

===== Options

define_callbacks :initialize, :save, :destroy
define_callbacks :validate

Define sets of events in the object life cycle that support callbacks.
def define_callbacks(*names)
  options = names.extract_options!
  names.each do |name|
    class_attribute "_#{name}_callbacks", instance_writer: false
    set_callbacks name, CallbackChain.new(name, options)
    module_eval <<-RUBY, __FILE__, __LINE__ + 1
      def _run_#{name}_callbacks(&block)
        __run_callbacks__(_#{name}_callbacks, &block)
      end
    RUBY
  end
end

def deprecated_false_terminator # :nodoc:

:nodoc:
def deprecated_false_terminator # :nodoc:
  Proc.new do |target, result_lambda|
    terminate = true
    catch(:abort) do
      result = result_lambda.call if result_lambda.is_a?(Proc)
      if Callbacks.halt_and_display_warning_on_return_false && result == false
        display_deprecation_warning_for_false_terminator
      else
        terminate = false
      end
    end
    terminate
  end
end

def display_deprecation_warning_for_false_terminator

def display_deprecation_warning_for_false_terminator
  ActiveSupport::Deprecation.warn(<<-MSG.squish)
    Returning `false` in Active Record and Active Model callbacks will not implicitly halt a callback chain in Rails 5.1.
    To explicitly halt the callback chain, please use `throw :abort` instead.
  MSG
end

def get_callbacks(name) # :nodoc:

:nodoc:
def get_callbacks(name) # :nodoc:
  send "_#{name}_callbacks"
end

def normalize_callback_params(filters, block) # :nodoc:

:nodoc:
def normalize_callback_params(filters, block) # :nodoc:
  type = CALLBACK_FILTER_TYPES.include?(filters.first) ? filters.shift : :before
  options = filters.extract_options!
  filters.unshift(block) if block
  [type, filters, options.dup]
end

def reset_callbacks(name)

Remove all set callbacks for the given event.
def reset_callbacks(name)
  callbacks = get_callbacks name
  ActiveSupport::DescendantsTracker.descendants(self).each do |target|
    chain = target.get_callbacks(name).dup
    callbacks.each { |c| chain.delete(c) }
    target.set_callbacks name, chain
  end
  self.set_callbacks name, callbacks.dup.clear
end

def set_callback(name, *filter_list, &block)

existing chain rather than appended.
* :prepend - If +true+, the callback will be prepended to the
be called only when they all return a false value.
strings, each naming an instance method or a proc; the callback will
* :unless - A symbol, a string or an array of symbols and
only when they all return a true value.
each naming an instance method or a proc; the callback will be called
* :if - A symbol, a string or an array of symbols and strings,

===== Options

wasn't halted, from the +yield+ call.
Around callbacks can access the return value from the event, if it

after callbacks are called in the reverse order.
Before and around callbacks are called in the order that they are set;

an argument.
of the current object. It can also optionally accept the current object as
If a proc, lambda, or block is given, its body is evaluated in the context

argument to +define_callbacks+.
object that responds to a certain method determined by the :scope
proc, lambda, or block; as a string to be instance evaluated(deprecated); or as an
The callback can be specified as a symbol naming an instance method; as a

set_callback :save, :before_method

means the first example above can also be written as:
+:after+, or +:around+ the event. If omitted, +:before+ is assumed. This
The second argument indicates whether the callback is to be run +:before+,

set_callback :save, :around, ->(r, block) { stuff; result = block.call; stuff }
set_callback :save, :after, :after_method, if: :condition
set_callback :save, :before, :before_method

Install a callback for the given event.
def set_callback(name, *filter_list, &block)
  type, filters, options = normalize_callback_params(filter_list, block)
  self_chain = get_callbacks name
  mapped = filters.map do |filter|
    Callback.build(self_chain, filter, type, options)
  end
  __update_callbacks(name) do |target, chain|
    options[:prepend] ? chain.prepend(*mapped) : chain.append(*mapped)
    target.set_callbacks name, chain
  end
end

def set_callbacks(name, callbacks) # :nodoc:

:nodoc:
def set_callbacks(name, callbacks) # :nodoc:
  send "_#{name}_callbacks=", callbacks
end

def skip_callback(name, *filter_list, &block)

already been set (unless the :raise option is set to false).
An ArgumentError will be raised if the callback has not

end
skip_callback :validate, :before, :check_membership, if: -> { self.age > 18 }
class Writer < Person

callback is skipped.
:unless options may be passed in order to control when the
Skip a previously set callback. Like +set_callback+, :if or
def skip_callback(name, *filter_list, &block)
  type, filters, options = normalize_callback_params(filter_list, block)
  options[:raise] = true unless options.key?(:raise)
  __update_callbacks(name) do |target, chain|
    filters.each do |filter|
      callback = chain.find {|c| c.matches?(type, filter) }
      if !callback && options[:raise]
        raise ArgumentError, "#{type.to_s.capitalize} #{name} callback #{filter.inspect} has not been defined"
      end
      if callback && (options.key?(:if) || options.key?(:unless))
        new_callback = callback.merge_conditional_options(chain, if_option: options[:if], unless_option: options[:unless])
        chain.insert(chain.index(callback), new_callback)
      end
      chain.delete(callback)
    end
    target.set_callbacks name, chain
  end
end