Module: CouchRest::Callbacks::ClassMethods

Defined in:
lib/couchrest/mixins/callbacks.rb

Constant Summary collapse

CHAINS =
{:before => :before, :around => :before, :after => :after}

Instance Method Summary collapse

Instance Method Details

#_create_and_run_keyed_callback(klass, kind, key, obj, &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.



359
360
361
362
363
364
365
366
367
368
369
370
371
# File 'lib/couchrest/mixins/callbacks.rb', line 359

def _create_and_run_keyed_callback(klass, kind, key, obj, &blk)
  @_keyed_callbacks ||= {}
  @_keyed_callbacks[[kind, key]] ||= begin
    str = self.send("_#{kind}_callbacks").compile(key, obj)
    self.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
      def _run_#{klass}_#{kind}_#{key}_callbacks
        #{str}
      end
    RUBY_EVAL
    true
  end
  obj.send("_run_#{klass}_#{kind}_#{key}_callbacks", &blk)
end

#_define_runner(symbol, str, options) ⇒ Object

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.



341
342
343
344
345
346
347
348
349
350
351
352
353
354
# File 'lib/couchrest/mixins/callbacks.rb', line 341

def _define_runner(symbol, str, options)
  self.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
    def _run_#{symbol}_callbacks(key = nil)
      if key
        send("_run_\#{self.class}_#{symbol}_\#{key}_callbacks") { yield }
      else
        #{str}
      end
    end
  RUBY_EVAL
  
  before_name, around_name, after_name = 
    options.values_at(:before, :after, :around)
end

#define_callbacks(*symbols) ⇒ Object

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.



404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
# File 'lib/couchrest/mixins/callbacks.rb', line 404

def define_callbacks(*symbols)
  symbols.each do |symbol|
    self.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
      class_inheritable_accessor :_#{symbol}_callbacks
      self._#{symbol}_callbacks = CallbackChain.new

      def self.#{symbol}_callback(type, *filters, &blk)
        options = filters.last.is_a?(Hash) ? filters.pop : {}
        filters.unshift(blk) if block_given?
        filters.map! {|f| Callback.new(f, type, options.dup, self, :#{symbol})}
        self._#{symbol}_callbacks.push(*filters)
        _define_runner(:#{symbol}, self._#{symbol}_callbacks.compile, options)
      end
      
      def self.skip_#{symbol}_callback(type, *filters, &blk)
        options = filters.last.is_a?(Hash) ? filters.pop : {}
        filters.unshift(blk) if block_given?
        filters.each do |filter|
          self._#{symbol}_callbacks = self._#{symbol}_callbacks.clone(self)
          
          filter = self._#{symbol}_callbacks.find {|c| c.matches?(type, :#{symbol}, filter) }
          per_key = options[:per_key] || {}
          if filter
            filter.recompile!(options, per_key)
          else
            self._#{symbol}_callbacks.delete(filter)
          end
          _define_runner(:#{symbol}, self._#{symbol}_callbacks.compile, options)
        end
        
      end
      
      self.#{symbol}_callback(:before)
    RUBY_EVAL
  end
end