Module: CouchRest::Mixins::Callbacks::ClassMethods

Extended by:
InheritableAttributes
Defined in:
lib/couchrest/mixins/callbacks.rb

Instance Method Summary collapse

Instance Method Details

#_alias_callbacks(callbacks, block) ⇒ Object



524
525
526
527
528
529
530
# 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

#_create_keyed_callback(name, kind, 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.



415
416
417
418
419
420
421
422
423
424
425
# 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

#_define_runner(symbol) ⇒ 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.



388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
# File 'lib/couchrest/mixins/callbacks.rb', line 388

def _define_runner(symbol)
  body = send("_#{symbol}_callback").
    compile(nil, :terminator => send("_#{symbol}_terminator"))

  body, line = <<-RUBY_EVAL, __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
  RUBY_EVAL

  undef_method "_run_#{symbol}_callbacks" if method_defined?("_run_#{symbol}_callbacks")
  class_eval body, __FILE__, line
end

#_update_callbacks(name, filters = CallbackChain.new(name), block = nil) {|callbacks, type, filters, options| ... } ⇒ Object Also known as: _reset_callbacks

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.

Yields:

  • (callbacks, type, filters, options)


458
459
460
461
462
463
464
465
466
467
# 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

#define_callbacks(*symbols) ⇒ Object



499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
# 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 <<-RUBY_EVAL, __FILE__, __LINE__ + 1
        def self.#{filter}_#{symbol}(*symbols, &blk)
          _alias_callbacks(symbols, blk) do |callback, options|
            set_callback(:#{symbol}, :#{filter}, callback, options)
          end
        end
      RUBY_EVAL
    end
  end
end

#set_callback(name, *filters, &block) ⇒ Object



471
472
473
474
475
476
477
478
479
480
481
# 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

#skip_callback(name, *filters, &block) ⇒ Object



483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
# 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