Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

A micro library providing Ruby objects with Publish-Subscribe capabilities

NotificationsYou must be signed in to change notification settings

nedap/wisper-compat

Repository files navigation

A micro library providing Ruby objects with Publish-Subscribe capabilities

Gem Version

  • Decouple core business logic from external concerns in Hexagonal style architectures
  • Use as an alternative to ActiveRecord callbacks and Observers in Rails apps
  • Connect objects based on context without permanence
  • Publish events synchronously or asynchronously

Note: Wisper was originally extracted from a Rails codebase but is not dependant on Rails.

Please also see theWiki for more additional information and articles.

For greenfield applications you might also be interested inWisperNext andMa.

Installation

Add this line to your application's Gemfile:

gem'wisper-compat','4.0.0'

Usage

Any class with theWisper::Publisher module included can broadcast eventsto subscribed listeners. Listeners subscribe, at runtime, to the publisher.

Publishing

classCancelOrderincludeWisper::Publisherdefcall(order_id)order=Order.find_by_id(order_id)# business logic...iforder.cancelled?broadcast(:cancel_order_successful,order.id)elsebroadcast(:cancel_order_failed,order.id)endendend

When a publisher broadcasts an event it can include any number of arguments.

Thebroadcast method is also aliased aspublish.

You can also includeWisper.publisher instead ofWisper::Publisher.

Subscribing

Objects

Any object can be subscribed as a listener.

cancel_order=CancelOrder.newcancel_order.subscribe(OrderNotifier.new)cancel_order.call(order_id)

The listener would need to implement a method for every event it wishes to receive.

classOrderNotifierdefcancel_order_successful(order_id)order=Order.find_by_id(order_id)# notify someone ...endend

Blocks

Blocks can be subscribed to single events and can be chained.

cancel_order=CancelOrder.newcancel_order.on(:cancel_order_successful){ |order_id| ...}.on(:cancel_order_failed){ |order_id| ...}cancel_order.call(order_id)

You can also subscribe to multiple events usingon by passingadditional events as arguments.

cancel_order=CancelOrder.newcancel_order.on(:cancel_order_successful){ |order_id| ...}.on(:cancel_order_failed,:cancel_order_invalid){ |order_id| ...}cancel_order.call(order_id)

Do notreturn from inside a subscribed block, due to the wayRuby treats blocksthis will prevent any subsequent listeners having their events delivered.

Handling Events Asynchronously

cancel_order.subscribe(OrderNotifier.new,async:true)

Wisper has various adapters for asynchronous event handling, please refer towisper-celluloid,wisper-sidekiq,wisper-activejob,wisper-que orwisper-resque.

Depending on the adapter used the listener may need to be a class instead of an object. In this situation, every method corresponding to events should be declared as a class method, too. For example:

classOrderNotifier# declare a class method if you are subscribing the listener class instead of its instance like:#   cancel_order.subscribe(OrderNotifier)#defself.cancel_order_successful(order_id)order=Order.find_by_id(order_id)# notify someone ...endend

ActionController

classCancelOrderController <ApplicationControllerdefcreatecancel_order=CancelOrder.newcancel_order.subscribe(OrderMailer,async:true)cancel_order.subscribe(ActivityRecorder,async:true)cancel_order.subscribe(StatisticsRecorder,async:true)cancel_order.on(:cancel_order_successful){ |order_id|redirect_toorder_path(order_id)}cancel_order.on(:cancel_order_failed){ |order_id|renderaction::new}cancel_order.call(order_id)endend

ActiveRecord

If you wish to publish directly from ActiveRecord models you can broadcast events from callbacks:

classOrder <ActiveRecord::BaseincludeWisper::Publisherafter_commit:publish_creation_successful,on::createafter_validation:publish_creation_failed,on::createprivatedefpublish_creation_successfulbroadcast(:order_creation_successful,self)enddefpublish_creation_failedbroadcast(:order_creation_failed,self)iferrors.any?endend

There are more examples in theWiki.

Global Listeners

Global listeners receive all broadcast events which they can respond to.

This is useful for cross cutting concerns such as recording statistics, indexing, caching and logging.

Wisper.subscribe(MyListener.new)

In a Rails app you might want to add your global listeners in an initializer.

Global listeners are threadsafe. Subscribers will receive events published on all threads.

Scoping by publisher class

You might want to globally subscribe a listener to publishers with a certainclass.

Wisper.subscribe(MyListener.new,scope::MyPublisher)Wisper.subscribe(MyListener.new,scope:MyPublisher)Wisper.subscribe(MyListener.new,scope:"MyPublisher")Wisper.subscribe(MyListener.new,scope:[:MyPublisher,:MyOtherPublisher])

This will subscribe the listener to all instances of the specified class(es) and theirsubclasses.

Alternatively you can also do exactly the same with a publisher class itself:

MyPublisher.subscribe(MyListener.new)

Temporary Global Listeners

You can also globally subscribe listeners for the duration of a block.

Wisper.subscribe(MyListener.new,OtherListener.new)do# do stuffend

Any events broadcast within the block by any publisher will be sent to thelisteners.

This is useful for capturing events published by objects to which you do not have access in a given context.

Temporary Global Listeners are threadsafe. Subscribers will receive events published on the same thread.

Subscribing to selected events

By default a listener will get notified of all events it can respond to. Youcan limit which events a listener is notified of by passing a string, symbol,array or regular expression toon:

post_creator.subscribe(PusherListener.new,on::create_post_successful)

Prefixing broadcast events

If you would prefer listeners to receive events with a prefix, for exampleon, you can do so by passing a string or symbol toprefix:.

post_creator.subscribe(PusherListener.new,prefix::on)

Ifpost_creator were to broadcast the eventpost_created the subscribedlisteners would receiveon_post_created. You can also passtrue which willuse the default prefix, "on".

Mapping an event to a different method

By default the method called on the listener is the same as the eventbroadcast. However it can be mapped to a different method usingwith:.

report_creator.subscribe(MailResponder.new,with::successful)

This is pretty useless unless used in conjunction withon:, since all eventswill get mapped to:successful. Instead you might do something like this:

report_creator.subscribe(MailResponder.new,on::create_report_successful,with::successful)

If you pass an array of events toon: each event will be mapped to the samemethod whenwith: is specified. If you need to listen for select eventsand map each one to a different method subscribe the listener once foreach mapping:

report_creator.subscribe(MailResponder.new,on::create_report_successful,with::successful)report_creator.subscribe(MailResponder.new,on::create_report_failed,with::failed)

You could also alias the method within your listener, as suchalias successful create_report_successful.

Testing

Testing matchers and stubs are in separate gems.

Clearing Global Listeners

If you use global listeners in non-feature tests youmight want to clear themin a hook to prevent global subscriptions persisting between tests.

after{Wisper.clear}

Need help?

TheWiki has more examples,articles and talks.

Got a specific question, try theWisper tag on StackOverflow.

Running Specs

bundle exec rspec

To run the specs on code changes tryentr:

ls **/*.rb | entr bundle exec rspec

Contributing

Please read theContributing Guidelines.

License

(The MIT License)

Copyright (c) 2013 Kris Leech

Permission is hereby granted, free of charge, to any person obtaining a copy ofthis software and associated documentation files (the 'Software'), to deal inthe Software without restriction, including without limitation the rights touse, copy, modify, merge, publish, distribute, sublicense, and/or sell copiesof the Software, and to permit persons to whom the Software is furnished to doso, subject to the following conditions:

The above copyright notice and this permission notice shall be included in allcopies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS ORIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THEAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHERLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THESOFTWARE.

About

A micro library providing Ruby objects with Publish-Subscribe capabilities

Resources

Stars

Watchers

Forks

Packages

No packages published

[8]ページ先頭

©2009-2025 Movatter.jp