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) {|chain, type, filters, options| ... } ⇒ Object
This is used internally to append, prepend and skip callbacks to the CallbackChain.
-
#define_callbacks(*callbacks) ⇒ Object
Define 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.
422 423 424 425 426 427 428 429 |
# File 'lib/active_support/callbacks.rb', line 422 def __create_keyed_callback(name, kind, object, &blk) #:nodoc: @_keyed_callbacks ||= {} @_keyed_callbacks[name] ||= begin str = send("_#{kind}_callbacks").compile(name, object) class_eval "def #{name}() #{str} end", __FILE__, __LINE__ 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.
385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 |
# File 'lib/active_support/callbacks.rb', line 385 def __define_runner(symbol) #:nodoc: send("_update_#{symbol}_superclass_callbacks") body = send("_#{symbol}_callbacks").compile(nil) silence_warnings do undef_method "_run_#{symbol}_callbacks" if method_defined?("_run_#{symbol}_callbacks") class_eval " def _run_\#{symbol}_callbacks(key = nil, &blk)\n @_initialized_\#{symbol}_callbacks ||= begin\n if self.class.send(\"_update_\#{symbol}_superclass_callbacks\")\n self.class.__define_runner(\#{symbol.inspect})\n return _run_\#{symbol}_callbacks(key, &blk)\n end\n true\n end\n\n if key\n name = \"_run__\\\#{self.class.name.hash.abs}__\#{symbol}__\\\#{key.hash.abs}__callbacks\"\n\n unless respond_to?(name)\n self.class.__create_keyed_callback(name, :\#{symbol}, self, &blk)\n end\n\n send(name, &blk)\n else\n \#{body}\n end\n end\n private :_run_\#{symbol}_callbacks\n RUBY_EVAL\n end\nend\n", __FILE__, __LINE__ + 1 |
#__update_callbacks(name, filters = [], block = nil) {|chain, type, filters, options| ... } ⇒ Object
This is used internally to append, prepend and skip callbacks to the CallbackChain.
434 435 436 437 438 439 440 441 442 443 444 445 |
# File 'lib/active_support/callbacks.rb', line 434 def __update_callbacks(name, filters = [], block = nil) #:nodoc: send("_update_#{name}_superclass_callbacks") type = [:before, :after, :around].include?(filters.first) ? filters.shift : :before = filters.last.is_a?(Hash) ? filters.pop : {} filters.unshift(block) if block chain = send("_#{name}_callbacks") yield chain, type, filters, if block_given? __define_runner(name) end |
#define_callbacks(*callbacks) ⇒ Object
Define callbacks types.
Example
define_callbacks :validate
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”.
-
:rescuable- By default, after filters are not executed if
the given block or an before_filter raises an error. Supply :rescuable => true to change this behavior.
-
:scope- Show which methods should be executed when a class
is given as callback:
define_callbacks :filters, :scope => [ :kind ]
When a class is given:
before_filter MyFilter
It will call the type of the filter in the given class, which in this case, is “before”.
If, for instance, you supply the given scope:
define_callbacks :validate, :scope => [ :kind, :name ]
It will call “#kind_#name” in the given class. So “before_validate” will be called in the class below:
before_validate MyValidation
Defaults to :kind.
556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 |
# File 'lib/active_support/callbacks.rb', line 556 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 extlib_inheritable_reader("_removed_#{callback}_callbacks") do [] end class_eval " def self._\#{callback}_superclass_callbacks\n if superclass.respond_to?(:_\#{callback}_callbacks)\n superclass._\#{callback}_callbacks + superclass._\#{callback}_superclass_callbacks\n else\n []\n end\n end\n\n def self._update_\#{callback}_superclass_callbacks\n changed, index = false, 0\n\n callbacks = (_\#{callback}_superclass_callbacks -\n _\#{callback}_callbacks) - _removed_\#{callback}_callbacks\n\n callbacks.each do |callback|\n if new_index = _\#{callback}_callbacks.index(callback)\n index = new_index + 1\n else\n changed = true\n _\#{callback}_callbacks.insert(index, callback)\n index = index + 1\n end\n end\n changed\n end\n METHOD\n\n __define_runner(callback)\n end\nend\n", __FILE__, __LINE__ + 1 |
#reset_callbacks(symbol) ⇒ Object
Reset callbacks for a given type.
506 507 508 509 510 511 |
# File 'lib/active_support/callbacks.rb', line 506 def reset_callbacks(symbol) callbacks = send("_#{symbol}_callbacks") callbacks.clear send("_removed_#{symbol}_callbacks").concat(callbacks) __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 ActionPack, 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.
473 474 475 476 477 478 479 480 481 482 483 |
# File 'lib/active_support/callbacks.rb', line 473 def set_callback(name, *filter_list, &block) __update_callbacks(name, filter_list, block) do |chain, type, filters, | filters.map! do |filter| removed = chain.delete_if {|c| c.matches?(type, filter) } send("_removed_#{name}_callbacks").push(*removed) Callback.new(chain, filter, type, .dup, self) end [:prepend] ? chain.unshift(*filters) : chain.push(*filters) end end |
#skip_callback(name, *filter_list, &block) ⇒ Object
Skip a previously defined callback for a given type.
487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 |
# File 'lib/active_support/callbacks.rb', line 487 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) send("_removed_#{name}_callbacks") << filter end end end |