Informator
The wisper-inspired tiny implementation of publish/subscribe design pattern.
The implementation differs from the original wisper's approach in the following aspects:
Unlike
Wisper::Publisherthat calls listener methods depending on the event, theInformatoruses the same listener's callback for sending all events. Instead it wraps attributes toInformator::Eventcontainer and uses its as the argument for the callback.The
Informatorhas two separate methods -#rememberand#publish. The first one is used to build and collect events, while the second one publishes all unpublished events at once (and clears the list of events to be published).The
Informatoralso defines#publish!method that is the same as#publishexcept for it throws the:published, that should be catched somewhere. This is just a shortcut for exiting from a use cases.The
#publishmethod returns the list of published events. The exception thrown by#publish!carries that list as well. This allows not only to publish them to listeners but to return them to caller. This is necessary to simplify chaining of service objects that includes theInformator.The
Informatorhas no global neither async subscribers. The gem's scope is narrower. Its main goal is to support service objects, that are either chained to each other, or publishes their results to decoupled listeners.
Synopsis
The Informator module API defines 4 instance methods:
#subscribeto subscribe listeners for receiving events, published by the informator#rememberto build an event and keep it unpublished#publishto publish all events keeped since last publishing#publish!the same as#publish, except for it throws:publishedexception which carries a list of events
Except for the Informator the module defines public class Informator::Event for immutable events, that has 3 attributes:
#typefor symbolic type of the event#attributesfor hash of attributes, carried by the event#messagesfor array of human-readable messages, describing the event
The event instance is build by the #remember, #publish or #publish! methods and is sent to listeners by either #publish or #publish!. When sending an event, the informator just calls the listeners callback, defined by #subscribe method, and gives it a corresponding event object as the only argument.
require "informator"
class Listener
def receive(event)
puts "The listener received #{ event.type }: #{ event.messages }"
end
end
listener = Listener.new
class Service
include Informator
def call
catch(:published) { do_some_staff }
end
def do_some_staff
# ...
remember :success, "for now all is fine", foo: :bar
#
publish! :error, "OMG!", bar: :baz
end
end
service = Service.new
service.subscribe listener, :receive
result = service.call
# The listener received success: ["for now all is fine"]
# The listener received error: ["OMG!"]
# => [
# #<Informator::Event @type=:success @attributes={ foo: :bar } @messages=["for now all is fine"]>,
# #<Informator::Event @type=:error @attributes={ bar: :baz } @messages=["OMG!"]>
# ]
In the example above the listener#receive method is called twice with the first, and then with the second event as an argument.
Then the publish! throws an exception that carries the list of messages.
The service#call catches it and returns the array of events. With this feature it is possible not only to subscribe for the service's events but receive it directly, combining both telling and asking when necessary.
Installation
Add this line to your application's Gemfile:
# Gemfile
gem "informator"
Then execute:
bundle
Or add it manually:
gem install informator
Compatibility
Tested under rubies compatible to MRI 2.1+.
Uses RSpec 3.0+ for testing and hexx-suit for dev/test tools collection.
Contributing
- Read the STYLEGUIDE
- Fork the project
- Create your feature branch (
git checkout -b my-new-feature) - Add tests for it
- Commit your changes (
git commit -am '[UPDATE] Add some feature') - Push to the branch (
git push origin my-new-feature) - Create a new Pull Request
License
See the MIT LICENSE.