Wisper: отмена подписки GlobalListeners между запросами

Я хочу зарегистрировать глобальный слушатель в моем ApplicationController, который содержит current_user. Я закончил тем, что попробовал это:

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

Однако, когда я развернул этот код на heroku, эти глобальные слушатели никогда не отписываются, и приложение продолжает накапливать слушателей через запросы. Я не могу рассчитывать на after_action, поскольку приложение может завершиться из-за ошибки. Как правильно сделать это, это принудительно очистить перед подпиской, вот так?

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

В другом вопросе Крис предложил использовать инициализатор, который подписывается один раз. Причина, по которой я этого не делаю, заключается в том, что я хочу получить доступ к current_user и предпочитаю не передавать его через глобальные переменные /Thread.current. Каков наилучший способ заставить GlobalListeners работать с current_user?

Мой пример использования - обработка всех экземпляров моделей ActiveRecord, загруженных current_user, во всех действиях контроллера. Виспер делает именно то , что мне было нужно, за исключением упомянутой проблемы.

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

и для слушателя:

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

  def process_find
    ...
  end
end

1 ответ

Решение

Вы можете подписать своего слушателя глобально на время блока:

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

Слушатель будет отписан по окончании блока.

Если вы хотите, чтобы это произошло более чем за одно действие, вы можете использовать around_action фильтр:

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
Другие вопросы по тегам