causes/pubsubhub

View on GitHub
README.md

Summary

Maintainability
Test Coverage
# PubSubHub

[![Build Status](https://travis-ci.org/causes/pubsubhub.png)](https://travis-ci.org/causes/pubsubhub)
[![Code Climate](https://codeclimate.com/github/causes/pubsubhub.png)](https://codeclimate.com/github/causes/pubsubhub)
[![Coverage Status](https://coveralls.io/repos/causes/pubsubhub/badge.png?branch=master)](https://coveralls.io/r/causes/pubsubhub)

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](http://causes.github.io/blog/2013/08/08/managing-side-effects-with-the-publish-subscribe-model/)".

## Installation

Add this line to your application's `Gemfile`:

    gem 'pubsubhub'

And then execute:

```bash
$ bundle
```

Or install it yourself as:

```bash
$ gem install pubsubhub
```

## Usage

`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:

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

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

```ruby
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:

```ruby
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:

```ruby
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.

## Requirements

`PubSubHub` requires Ruby 2.0 or above.

## Credits

`PubSubHub` is built by Causes.

- come work with us: http://www.causes.com/jobs
- read our Engineering blog: http://causes.github.io/