Module: Fidgit::Event

Included in:
Element
Defined in:
lib/fidgit/event.rb

Overview

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

Examples:

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

Defined Under Namespace

Classes: Subscription

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(base) ⇒ Object

Add singleton methods to the class that includes Event.



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# 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_handlersObject



44
45
46
47
# 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

Instance Method Details

#eventsObject

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



136
137
138
# File 'lib/fidgit/event.rb', line 136

def events
  self.class.events
end

#publish(event, *args) ⇒ Symbol?

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.

Parameters:

  • event (Symbol)

    Name of the event to publish.

  • args (Array)

    Arguments to pass to the event handlers.

Returns:

  • (Symbol, nil)

    :handled if any handler handled the event or nil if none did.

Raises:

  • (ArgumentError)


116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# 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) ⇒ Subscription

Returns Definition of this the handler created by this subscription, to be used with #unsubscribe.

Returns:

Raises:

  • (ArgumentError)


51
52
53
54
55
56
57
58
59
60
# 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(subscription) ⇒ Boolean #unsubscribe(handler) ⇒ Boolean #unsubscribe(event, handler) ⇒ Boolean

Overloads:

  • #unsubscribe(subscription) ⇒ Boolean

    Unsubscribe from a #Subscription, as returned from #subscribe

    Parameters:

    Returns:

    • (Boolean)

      true if the handler was able to be deleted.

  • #unsubscribe(handler) ⇒ Boolean

    Unsubscribe from first event this handler has been used to subscribe to..

    Parameters:

    • handler (Block, Method)

      Event handler used.

    Returns:

    • (Boolean)

      true if the handler was able to be deleted.

  • #unsubscribe(event, handler) ⇒ Boolean

    Unsubscribe from specific handler on particular event.

    Parameters:

    • event (Symbol)

      Name of event originally subscribed to.

    • handler (Block, Method)

      Event handler used.

    Returns:

    • (Boolean)

      true if the handler was able to be deleted.



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# 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