module Fidgit::Event

Adds simple event handling methods to an object (subscribe/publish pattern).

@example

class JumpingBean
  include Event
  event :jump
end

bean = JumpingBean.new
bean.subscribe :jump do
  puts "Whee!"
end

bean.subscribe :jump do |object, direction, distance|
  puts "#{object.class.name} jumped #{distance} metres #{direction}"
end

bean.publish :jump, :up, 4
# Whee!
# JumpingBean jumped 4 metres up

Public Class Methods

included(base) click to toggle source

Add singleton methods to the class that includes Event.

# File lib/fidgit/event.rb, line 141
def self.included(base)
  class << base
    def events
      # Copy the events already set up for your parent.
      @events ||= if superclass.respond_to? :events
                    superclass.events.dup
                  else
                    []
                  end
    end

    def event(event)
      events.push event.to_sym unless events.include? event
      event
    end
  end
end
new_event_handlers() click to toggle source
# File lib/fidgit/event.rb, line 44
def new_event_handlers
  # Don't use Set, since it is not guaranteed to be ordered.
  Hash.new {|h, k| h[k] = [] }
end

Public Instance Methods

events() click to toggle source

The list of events that this object can publish/subscribe.

# File lib/fidgit/event.rb, line 136
def events
  self.class.events
end
publish(event, *args) click to toggle source

Publish an event to all previously added handlers in the order they were added. It will automatically call the publishing object with the method named after the event if it is defined (this will be done before the manually added handlers are called).

If any handler returns :handled, then no further handlers will be called.

@param [Symbol] event Name of the event to publish. @param [Array] args Arguments to pass to the event handlers. @return [Symbol, nil] :handled if any handler handled the event or nil if none did.

# File lib/fidgit/event.rb, line 116
def publish(event, *args)
  raise ArgumentError, "#{self.class} does not handle #{event.inspect}" unless events.include? event

  # Do nothing if the object is disabled.
  return if respond_to?(:enabled?) and not enabled?

  if respond_to? event
    return :handled if send(event, self, *args) == :handled
  end

  if defined? @_event_handlers
    @_event_handlers[event].reverse_each do |handler|
      return :handled if handler.call(self, *args) == :handled
    end
  end

  nil
end
subscribe(event, method = nil, &block) click to toggle source

@return [Subscription] Definition of this the handler created by this subscription, to be used with {#unsubscribe}

# File lib/fidgit/event.rb, line 51
def subscribe(event, method = nil, &block)
  raise ArgumentError, "Expected method or block for event handler" unless !block.nil? ^ !method.nil?
  raise ArgumentError, "#{self.class} does not handle #{event.inspect}" unless events.include? event

  @_event_handlers ||= Event.new_event_handlers
  handler = method || block
  @_event_handlers[event] << handler

  Subscription.new self, event, handler
end
unsubscribe(*args) click to toggle source

@overload unsubscribe(subscription)

Unsubscribe from a #{Subscription}, as returned from {#subscribe}
@param subscription [Subscription]
@return [Boolean] true if the handler was able to be deleted.

@overload unsubscribe(handler)

Unsubscribe from first event this handler has been used to subscribe to..
@param handler [Block, Method] Event handler used.
@return [Boolean] true if the handler was able to be deleted.

@overload unsubscribe(event, handler)

Unsubscribe from specific handler on particular event.
@param event [Symbol] Name of event originally subscribed to.
@param handler [Block, Method] Event handler used.
@return [Boolean] true if the handler was able to be deleted.
# File lib/fidgit/event.rb, line 78
def unsubscribe(*args)
  @_event_handlers ||= Event.new_event_handlers

  case args.size
    when 1
      case args.first
        when Subscription
          # Delete specific event handler.
          subscription = args.first
          raise ArgumentError, "Incorrect publisher for #{Subscription}: #{subscription.publisher}" unless subscription.publisher == self
          unsubscribe subscription.event, subscription.handler
        when Proc, Method
          # Delete first events that use the handler.
          handler = args.first
          !!@_event_handlers.find {|_, handlers| handlers.delete handler }
        else
          raise TypeError, "handler must be a #{Subscription}, Block or Method: #{args.first}"
      end
    when 2
      event, handler = args
      raise TypeError, "event name must be a Symbol: #{event}" unless event.is_a? Symbol
      raise TypeError, "handler name must be a Proc/Method: #{handler}" unless handler.is_a? Proc or handler.is_a? Method
      !!@_event_handlers[event].delete(handler)
    else
      raise ArgumentError, "Requires 1..2 arguments, but received #{args.size} arguments"
  end
end