0

I want to register a global listener in my ApplicationController, that contains the current_user. I ended up trying this:

class ApplicationController < ActionController::Base
  before_action do
    @listener = MyListener.new(current_user)
    Wisper.clear if Rails.env.development?
    Wisper.subscribe(@listener, scope: :MyPublisher)
  end
end

However, when I deployed this code to heroku these global listeners never get unsubscribed and the app keeps on accumulating listeners through requests. I can't rely on after_action since the application could terminate due to an error. What is the right way of doing this, is it to forcibly clear before I subscribe, like this?

class ApplicationController < ActionController::Base
  before_action do
    @listener = MyListener.new(current_user)
    Wisper.clear
    Wisper.subscribe(@listener, scope: :MyPublisher)
  end
end

In another question, Kris suggested that we should use an initializer which subscribes once. The reason I am not doing this is because I want to have access to the current_user, and I prefer not to pass it via global variables/Thread.current. What is the best way to make GlobalListeners work with current_user?

My use case is to process all instances of ActiveRecord models loaded by current_user across all controller actions. Wisper does exactly what I needed it to do except for the issue mentioned.

class MyPublisher < ActiveRecord::Base
  include Wisper::Publisher
  after_find { broadcast(:process_find, self) }
end

and for the Listener:

class MyListener
  def initialize(current_user)
    @current_user = current_user
  end

  def process_find
    ...
  end
end
Community
  • 1
  • 1
bugzpodder
  • 29
  • 1
  • 6
  • `Wisper.clear` is only for use in test environment. It will clear all global subscribers. I suspect the reason it isn't working on Heroku is that the app is running in production environment. – Kris May 11 '16 at 14:30
  • Global listeners are unsuitable for cases where you want the subscription to last for just the duration of a request. One option is temporary global listeners, these are threadsafe, so should work. But better still is to subscribe to an instance of a publisher inside the controller action. If you you show your publisher code I can help better. – Kris May 11 '16 at 14:35
  • Thanks for the fast response Kris. I have updated my question above, and to reiterate my use case is to subscribe across all find for ActiveRecord models across all controller actions. Would you recommend a TemporaryGlobalListener with a around_action block in ApplicationController instead? – bugzpodder May 11 '16 at 17:33
  • I can't, at the moment, see any way to do what you want because you want to subscribe a listener initialized with local data (`current_user`) to an event which is published from a global (`MyPublisher` class). – Kris May 12 '16 at 08:27

1 Answers1

0

You can subscribe your listener globally for the duration of a block:

def show
  Wisper.subscribe(MyListener.new(current_user)) do
    @model = MyPublisher.find(id)
  end
end

The listener will be unsubscribed when the block is finished.

If you wanted it to happen for more than one action you could use an around_action filter:

around_action :subscribe_listener

def show
  @model = MyPublisher.find(id)
end

def create
  # ...
end

# etc.

private

def subscribe_listener
  Wisper.subscribe(MyListener.new(current_user)) do
    yield
  end
end
Kris
  • 19,188
  • 9
  • 91
  • 111