Module: ActiveSupport::Callbacks
Overview
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.
Mixing in this module allows you to define the events in the object’s life cycle that will support callbacks (via ClassMethods#define_callbacks), set the instance methods, procs, or callback objects to be called (via ClassMethods#set_callback), and run the installed callbacks at the appropriate times (via #run_callbacks).
By default callbacks are halted by throwing :abort
. See ClassMethods#define_callbacks for details.
Three kinds of callbacks are supported: before callbacks, run before a certain event; after callbacks, run after the event; and around callbacks, blocks that surround the event, triggering it when they yield. Callback
code can be contained in instance methods, procs or lambdas, or callback objects that respond to certain predetermined methods. See ClassMethods#set_callback for details.
class Record
include ActiveSupport::Callbacks
define_callbacks :save
def save
run_callbacks :save do
puts "- save"
end
end
end
class PersonRecord < Record
set_callback :save, :before, :
def
puts "saving..."
end
set_callback :save, :after do |object|
puts "saved"
end
end
person = PersonRecord.new
person.save
Output:
saving...
- save
saved
Constant Summary
-
CALLBACK_FILTER_TYPES =
# File 'activesupport/lib/active_support/callbacks.rb', line 72[:before, :after, :around].freeze
Class Method Summary
Concern
- Extended
class_methods | Define class methods from given block. |
included | Evaluate given block in context of base class, so that you can write class macros here. |
prepended | Evaluate given block in context of base class, so that you can write class macros here. |
append_features, prepend_features |
Instance Method Summary
-
#run_callbacks(kind, type = nil)
Runs the callbacks for the given event.
-
#halted_callback_hook(filter, name)
private
A hook invoked every time a before callback is halted.
DSL Calls
included
[ GitHub ]67 68 69 70
# File 'activesupport/lib/active_support/callbacks.rb', line 67
included do extend ActiveSupport::DescendantsTracker class_attribute :__callbacks, instance_writer: false, instance_predicate: false, default: {} end
Instance Method Details
#halted_callback_hook(filter, name) (private)
A hook invoked every time a before callback is halted. This can be overridden in Callbacks
implementors in order to provide better debugging/logging.
# File 'activesupport/lib/active_support/callbacks.rb', line 149
def halted_callback_hook(filter, name) end
#run_callbacks(kind, type = nil)
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 order.
If the callback chain was halted, returns false
. Otherwise returns the result of the block, nil
if no callbacks have been set, or true
if callbacks have been set but no block is given.
run_callbacks :save do
save
end
# File 'activesupport/lib/active_support/callbacks.rb', line 96
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(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) env.value else invoke_sequence = Proc.new do skipped = nil while true current = next_sequence current.invoke_before(env) if current.final? env.value = !env.halted && (!block_given? || yield) elsif current.skip?(env) (skipped ||= []) << current next_sequence = next_sequence.nested next else next_sequence = next_sequence.nested begin target, block, method, *arguments = current. (env, invoke_sequence) target.send(method, *arguments, &block) ensure next_sequence = current end end current.invoke_after(env) skipped.pop.invoke_after(env) while skipped&.first break env.value end end invoke_sequence.call end end end