class Deferred::Callbacks

A multi-purpose callbacks list object that provides a powerful way to manage callback lists.

Constants

DEFAULT_OPTIONS

Public Class Methods

new(options = {}) click to toggle source

Create a callback list. By default a callback list will act like an event callback list and can be “fired” multiple times.

@param options [Hash] @option options [Boolean] :once (false)

will ensure the callback list can only be fired once

@option options [Boolean] :memory (false)

will keep track of previous values and will call any callback added
after the list has been fired right away with the latest "memorized"
values

@option options [Boolean] :unique (false)

will ensure a callback can only be added once (no duplicate in the list)

@option options [Boolean] :stop_on_false (false)

interrupt callings when a callback returns false
# File lib/deferred/callbacks.rb, line 28
def initialize(options = {})
  @options = DEFAULT_OPTIONS.merge(options)
  # Last fire value (for non-forgettable lists)
  @memory = nil
  # Flag to know if list was already fired
  @fired = false
  # Flag to know if list is currently firing
  @firing = false
  # First callback to fire (used internally by add and fireWith)
  @firing_start = 0
  # End of the loop when firing
  @firing_length = 0
  # Index of currently firing callback (modified by remove if needed)
  @firing_index = 0
  # Actual callback list
  @list = []
  # Stack of fire calls for repeatable lists
  @stack = options[:once] ? nil : []
end

Public Instance Methods

add(*args, &block) click to toggle source

Add a callback or a collection of callbacks to a callback list

@overload add(*callbacks)

@param callbacks [Array<Proc, Method>] array of callbacks

@overload add

@yield [*arguments]

@return [Callbacks] self

# File lib/deferred/callbacks.rb, line 73
def add(*args, &block)
  return self unless @list
  # First, we save the current length
  start = @list.size
  args << block if block_given?
  args.flatten.each do |arg|
    case arg
    when Proc, Method
      @list << arg unless @options[:unique] && has?(arg)
    end
  end
  # Do we need to add the callbacks to the current firing batch?
  if @firing
    @firing_length = @list.size
    # With memory, if we're not firing then we should call right away
  elsif @memory
    @firing_start = start
    _fire(*@memory)
  end
  self
end
disable!() click to toggle source

Disable a callback list from doing anything more

@return [Callbacks] self

# File lib/deferred/callbacks.rb, line 137
def disable!
  @list = @stack = @memory = nil
  self
end
disabled?() click to toggle source

Determine if the callbacks list has been disabled

@return [Boolean]

# File lib/deferred/callbacks.rb, line 145
def disabled?
  @list.nil?
end
empty!() click to toggle source

Remove all of the callbacks from a list

@return [Callbacks] self

# File lib/deferred/callbacks.rb, line 128
def empty!
  @list = []
  @firing_length = 0
  self
end
fire(*args) click to toggle source

Call all of the callbacks with the given arguments

@overload fire(*arguments)

@param arguments [Array<Object>]
  the argument or list of arguments to pass back to the callback list

@return [Callbacks] self

# File lib/deferred/callbacks.rb, line 54
def fire(*args)
  return self unless @list && (!fired? || @stack)
  _fire(*args)
end
fired?() click to toggle source

Determine if the callbacks have already been called at least once

@return [Boolean]

# File lib/deferred/callbacks.rb, line 62
def fired?
  @fired
end
has?(arg = nil) click to toggle source

Determine whether a supplied callback is in a list

@overload has?(callback) @return [Boolean]

# File lib/deferred/callbacks.rb, line 121
def has?(arg = nil)
  !!(arg ? @list && @list.include?(arg) : @list && @list.any?)
end
lock!() click to toggle source

Lock a callback list in its current state

@return [Callbacks] self

# File lib/deferred/callbacks.rb, line 152
def lock!
  @stack = nil
  disable! unless @memory
  self
end
locked?() click to toggle source

Determine if the callbacks list has been locked

@return [Boolean]

# File lib/deferred/callbacks.rb, line 161
def locked?
  @stack.nil?
end
remove(*args, &block) click to toggle source

Remove a callback or a collection of callbacks from a callback list

@overload remove(*callbacks)

@param callbacks [Array<Proc, Method>] array of callbacks

@return [Callbacks] self

# File lib/deferred/callbacks.rb, line 100
def remove(*args, &block)
  return self unless @list
  args << block if block_given?
  args.flatten.each do |arg|
    case arg
    when Proc, Method
      while index = @list.index(arg)
        @list.delete_at(index)
        next unless @firing
        @firing_length -= 1 if index <= @firing_length
        @firing_index  -= 1 if index <= @firing_index
      end
    end
  end
  self
end

Private Instance Methods

_fire(*args) click to toggle source
# File lib/deferred/callbacks.rb, line 167
def _fire(*args)
  if @firing
    @stack.push(args)
  else
    @memory = @options[:memory] ? args : nil
    @fired = true
    @firing_index = @firing_start
    @firing_start = 0
    @firing_length = @list.size
    @firing = true
    while @list && @firing_index < @firing_length
      callback = @list[@firing_index]
      result = (
        case arity = callback.arity
        when -1
          callback.call(*args)
        else
          callback.call(*args.slice(0, arity))
        end
      )
      if @options[:stop_on_false] && result == false
        # To prevent further calls using add
        @memory = false
        break
      end
      @firing_index += 1
    end
    @firing = false
    if @list
      if @stack
        _fire(*@stack.shift) if @stack.any?
      elsif @memory
        @list = []
      else
        disable!
      end
    end
  end
  self
end