# File lib/couchrest/mixins/callbacks.rb, line 524 def _alias_callbacks(callbacks, block) options = callbacks.last.is_a?(Hash) ? callbacks.pop : {} callbacks.push(block) if block callbacks.each do |callback| yield callback, options end end
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.
# File lib/couchrest/mixins/callbacks.rb, line 415 def _create_keyed_callback(name, kind, obj, &blk) @_keyed_callbacks ||= {} @_keyed_callbacks[name] ||= begin str = send("_#{kind}_callback"). compile(name, :object => obj, :terminator => send("_#{kind}_terminator")) class_eval "def #{name}() #{str} end", __FILE__, __LINE__ true end end
Make the _run_save_callbacks 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_save_callbacks do
save
end
The _run_save_callbacks 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.
# File lib/couchrest/mixins/callbacks.rb, line 388 def _define_runner(symbol) body = send("_#{symbol}_callback"). compile(nil, :terminator => send("_#{symbol}_terminator")) body, line = " 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 ", __LINE__ + 1 undef_method "_run_#{symbol}_callbacks" if method_defined?("_run_#{symbol}_callbacks") class_eval body, __FILE__, line end
Define callbacks.
Creates a <name>_callback method that you can use to add callbacks.
Syntax:
save_callback :before, :before_meth save_callback :after, :after_meth, :if => :condition save_callback :around {|r| stuff; yield; stuff }
The <name>_callback method also updates the run<name>_callbacks method, which is the public API to run the callbacks.
Also creates a skip_<name>_callback method that you can use to skip callbacks.
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_dispatch_callbacks(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.
# File lib/couchrest/mixins/callbacks.rb, line 458 def _update_callbacks(name, filters = CallbackChain.new(name), block = nil) type = [:before, :after, :around].include?(filters.first) ? filters.shift : :before options = filters.last.is_a?(Hash) ? filters.pop : {} filters.unshift(block) if block callbacks = send("_#{name}_callback") yield callbacks, type, filters, options if block_given? _define_runner(name) end
# File lib/couchrest/mixins/callbacks.rb, line 499 def define_callbacks(*symbols) terminator = symbols.pop if symbols.last.is_a?(String) symbols.each do |symbol| couchrest_inheritable_accessor("_#{symbol}_terminator") { terminator } couchrest_inheritable_accessor("_#{symbol}_callback") do CallbackChain.new(symbol) end _define_runner(symbol) # Define more convenient callback methods # set_callback(:save, :before) becomes before_save [:before, :after, :around].each do |filter| self.class_eval " def self.#{filter}_#{symbol}(*symbols, &blk) _alias_callbacks(symbols, blk) do |callback, options| set_callback(:#{symbol}, :#{filter}, callback, options) end end ", __FILE__, __LINE__ + 1 end end end
# File lib/couchrest/mixins/callbacks.rb, line 471 def set_callback(name, *filters, &block) _update_callbacks(name, filters, block) do |callbacks, type, filters, options| filters.map! do |filter| # overrides parent class callbacks.delete_if {|c| c.matches?(type, filter) } Callback.new(filter, type, options.dup, self) end options[:prepend] ? callbacks.unshift(*filters) : callbacks.push(*filters) end end
# File lib/couchrest/mixins/callbacks.rb, line 483 def skip_callback(name, *filters, &block) _update_callbacks(name, filters, block) do |callbacks, type, filters, options| filters.each do |filter| callbacks = send("_#{name}_callback=", callbacks.clone(self)) filter = callbacks.find {|c| c.matches?(type, filter) } if filter && options.any? filter.recompile!(options, options[:per_key] || {}) else callbacks.delete(filter) end end end end