Giter VIP home page Giter VIP logo

wisper's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

wisper's Issues

One of my broadcast calls does not work in production

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!

`require': cannot load such file -- celluloid/autostart (LoadError)

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.

Get rid of warnings

/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.

Get rid of warnings

/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.

A copy of <SomeListenerHere> has been removed from the module tree but is still active!

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.

Convert all specs to new `expect` style

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 😄

Publish for class method

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 ?

A copy of XXX has been removed from the module tree but is still active!

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:inconst_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:inpublic_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:inpublic_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:indispatch'
/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:inblock 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:inblock 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)

Make global listeners getter and setter threadsafe

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.

alias global and temporary listener methods to "subscribe" for consistancy

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

React to multiple events at once

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

"A copy of <Subscriber> has been removed from the module tree but is still active!"

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?

Allow #broadcast method to return its own arguments

What

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

Why

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.

Block listener not working in controller

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

Can't define an async listener

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.

Make dispatch of event to listener easier to override

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.

Pass the name of the event as a final argument

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.

method name 'subscribe' is a bit confusing. 'add_as_listener' , 'add_listener' as aliases ?

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)

License missing from gemspec

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

Unit testing for publishing?

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?

Ruby Gems version

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.

Allow broadcast events to be prefixed with "on"

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)

`respond_to` name collision

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

Testing Wisper with Minitest

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.

Temporary Global Registration (thread local or request local)

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.

warn if broadcaster key overridden

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

[FEATURE] Events pattern.

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)

Safe subscription

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

Temporary Listener for all events

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

Replace ActiveRecord example with one using callbacks

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

on(...) Callbacks doesn't seem to work correctly inside Grape API

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!

Allow multiple listeners to be added using a block

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

Ability to stub wisper publishers with message expectations (should_receive with args)

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!

Recommendations for testing wispered controllers?

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:

  1. that the business action was invoked. This can be done with ConnectOauth.should_receive
  2. that on failure the controller does the right thing. How do we test this part? Do we substitute the creation of the ConnectOauth action with some other test publisher?

Allow listener to be subscribed to all instances of a class

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)

Inheriting of events

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.

Broadcasting to protected methods

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!

Allow broadcaster to be set using name of broadcaster in subscribe options

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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.