Module: ActiveSupport::Callbacks::ClassMethods
- Defined in:
- lib/active_support/callbacks.rb
Instance Method Summary collapse
-
#__create_keyed_callback(name, kind, object, &blk) ⇒ Object
This is called the first time a callback is called with a particular key.
-
#__define_runner(symbol) ⇒ Object
Make the run_callbacks :save method.
-
#__update_callbacks(name, filters = [], block = nil) ⇒ Object
This is used internally to append, prepend and skip callbacks to the CallbackChain.
-
#define_callbacks(*callbacks) ⇒ Object
Defines callbacks types:.
-
#reset_callbacks(symbol) ⇒ Object
Reset callbacks for a given type.
-
#set_callback(name, *filter_list, &block) ⇒ Object
Set callbacks for a previously defined callback.
-
#skip_callback(name, *filter_list, &block) ⇒ Object
Skip a previously defined callback for a given type.
Instance Method Details
#__create_keyed_callback(name, kind, object, &blk) ⇒ Object
This is called the first time a callback is called with a particular key. It creates a new callback method for the key, calculating which callbacks can be omitted because of per_key conditions.
424 425 426 427 428 429 430 431 432 433 434 |
# File 'lib/active_support/callbacks.rb', line 424 def __create_keyed_callback(name, kind, object, &blk) #:nodoc: @_keyed_callbacks ||= {} @_keyed_callbacks[name] ||= begin str = send("_#{kind}_callbacks").compile(name, object) class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 def #{name}() #{str} end protected :#{name} RUBY_EVAL true end end |
#__define_runner(symbol) ⇒ Object
Make the run_callbacks :save method. The generated method takes a block that it’ll yield to. It’ll call the before and around filters in order, yield the block, and then run the after filters.
run_callbacks :save do
save
end
The run_callbacks :save method can optionally take a key, which will be used to compile an optimized callback method for each key. See #define_callbacks for more information.
396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 |
# File 'lib/active_support/callbacks.rb', line 396 def __define_runner(symbol) #:nodoc: body = send("_#{symbol}_callbacks").compile(nil) silence_warnings do undef_method "_run_#{symbol}_callbacks" if method_defined?("_run_#{symbol}_callbacks") class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 def _run_#{symbol}_callbacks(key = nil, &blk) if key name = "_run__\#{self.class.name.hash.abs}__#{symbol}__\#{key.hash.abs}__callbacks" unless respond_to?(name) self.class.__create_keyed_callback(name, :#{symbol}, self, &blk) end send(name, &blk) else #{body} end end private :_run_#{symbol}_callbacks RUBY_EVAL end end |
#__update_callbacks(name, filters = [], block = nil) ⇒ Object
This is used internally to append, prepend and skip callbacks to the CallbackChain.
439 440 441 442 443 444 445 446 447 448 449 |
# File 'lib/active_support/callbacks.rb', line 439 def __update_callbacks(name, filters = [], block = nil) #:nodoc: type = [:before, :after, :around].include?(filters.first) ? filters.shift : :before = filters.last.is_a?(Hash) ? filters.pop : {} filters.unshift(block) if block ([self] + ActiveSupport::DescendantsTracker.descendants(self)).each do |target| chain = target.send("_#{name}_callbacks") yield chain, type, filters, target.__define_runner(name) end end |
#define_callbacks(*callbacks) ⇒ Object
Defines callbacks types:
define_callbacks :validate
This macro accepts the following options:
-
:terminator
- Indicates when a before filter is considered
to be halted.
define_callbacks :validate, :terminator => "result == false"
In the example above, if any before validate callbacks returns false
, other callbacks are not executed. Defaults to “false”, meaning no value halts the chain.
-
:rescuable
- By default, after filters are not executed if
the given block or a before filter raises an error. Set this option to true to change this behavior.
-
:scope
- Indicates which methods should be executed when a class
is given as callback. Defaults to [:kind]
.
class Audit
def before(caller)
puts 'Audit: before is called'
end
def before_save(caller)
puts 'Audit: before_save is called'
end
end
class Account
include ActiveSupport::Callbacks
define_callbacks :save
set_callback :save, :before, Audit.new
def save
run_callbacks :save do
puts 'save in main'
end
end
end
In the above case whenever you save an account the method Audit#before
will be called. On the other hand
define_callbacks :save, :scope => [:kind, :name]
would trigger Audit#before_save
instead. That’s constructed by calling "#{kind}_#{name}"
on the given instance. In this case “kind” is “before” and “name” is “save”. In this context “:kind” and “:name” have special meanings: “:kind” refers to the kind of callback (before/after/around) and “:name” refers to the method on which callbacks are being defined.
A declaration like
define_callbacks :save, :scope => [:name]
would call Audit#save
.
590 591 592 593 594 595 596 597 598 |
# File 'lib/active_support/callbacks.rb', line 590 def define_callbacks(*callbacks) config = callbacks.last.is_a?(Hash) ? callbacks.pop : {} callbacks.each do |callback| extlib_inheritable_reader("_#{callback}_callbacks") do CallbackChain.new(callback, config) end __define_runner(callback) end end |
#reset_callbacks(symbol) ⇒ Object
Reset callbacks for a given type.
515 516 517 518 519 520 521 522 523 524 525 526 |
# File 'lib/active_support/callbacks.rb', line 515 def reset_callbacks(symbol) callbacks = send("_#{symbol}_callbacks") ActiveSupport::DescendantsTracker.descendants(self).each do |target| chain = target.send("_#{symbol}_callbacks") callbacks.each { |c| chain.delete(c) } target.__define_runner(symbol) end callbacks.clear __define_runner(symbol) end |
#set_callback(name, *filter_list, &block) ⇒ Object
Set callbacks for a previously defined callback.
Syntax:
set_callback :save, :before, :before_meth
set_callback :save, :after, :after_meth, :if => :condition
set_callback :save, :around, lambda { |r| stuff; yield; stuff }
Use skip_callback to skip any defined one.
When creating or skipping callbacks, you can specify conditions that are always the same for a given key. For instance, in Action Pack, we convert :only and :except conditions into per-key conditions.
before_filter :authenticate, :except => "index"
becomes
dispatch_callback :before, :authenticate, :per_key => {:unless => proc {|c| c.action_name == "index"}}
Per-Key conditions are evaluated only once per use of a given key. In the case of the above example, you would do:
run_callbacks(:dispatch, action_name) { ... dispatch stuff ... }
In that case, each action_name would get its own compiled callback method that took into consideration the per_key conditions. This is a speed improvement for ActionPack.
479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 |
# File 'lib/active_support/callbacks.rb', line 479 def set_callback(name, *filter_list, &block) mapped = nil __update_callbacks(name, filter_list, block) do |chain, type, filters, | mapped ||= filters.map do |filter| Callback.new(chain, filter, type, .dup, self) end filters.each do |filter| chain.delete_if {|c| c.matches?(type, filter) } end [:prepend] ? chain.unshift(*(mapped.reverse)) : chain.push(*mapped) end end |
#skip_callback(name, *filter_list, &block) ⇒ Object
Skip a previously defined callback for a given type.
497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 |
# File 'lib/active_support/callbacks.rb', line 497 def skip_callback(name, *filter_list, &block) __update_callbacks(name, filter_list, block) do |chain, type, filters, | filters.each do |filter| filter = chain.find {|c| c.matches?(type, filter) } if filter && .any? new_filter = filter.clone(chain, self) chain.insert(chain.index(filter), new_filter) new_filter.recompile!(, [:per_key] || {}) end chain.delete(filter) end end end |