Callbacks are code hooks that are run at key points in an object’s lifecycle. 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 lifecycle 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).
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, :saving_message def saving_message puts "saving..." end set_callback :save, :after do |object| puts "saved" end end person = PersonRecord.new person.save
Output:
saving... * save saved
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. Optionally accepts a key, which will be used to compile an optimized callback method for each key. See ClassMethods.define_callbacks for more information.
If the callback chain was halted, returns false. Otherwise returns the result of the block, or true if no block is given.
run_callbacks :save do save end
# File lib/active_support/callbacks.rb, line 80 80: def run_callbacks(kind, *args, &block) 81: send("_run_#{kind}_callbacks", *args, &block) 82: end
Filters support:
Arrays:: Used in conditions. This is used to specify multiple conditions. Used internally to merge conditions from skip_* filters Symbols:: A method to call Strings:: Some content to evaluate Procs:: A proc to call with the object Objects:: An object with a before_foo method on it to call
All of these objects are compiled into methods and handled the same after this point:
Arrays:: Merged together into a single filter Symbols:: Already methods Strings:: class_eval'ed into methods Procs:: define_method'ed into methods Objects:: a method is created that calls the before_foo method on the object.
# File lib/active_support/callbacks.rb, line 287 287: def _compile_filter(filter) 288: method_name = "_callback_#{@kind}_#{next_id}" 289: case filter 290: when Array 291: filter.map {|f| _compile_filter(f)} 292: when Symbol 293: filter 294: when String 295: "(#{filter})" 296: when Proc 297: @klass.send(:define_method, method_name, &filter) 298: return method_name if filter.arity <= 0 299: 300: method_name << (filter.arity == 1 ? "(self)" : " self, Proc.new ") 301: else 302: @klass.send(:define_method, "#{method_name}_object") { filter } 303: 304: _normalize_legacy_filter(kind, filter) 305: scopes = Array.wrap(chain.config[:scope]) 306: method_to_call = scopes.map{ |s| s.is_a?(Symbol) ? send(s) : s }.join("_") 307: 308: @klass.class_eval def #{method_name}(&blk) #{method_name}_object.send(:#{method_to_call}, self, &blk) end, __FILE__, __LINE__ + 1 309: 310: method_name 311: end 312: end
Options support the same options as filters themselves (and support symbols, string, procs, and objects), so compile a conditional expression based on the options
# File lib/active_support/callbacks.rb, line 252 252: def _compile_options(options) 253: conditions = ["true"] 254: 255: unless options[:if].empty? 256: conditions << Array.wrap(_compile_filter(options[:if])) 257: end 258: 259: unless options[:unless].empty? 260: conditions << Array.wrap(_compile_filter(options[:unless])).map {|f| "!#{f}"} 261: end 262: 263: conditions.flatten.join(" && ") 264: end
# File lib/active_support/callbacks.rb, line 318 318: def _normalize_legacy_filter(kind, filter) 319: if !filter.respond_to?(kind) && filter.respond_to?(:filter) 320: filter.singleton_class.class_eval def #{kind}(context, &block) filter(context, &block) end, __FILE__, __LINE__ + 1 321: elsif filter.respond_to?(:before) && filter.respond_to?(:after) && kind == :around 322: def filter.around(context) 323: should_continue = before(context) 324: yield if should_continue 325: after(context) 326: end 327: end 328: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.