Tag Archives: Callback

Whisper Ruby

When using callbacks in your Ruby objects, there are more than a few ways of doing it. Recently I stumbled into Wisper gem that abstracts details away in a nice manner. Best do some code comparison.

Previously, I might be writing something like this:

class Worker
  def do_some_hard_work
    status = :should_be_sleeping_like_a_log
    notify_listeners(:on_work_done, status)
  end

  def add_listener(listener)
    (@listeners ||= []) << listener
  end

  def notify_listeners(event_name, *args)
    @listeners && @listeners.each do |listener|
      if listener.respond_to?(event_name)
        listener.public_send(event_name, self, *args)
      end
    end
  end
end

class Owner
  def set_things_in_motion
    worker = Worker.new
    worker.add_listener(WorkerDisplay.new)
    worker.do_some_hard_work
  end
end

class WorkerDisplay
  def on_work_done(worker, status)
    display_worker_status(worker, status)
  end
end

Here you have a worker class that has the ability to register listeners and to trigger appropriate events on them, if they respond to the given event. Not too big of a footprint but it does sort of pollute the domain a bit. I guess things could be extracted to a module but let’s try it with Wisper now:

class Worker
  include Wisper

  def do_some_hard_work
   status = :should_be_sleeping_like_a_log
   publish(:on_work_done, self, status)
  end
end

class Owner
  def set_things_in_motion
    worker = Worker.new
    worker.subscribe(WorkerDisplay.new)
    worker.do_some_hard_work
  end
end

class WorkerDisplay
  def on_work_done(worker, status)
    display_worker_status(worker, status)
  end
end

So, most of the code didn’t change, but the Worker class did benefit from a more clearly revealed intent. A small one, but still a win. Best thing is, you can use the async Wisper gem extension and turn your listener into a Celluloid Actor.

class Owner
  def set_things_in_motion
    ...
    worker.subscribe(WorkerDisplay.new, async: true)
    ...
  end
end

There are other nice features like global listeners, mapping subscription to a different method or  subscription chaining, so if you’re interested go ahead and read the project’s readme.

Tagged ,