PubSubHub allows you to loosen the coupling between components in a system by providing a centralized registry of events and listeners that subscribe to those events.
For example, given a method #foo
, on class Bar
, with a slew of side-effects
(communication with multiple classes outside of Bar
), you effectively have
tightly coupled Bar
to a number of different classes in the system. Any change
to those other classes on which Bar
depends may break it. PubSubHub
provides
a pattern in which Bar
can be freed of its dependencies: the parts of the
system that care when #foo
happens can subscribe to an event, and Bar
need
not even know that those other parts exist.
You can learn more about the motivation behind PubSubHub in our blog post, "Managing side-effects with the Pub-Sub model".
Add this line to your application's Gemfile
:
gem 'pubsubhub'
And then execute:
$ bundle
Or install it yourself as:
$ gem install pubsubhub
PubSubHub
provides a mechanism to subscribe to events and notify objects of
events.
To set up event listeners, pass a hash of events and listeners as follows:
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
Note that PubSubHub
is usable in any Ruby application; we happen to use it in
a Rails application, and make the call to PubSubHub.register
in a file in the
config/initializers/
directory.
PubSubHub
requires Ruby 2.0 or above.
PubSubHub
is built by Causes.
- come work with us: http://www.causes.com/jobs
- read our Engineering blog: http://causes.github.io/