krisleech / wisper Goto Github PK
View Code? Open in Web Editor NEWA micro library providing Ruby objects with Publish-Subscribe capabilities
A micro library providing Ruby objects with Publish-Subscribe capabilities
Hi,
i have a really ridiculous situation going on my production server. I'm using wisper with my rails app and, from among about 50 broadcast
calls here and there in my source code, one of these broadcast
calls is not beeing triggered.
I'm defining a global listeners in rails initializers:
# config/initializers/wisper.rb
Wisper.subscribe(LogListener.new)
# ... other listeners
And LogListener looks like that:
class LogListener
def create_job_success(job)
log "Job has been created [##{job.id}]"
end
def create_job_success2(job)
log "Job has been created [##{job.id}]"
end
def test_event
log "It works"
end
end
And also i have a service which, let's say, have a following code:
class MyService
def call
broadcast :test_event
broadcast :create_job_success
broadcast :create_job_success2
broadcast :test_event
end
end
And here is the deal - when running my service with MyService.new.call
, i will always get 'It works' produced by test_event
broadcast and create_job_success2
is also beeing triggered (i have that fully covered by rspec tests). What's more - according to rspec results, create_job_success
is also getting triggered properly (these specs covers only this single MyService
class, nothing more. So the issue can be somewhere else) .
However, in production, this create_job_success
event is not getting triggered (while the others are getting triggered properly - i know that by watching log results produced by LogListener). I also can't see any exception or antyhing like that - it just doesn't work and falls silently.
That situation drives me mad becuase i'm completely not able to debug what's going on and my boss want's to kill me so i have a question to you guys - have you any idea what can possibly create such situation, in which wisper loggers/listeners fails silently or have you any idea what can be wrong with my code?
Thanks in advance!
Trying out master and for some reason celluloid isn't getting picked up as a dependency. I could probably add it to my Gemfile manually, but I wonder if it would work to make a wisper-celluloid
or if celluloid was optional. It feels weird having a simple pub sub framework bundled celluloid with it.
As well as:
Wisper::GlobalListeners.add_listener(MyListener.new)
allow:
Wisper.add_listener(MyListener.new)
/wisper-2.0.0.rc1/lib/wisper/configuration.rb:29: warning: method redefined; discarding old fetch
/forwardable.rb:181: warning: previous definition of fetch was here
/wisper-2.0.0.rc1/lib/wisper/broadcasters/logger_broadcaster.rb:19: warning: shadowing outer local variable - id_method
configuration might need to undef
the method first.
/wisper-2.0.0.rc1/lib/wisper/configuration.rb:29: warning: method redefined; discarding old fetch
/forwardable.rb:181: warning: previous definition of fetch was here
/wisper-2.0.0.rc1/lib/wisper/broadcasters/logger_broadcaster.rb:19: warning: shadowing outer local variable - id_method
configuration might need to undef
the method first.
Hello!
I'm trying to use global listeners in my rails app. According to wisper docs, i should put the following code in my initializer:
Wisper.subscribe(OutstandingWorkValueListener.new)
Wisper.subscribe(JobVideoCreatorListener.new)
Wisper.subscribe(JobRatingsListener.new)
However, for some reason, from time to time (it's completely unpredictible! sometimes restarting a server does the job, sometimes not) i'm getting an error that says for exmaple:
A copy of OutstandingWorkValueListener has been removed from the module tree but is still active!
Am i doing something wrong? What does that error mean? Thanks in advance for any clues.
We use rspec 3, with backwards compatibility for old should
syntax. All specs should be changed to expect
syntax and should
syntax disabled in spec_helper
.
Anyone who wants to take this please leave a comment and/or submit a PR referencing this issue 😄
When I try to publish something from a class method it returns the error:
undefined method
publish' for Fo:Class`
class Foo
include Wisper::Publisher
def self.do_something
publish(:something_happened)
end
end
What would be the suggested approach here ?
I'm getting this error very frequently:
A copy of EventDispatcher has been removed from the module tree but is still active!
/Users/Tute/.rvm/gems/ruby-2.2.2@game-events/gems/activesupport-4.2.0/lib/active_support/dependencies.rb:479:in load_missing_constant' /Users/Tute/.rvm/gems/ruby-2.2.2@game-events/gems/activesupport-4.2.0/lib/active_support/dependencies.rb:184:in
const_missing'
/Users/Tute/playspace/game_events/app/listeners/event_dispatcher.rb:16:in method_missing' /Users/Tute/.rvm/gems/ruby-2.2.2@game-events/gems/wisper-celluloid-0.0.1/lib/wisper/celluloid_broadcaster.rb:17:in
public_send'
/Users/Tute/.rvm/gems/ruby-2.2.2@game-events/gems/wisper-celluloid-0.0.1/lib/wisper/celluloid_broadcaster.rb:17:in broadcast' /Users/Tute/.rvm/gems/ruby-2.2.2@game-events/gems/celluloid-0.16.0/lib/celluloid/calls.rb:26:in
public_send'
/Users/Tute/.rvm/gems/ruby-2.2.2@game-events/gems/celluloid-0.16.0/lib/celluloid/calls.rb:26:in dispatch' /Users/Tute/.rvm/gems/ruby-2.2.2@game-events/gems/celluloid-0.16.0/lib/celluloid/calls.rb:122:in
dispatch'
/Users/Tute/.rvm/gems/ruby-2.2.2@game-events/gems/celluloid-0.16.0/lib/celluloid/cell.rb:60:in block in invoke' /Users/Tute/.rvm/gems/ruby-2.2.2@game-events/gems/celluloid-0.16.0/lib/celluloid/cell.rb:71:in
block in task'
/Users/Tute/.rvm/gems/ruby-2.2.2@game-events/gems/celluloid-0.16.0/lib/celluloid/actor.rb:357:in block in task' /Users/Tute/.rvm/gems/ruby-2.2.2@game-events/gems/celluloid-0.16.0/lib/celluloid/tasks.rb:57:in
block in initialize'
/Users/Tute/.rvm/gems/ruby-2.2.2@game-events/gems/celluloid-0.16.0/lib/celluloid/tasks/task_fiber.rb:15:in `block in create'
I'm using an async global listener, loaded in an initializer:
Wisper.subscribe(EventDispatcher.new, prefix: true, async: true)
https://github.com/krisleech/wisper/blob/master/lib/wisper/global_listeners.rb
The singleton instance used to store the global listeners is the same instance across all threads. Its setter method mutates internal state (the array of listeners) making it not threadsafe, one thread can clobber another. The same, to a lesser extent, applies to reading the array of listeners.
Generally all global listeners will be added once during app initialization, not ad-hoc during runtime, so its unlikely to be a problem in practice. But that said it would be good to make it threadsafe.
Allow:
Wisper.add_listener(listener)
to be invoked as Wisper.subscribe(listeners)
Wisper.with_listeners(listeners) { }
to be invoked as Wisper.subscribe(listeners) { }
Must retain backwards compatibility.
New method Wisper#subscribe
will be something like:
def self.subscribe(*listeners, &block)
options = listeners.last.is_a?(Hash) ? listeners.pop : {}
if block_given?
TemporaryListeners.with(listeners, block)
else
GlobalListeners.add(listeners, options)
end
end
The newwest 1.3.0 release doesn't seem to be available via gem install from rubygems....
I'm finding I sometimes have actions that raise multiple events to which I want one response. For example:
action = Something.new
action.on(:foo, :bar, :baz) do
# do a thing
end
What are your thoughts on supporting such a syntax? I could try to make a PR, just wanted to run it by you first, @krisleech
I'm using Wisper to add subscribers to model classes (not instances of classes). After some time, and after modyfing some code, I receive the exception noted above.
I know it has to do with Rails/Ruby class reloading in development. What I don't know is how to solve this issue. Does anyone have an idea, experience with this issue?
Instead of the last registration.broadcast
results
def broadcast(event, *args)
registrations.each do | registration |
registration.broadcast(clean_event(event), self, *args)
end
end
let it the method returns arguments of its own:
def broadcast(event, *args)
registrations.each do | registration |
registration.broadcast(clean_event(event), self, *args)
end
return event, *args
end
I like using the method in my service objects
require 'wisper'
class MyService
include Wisper::Publisher
def run
begin
# do something
rescue SomeError
publish :some_error
rescue AnotherError
publish :another_error
rescue
publish :common_error
else
publish :success
end
end
end
This works fine in controllers
class MyController < ApplicationController
def my_action
service = MyService.new
service.subscribe self
service.run
end
def success
# send some responce
end
end
Here the controller is playing the role of some listener with a 'responce methods' defined.
But when I'm trying to use one service inside another one, I need to set special listener to catch published notifications. Instead I'd like in some cases to get an answer from MyService#run directly.
service = MyService.run
Yeah, I know this «breaks» the pattern, but can be useful in special cases.
Currently it is only registered as default
. But it should also be registered under the more descriptive sync
.
The default broadcaster key should also be configurable:
config.broadcasters.default = :sync
The default broadcaster is used when none is specified when subscribing.
Hello!
I've just discovered wisper gem and i'm amazed of all the things i can accomplish with it. However, i've failed on the most basic thing possible.
I have a class which saves a record in a database. Here is it's code (totally simplified):
class CreateJob
include Wisper::Publisher
def call()
@job = ...
if @job.save
broadcast(:create_job_success, @job)
else
broadcast(:create_job_failure, @job)
end
end
end
My controller method looks like that:
def create
create_job_service.on(:create_job_success) do |job|
render 'create_success', status: :created, job: job
end
create_job_service.on(:create_job_failure) do |job|
render 'create_failure', status: :bad_request, job: job
end
create_job_service.call()
end
private
def create_job_service
CreateJob.new
end
The CreateJob
method is tested using rspec-wisper to check if events are broadcasted and yes, they are. However, for some reason, these events are not visible for my controller so none of the on
block code is beeing triggered.
Am i doing something wrong in here? Can i somehow debug what events are emitted ? Or do anything else to check what's wrong in here?
Thanks in advance
Hello again,
I'm having trouble configuring a global async Listener.
My first attempt was as stated in the README:
Wisper.subscribe QueueListener.new, async: true
But I get
wisper-1.6.0/lib/wisper/configuration.rb:25:in `fetch': broadcaster not found for async (KeyError)
Then, after reading some of the wisper-celluloid
source code I tried this
Wisper.configure do |config|
config.broadcaster :async, Engine1::AnotherModel
end
Wisper.subscribe QueueListener.new, async: true
(QueueListener
is in another engine, Engine2
)
But I get
activerecord (4.1.6) lib/active_record/dynamic_matchers.rb:26:in `method_missing'
wisper (1.6.0) lib/wisper/registration/object.rb:16:in `broadcast'
wisper (1.6.0) lib/wisper/publisher.rb:65:in `block in broadcast'
Finally, if I remove the new
off the QueueListener
it no longer throws an error, but the event doesn't seem to get to the listener.
Hopefully I don't have to do something like the second example, since the publisher and the listener are in separate engines, this would couple the listener to the publisher.
Am I doing something wrong?
Thanks in advance.
So we can provide integration with Sidekiq, Celluloid and similar we need to allow dispatch of an event (i.e. public_send
) inWisper::ObjectRegistration#broadcast
to be overriden easily in another gem.
broadcast
should be reduced to something like:
def broadcast(event, *args)
broadcast_event(event, *args) if should_broadcast?(event)
end
This allows either broadcast
or broadcast_event
to be overriden to provide alternative dispatch of events in gems like wisper-async
and (hopefully forthcoming) wisper-sidekiq
.
It is possible to subscribe to all or multiple events with a single method using :with
. It would make sense not to lose information on the subscriber side of the event name. This is useful in cross cutting subscribers like logging or analytics.
Currently I'm working around this with method_missing
.
Would be happy to submit a PR if this is a sensible idea.
From the docs, I see that the way to to 'couple' a Publisher and a Listener is to call the 'subscribe' method on the Publisher instance.
So, @publisher.subscribe(SomeListener.new)
Actually, the subscription is happening the other way around, i.e the Listener is subscribed to the publisher's events and the listener's methods get invoked when an event is 'fired'.
Maybe 'add_as_listener' or 'add_listener' are alternatives that better express the actual intent?
So, we could have
@publisher.add_listener(SomeListener)
Some companies will only use gems with a certain license.
The canonical and easy way to check is via the gemspec,
via e.g.
spec.license = 'MIT'
# or
spec.licenses = ['MIT', 'GPL-2']
Even for projects that already specify a license, including a license in your gemspec is a good practice, since it is easily
discoverable there without having to check the readme or for a license file.
For example, there is a License Finder gem to help companies ensure all gems they use
meet their licensing needs. This tool depends on license information being available in the gemspec. This is an important enough
issue that even Bundler now generates gems with a default 'MIT' license.
If you need help choosing a license (sorry, I haven't checked your readme or looked for a license file),
github has created a license picker tool.
In case you're wondering how I found you and why I made this issue, it's because I'm collecting stats on gems (I was originally
looking for download data) and decided to collect license metadata,too, and make issues for gemspecs not specifying a license as a public service :).
I hope you'll consider specifying a license in your gemspec. If not, please just close the issue and let me know. In either case, I'll follow up. Thanks!
p.s. I've written a blog post about this project
I think I found an issue where if you use an on
block with a named subscriber (.subscribe(Foo.new)
), the named subscriber will not get called.
I have a failing test on this branch: https://github.com/reverbdev/wisper/tree/on-with-named-subscriber - commit reverbdotcom@50c789d
But wanted to get your input @krisleech before proceeding. Am I seeing something wrong?
Hey @krisleech , we're trying to test publishers in isolation but the global listeners are kicking in. Should we just be doing Wisper::GlobalListeners.clear in our test? What do you think?
What's the 'correct' way to verify if a model is publishing an event? I tried this:
class User
include Wisper::Publisher
after_create do |user|
publish(:user_created, user)
end
end
it "publishes a message on creation" do
allow(Wisper::Publisher).to receive(:publish)
user = create(:user)
expect(Wisper::Publisher).to have_received(:publish).with(:user_created, user)
end
But this fails with an unmet expectation - I think because publish
is a private method?
Them gem on 'RubyGems' is not with the regex option.
I am trying to use the github version in the meantime, but I am getting the following error.
Errno::ENOENT: No such file or directory @ rb_sysopen - /Users/alexandrecalvao/.ssh/gem-private_key.pem
An error occurred while installing wisper (1.6.0), and Bundler cannot continue.
subscribe
allows a new option, prefix
, the given value is prefixed to all broadcast events:
subscribe(MyListenter.new, :prefix => 'on')
Events are broadcast as normal:
broadcast(:create_delivery_successful, delivery)
But the subscribed listener, if it responds, receives on_create_delivery_successful
:
class My Listener
def on_create_delivery_successful(delivery)
# ...
end
end
I can't think of any other useful prefix other than "on" so prefix
could also accept true
and the prefix would default to "on":
subscribe(MyListenter.new, :prefix => true)
Hello,
I was trying out Wisper, creating a global listener and using a scaffolded ActionController as a publisher. After the action was finished, the controller was supposed to broadcast a different message whether a save
was successful or not. But I got this exception:
must give at least one event
Here is my code
respond_to do |format| # Exception marked here
if @my_model.save
broadcast(:create_model_success, @my_model)
format.html { redirect_to @my_model, notice: 'My model was successfully created.' }
format.json { render :show, status: :created, location: @my_model }
else
broadcast(:create_model_error, @my_model)
format.html { render :new }
format.json { render json: @my_model.errors, status: :unprocessable_entity }
end
end
I then noticed the publisher module has a respond_to
method. So my guess is a name collision. Here is my working code:
# respond_to do |format|
if @my_model.save
broadcast(:create_model_success, @my_model)
redirect_to @my_model, notice: 'My model was successfully created.'
# format.html { redirect_to @my_model, notice: 'My model was successfully created.' }
# format.json { render :show, status: :created, location: @my_model }
else
broadcast(:create_model_error, @my_model)
render :new
# format.html { render :new }
# format.json { render json: @my_model.errors, status: :unprocessable_entity }
end
# end
You can check my complete project here: https://github.com/SebastianOsuna/wisper_mailing
Hi,
this is not really an issue... just wanted to let you know that I've began porting wisper-rspec to a wisper-minitest gem!
Hopefully it'll be useful for people who want to use Minitest as their testing framework.
I've been thinking about how to deal with multiple things happening on multiple children of an aggregate root. Something along these lines:
global_subscribe(listener1, listener2) do
# ... do something ...
end
The idea is that I don't always want the global subscription, but I'm not able to or I don't want to add_listener
on each child. Of course, the parent could "forward" the events, but I'm not sure that's the right pattern. It's a little more tedious.
If a broadcaster key is configured more than once, for example both wisper-sidekiq
and wisper-celluloid
gems are required, then a warning should be issued.
when registering a broadcaster
and key is already present
issue warning
class Foo; include Wisper.publisher; end
f = Foo.new
f.subscribe(:success) { ... }
f.send(:broadcast, :success)
In the above example I want to subscribe a block, for which I should use on
. I accidently used subscribe
but I got no error despite the method signature not allowing for a block...
I have the following code
Wisper.add_listener(ItemListener.new,
on: [:item_query_rs,
:item_discount_add_rs,
:item_non_inventory_add_rs,
:item_other_charge_add_rs,
:item_payment_add_rs,
:item_service_add_rs],
with: :add_item)
It would be nice to write something like this instead.
Wisper.add_listener(ItemListener.new, pattern: "item_*_rs", with: :add_item)
Right now, integration tests are pretty much necessary to ensure that event names are the same on both the publisher and the subscriber side. I've implemented a monkey patch to make these explicit on my project.
What do you think of this technique? Would it be worth integrating directly in wisper?
module Publisher
extend ActiveSupport::Concern
include Wisper
included do
alias_method_chain :publish, :validation
alias_method_chain :add_listener, :validation
alias :subscribe :add_listener
end
module ClassMethods
def publishes(*args)
published_events.concat args.flatten.map(&:to_sym)
end
def published_events
@published_events ||= []
end
def publishes?(event)
published_events.include? event.to_sym
end
end
def publishes?(event)
self.class.publishes? event
end
def publish_with_validation(event, *args)
raise CannotPublishEvent.new(self, event) unless publishes? event
publish_without_validation(event, *args)
end
def add_listener_with_validation(subscriber, options = {})
Array(options[:on]).each do |event|
raise MissingEventHandler.new(subscriber, event) unless subscriber.respond_to? event
raise PublisherDoesNotPublishEvent.new(self, event) unless publishes? event
end
add_listener_without_validation(subscriber, options)
end
class CannotPublishEvent < Exception
def initialize(publisher, event)
super "Attempted to publish unknown event :#{event} on #{publisher.class.name}.
Make sure it is included in the publishes clause:
publishes :#{event}"
end
end
class MissingEventHandler < Exception
def initialize(subscriber, event)
super "Attempted to subscribe to event :#{event}, but
#{subscriber.class.name} does not respond_to? it."
end
end
class PublisherDoesNotPublishEvent < Exception
def initialize(publisher, event)
super "Attempted to subscribe to event :#{event}, but
#{publisher.class.name} does not publish it."
end
end
end
require 'spec_helper'
require 'publisher'
describe Publisher do
class MyPublisher
include Publisher
publishes :something_happened
end
class MySubscriber
def something_happened
end
def something_else_done
end
end
let(:publisher) { MyPublisher.new }
describe '.publishes?' do
it 'is true when the publisher publishes the event' do
MyPublisher.publishes?(:something_happened).should be_true
end
it 'is false when the publisher does not publishe the event' do
MyPublisher.publishes?(:something_else_happened).should be_false
end
end
describe '#publishes?' do
it 'is true when the publisher publishes the event' do
publisher.publishes?(:something_happened).should be_true
end
it 'is false when the publisher does not publish the event' do
publisher.publishes?(:something_else_happened).should be_false
end
end
describe '#publish' do
it 'allows publishing specified events' do
subscriber = spy("Subscriber")
publisher.subscribe subscriber
publisher.send :publish, :something_happened
subscriber.should have_received(:something_happened)
end
it 'prevents publishing unspecified events' do
expect {
publisher.send :publish, :something_else_happened
}.to raise_error(Publisher::CannotPublishEvent)
end
end
describe '#subscribe' do
it 'allows subscriptions if the subscriber does have the method' do
subscriber = MySubscriber.new
publisher.subscribe subscriber, on: :something_happened
publisher.listeners.map(&:listener).should include subscriber
end
it 'prevents subscriptions if the subscriber cannot handle the event' do
subscriber = Object.new
expect {
publisher.subscribe subscriber, on: :something_happened
}.to raise_error(Publisher::MissingEventHandler)
end
it 'prevents subscriptions if the publisher does not publish the event' do
subscriber = MySubscriber.new
expect {
publisher.subscribe subscriber, on: :something_else_done
}.to raise_error(Publisher::PublisherDoesNotPublishEvent)
end
end
end
Is there any possibility to listen to all events without explicitly defining them? So to say, to define a "wrapper" or a "proxy" method which is called if any event is emitted, but without a) defining a method for each event and b) loosing the information about the fired event?
I know I can use the "with" method to redirect all events to one method, but this way I gonna loose the (very important) information, which event was called originally. The other option I can think of (if overwriting "send" in my listener is not an option...) is writing my own broadcaster (like this https://github.com/krisleech/wisper/blob/master/lib/wisper/broadcasters/logger_broadcaster.rb) for it, but can I attach this broadcaster only for one temporary subscription like this:
Wisper.subscribe(MyPublicLoggingListener.new, broadcaster: :my_logging_broadcaster) do
# anything
end
class MyPublicLoggingListener
def any_event_emitted(event)
log(event)
end
end
Instead of creating a new method commit
as per the example in the README and Wiki instead callbacks could be used?
class Bid < ActiveRecord::Base
include Wisper::Publisher
after_create :publish_creation_successful
after_validation :publish_creation_failed, on: :create
private
def publish_creation_successful
broadbast(:bid_creation_successful, self)
end
def publish_creation_failed
broadbast(:bid_creation_failed, self) if errors.any?
end
end
Need to test of this actually works...
Hello,
I've tried using the listeners as desribed in the howto inside a Grape API with success and fail callbacks.
The basic code is as follows:
post do
service = RatingService.new
service.execute(rating)
service.on(:rating_successful) do |rating|
status(200)
{
message: "Successfully rated #{rating.topic.name}"
}
end
service.on(:rating_failed) do |errors|
render_api_error!(errors, 400)
end
end
Now when I post to this Grape API I don't get the expected response defined in the on() blocks but instead a direct response object from the Whisper Service:
{
async: false,
local_registrations: [
{
listener: { },
on: [
"all"
],
with: null,
prefix: "",
allowed_classes: [ ]
},
{
listener: { },
on: [
"all"
],
with: null,
prefix: "",
allowed_classes: [ ]
},
{
listener: { },
on: [
"rating_successful"
]
},
{
listener: { },
on: [
"rating_failed"
]
}
]
}
I tried moving the execute() call AFTER the on() blocks but it didn't work either...
Am I missing something?
Any help would be greatly appreciated!
my_publisher.listeners do
add ActivityFeedListener.new
add StatisticsListener.new
end
and
Wisper.global_listeners do
add ActivityFeedListener.new
add StatisticsListener.new
end
Work in progress in "new_add_listener_syntax" branch
Start is here:
https://github.com/krisleech/wisper-rspec
As we're testing our publishers with unit tests, we sometimes want to do something like:
publisher.should_receive(:do_a_thing).with(args).and_publish(:some_event, arg1, arg2)
I am starting to think about how to implement this, but if you have any insight or experience with RSpec, that would help tremendously. I think we should agree on the syntax first (my hacky stub_wisper_publisher needs rework), and then move on from there. Are you interested in this as a PR if we can figure out the right syntax?
thanks!
I'm trying to figure out a good technique for testing wisper driven controllers. For example:
class UserServicesController < ApplicationController
def create
connector = Reverb::Actions::ConnectOauth.new(omniauth_request)
connector.on(:user_oauth_failure) do |user|
flash[:error] = "Sorry, we were unable to log you in. Please try again."
@user = user
return render :template => 'users/new'
end
connector.connect
return_or_redirect_to root_path
end
So what are we interested in from the controller perspective:
ConnectOauth.should_receive
class CreateDelivery
include Wisper::Publisher
end
CreateDelivery.add_listener(StatsListener.new)
This is like global subscription but scoped to instances of a single class.
Another option would be to add an option to global subscriptions allowing scoping to a class (or classes):
Wisper.add_listener(MyListener.new, :scope => CreateDelivery)
The method signeture would become something like:
subscribe(*listeners, on:, with: prefix:, broadcaster: async:)
If combining splat and named arguments is possible.
This would mean dropping 1.9 support in Wisper 2.0.
Use case: One publisher calls another publisher, the second publisher need to inherit subscribers from the first.
class SecondPublisher
include Wisper::Publisher
end
class FirstPublisher
include Wisper::Publisher
def execute
# ...
command = SecondPublisher.new
command.inherit_listeners(self) # NEW METHOD TO BE ADDED
command.execute
# ...
broadcast(:successful)
end
end
The SecondPublisher
should inherit the successful
event from FirstPublisher
.
Each release should be summarised in a CHANGELOG file.
First thanks for the hard work you have put in on Wisper. I love this library and use it in every Ruby project I participate in.
I am not sure how you will feel about this issue, but I do not particularly like that the message handlers must be public methods as it mucks up the public API of the class. Have you considered when determining if a listener will receive a broadcasted message with respond_to?( :some_message, true )
?
If you wanted to get a little more restrictive you could only allow for public or protected methods and not use #respond_to?, but instead a custom method that looks at only public and protected methods.
C# handles this by exposing a public delegate and a protected handler method. This allows for a clear public API of the class.
Thanks!
Currently the broadcaster can be specified as broadcaster: XXX
or async: true
.
When there are more than just sync and async broadcasters in a project it would be useful to use the broadcasters key to set it.
For example if both wisper-celluloid and wisper-sidekiq in Gemfile then both will set the async
broadcaster, in this case instead of doing broadcaster: :celluloid
we can do celluloid: true
.
subscribe(listener, celluloid: true)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.