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
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
# 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
The list of events that this object can publish/subscribe.
# File lib/fidgit/event.rb, line 136 def events self.class.events end
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
@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
@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