class Octiron::Events::Bus
Implements and in-process pub-sub events broadcaster allowing multiple observers to subscribe to different events.
Constants
- DEFAULT_CLASS
The default handler class
Attributes
@return (String) the default namespace to search for events
Public Class Methods
@param default_namespace
(Symbol) The default namespace to look in for
Event classes.
# File lib/octiron/events/bus.rb, line 30 def initialize(default_namespace = ::Octiron::Events) @default_namespace = default_namespace.to_s clear end
Public Instance Methods
Clears all event handlers
# File lib/octiron/events/bus.rb, line 37 def clear @handlers = {} end
Broadcast an event. This is an instance of a class provided to subscribe
previously. @param event (Object) the event to publish
# File lib/octiron/events/bus.rb, line 90 def publish(event) event_name = event if not event.is_a?(Hash) event_name = event.class.to_s end @handlers.keys.sort.each do |handler_class| handlers_for(event_name, handler_class, false).each do |handler| handler.call(event) end end end
Subscribe an event handler to an event. @param event_id (Class, String, other) A class or String naming an event
class.
@param handler_object (Object) Handler object that must implement a
`#call` method accepting an instance of the event class provided in the first parameter. If nil, a block needs to be provided.
@param handler_class (Object) An object describing the handler class; the
default is DEFAULT_CLASS. Handlers are executed sorted by handler class, so the class provided here must be sortable.
@param handler_proc (Proc) Handler block that accepts an instance of the
event class provided in the first parameter. If nil, a handler object must be provided.
@return The class represented by the event_id, as a String name.
# File lib/octiron/events/bus.rb, line 55 def subscribe(event_id, handler_object = nil, handler_class = DEFAULT_CLASS, &handler_proc) return with_handlers(event_id, handler_class, handler_object, handler_proc) do |hlist, h| hlist << h end end
Unsubscribe an event handler from an event. @param event_id (Class, String, other) A class or String naming an event
class.
@param handler_object (Object) Handler object that must implement a
`#call` method accepting an instance of the event class provided in the first parameter. If nil, a block needs to be provided.
@param handler_class (Object) An object describing the handler class; the
default is DEFAULT_CLASS. Handlers are executed sorted by handler class, so the class provided here must be sortable.
@param handler_proc (Proc) Handler block that accepts an instance of the
event class provided in the first parameter. If nil, a handler object must be provided.
@return The class represented by the event_id, as a String name.
# File lib/octiron/events/bus.rb, line 78 def unsubscribe(event_id, handler_object = nil, handler_class = DEFAULT_CLASS, &handler_proc) return with_handlers(event_id, handler_class, handler_object, handler_proc) do |hlist, h| hlist.delete(h) end end
Private Instance Methods
The first parameters is the event class or event name. The second parameter is whether this is for write or read access. We don't want to pollute @handlers with data from the publish
call.
# File lib/octiron/events/bus.rb, line 109 def handlers_for(name, handler_class, write) # Defaults for the class @handlers[handler_class] ||= {} # Use prototype matching for Hash names if name.is_a?(Hash) name.extend(::Collapsium::PrototypeMatch) # The prototype hash logic is a little complex. A hash event can match # multiple prototypes, e.g. { a: 42 } should match { a: nil } as well as # { a: 42 }. # When writing, we want to be as precise as possible and find the best # match. When reading, we want to merge all matches, ideally. if write best_score = -1 best_proto = nil # Find the best matching prototype @handlers[handler_class].keys.each do |proto| score = name.prototype_match_score(proto) if score > best_score best_score = score best_proto = proto end end if not best_proto.nil? return @handlers[handler_class][best_proto] end else merged = [] @handlers[handler_class].keys.each do |proto| if name.prototype_match(proto) merged += @handlers[handler_class][proto] end end if not merged.empty? return merged end end # No prototype matches. That means if write is true, we need to treat # name as a new prototype to regiser. Otherwise, we need to return an # empty list. That happens to be the same logic as for the simple key # below. end # If we're in write access, make sure to store an empty list as well as # returning one (if necessary). if write @handlers[handler_class][name] ||= [] end # In read access, want to either return an empty list, or the registered # handlers, but not ovewrite the registered handlers. return @handlers[handler_class][name] || [] end
# File lib/octiron/events/bus.rb, line 178 def resolve_handler(handler_object = nil, &handler_proc) handler = handler_proc || handler_object if not handler raise ArgumentError, "Please pass either an object or a handler block" end return handler end
# File lib/octiron/events/bus.rb, line 169 def with_handlers(event_id, handler_class, handler_object, handler_proc) handler = resolve_handler(handler_object, &handler_proc) event_name = identify(event_id) yield handlers_for(event_name, handler_class, true), handler return event_name end