Class: PubSubHub

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Includes:
Singleton
Defined in:
lib/pubsubhub.rb

Overview

The ‘PubSubHub` provides a mechanism to subscribe to events and notify objects of events.

To subscribe to an event, add a hash to pub_sub_hub. The listener must implement ‘handle_#event_name`.

# {file:config/initializers/pub_sub_hub.rb}

PubSubHub.register(
  took_action: [
    { listener: Mailer, async: true },
  ],
)

To trigger an event, call ‘PubSubHub.trigger`. All the arguments are forwarded to the `listener`.

class Action
  def take_action(person)
    # ...
    PubSubHub.trigger :took_action, self, person
  end
end

class Mailer
  def self.handle_took_action(action, person)
    # send `action.creator` an email
  end
end

By default, exceptions raised during event propagation are handled by printing them to standard error. You can set a custom handler by passing in a callable object to ‘PubSubHub.error_handler=`. We use this at Causes to integrate with our `Oops` plug-in, without creating a hard dependency on it:

PubSubHub.error_handler = ->(exception) { Oops.log(exception) }

Likewise, dispatch of ‘async: true` events is handled by a callable passed in to `PubSubHub.async_dispatcher=`. The default implementation just calls `Object#send` (ie. it is not actually asynchronous). At Causes, we’ve supplied a custom dispatcher that relies on the async_observer plug-in:

PubSubHub.async_dispatcher = ->(listener, handler, args) do
  listener.async_send(handler, *args)
end

Constant Summary collapse

VERSION =
'0.0.3'

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializePubSubHub

Returns a new instance of PubSubHub.



72
73
74
75
76
77
78
# File 'lib/pubsubhub.rb', line 72

def initialize
  @async_dispatcher = ->(listener, handler, args) do
    listener.send(handler, *args)
  end

  @error_handler = ->(exception) { STDERR.puts exception }
end

Instance Attribute Details

#async_dispatcherObject

Returns the value of attribute async_dispatcher.



69
70
71
# File 'lib/pubsubhub.rb', line 69

def async_dispatcher
  @async_dispatcher
end

#error_handlerObject

Returns the value of attribute error_handler.



69
70
71
# File 'lib/pubsubhub.rb', line 69

def error_handler
  @error_handler
end

#registryObject (readonly)

Returns the value of attribute registry.



70
71
72
# File 'lib/pubsubhub.rb', line 70

def registry
  @registry
end

Instance Method Details

#register(registry) ⇒ Object



80
81
82
# File 'lib/pubsubhub.rb', line 80

def register(registry)
  @registry = validate_registry!(registry)
end

#trigger(event_name, *args) ⇒ Object

Notifies listeners of a event.

Arguments are forwarded to the handlers. If the listener registered with ‘async: true`, `trigger` calls the handler using an asynchronous dispatcher.

The default dispatcher just forwards the message directly. For an example of a dispatcher that is actually asynchronous, see pub_sub_hub, which sets up:

PubSubHub.async_dispatcher = ->(listener, handler, args) do
  listener.async_send(handler, *args)
end


97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/pubsubhub.rb', line 97

def trigger(event_name, *args)
  @registry.fetch(event_name.to_sym, []).each do |registration|
    begin
      listener = Object.const_get(registration[:listener])
      async    = registration[:async]
      handler  = :"handle_#{event_name}"

      if async
        @async_dispatcher.call(listener, handler, args)
      else
        listener.send(handler, *args)
      end
    rescue => e
      @error_handler.call(e)
    end
  end
end