The hooks gem is a very simple way to add callbacks to your objects. It’s less complex than ActiveSupport::Callbacks and allows passing arbitrary data to the callbacks. That’s 3x callbacks in one paragraph.

class Uploader
  include Hooks
  define_hook :after_save
  def save
    run_hook(:after_save, @file)

It works by defining a hook (line 4) and calling it wherever you need it (line 8). Note that you can pass arguments to #run_hook which propagates those to the callbacks.

Adding Callbacks.

Now a hook is useless without callbacks.

class Uploader
  # ..
  after_save :check
  after_save { |file| file.compress }
  after_save { |file| file.resize }
  def check(file)

Whenever you run the :after_save hook, it will execute #check, then the two blocks in the order they were added. So far so good.

Stop It!

My friend Fred Wu had the idea to allow halting the callback chain. This might be handy in our case when the check detects a poor file.

  def check(file)
    valid_upload?(file) # returns true or false.

In order to tell hooks to stop executing further callbacks we need to configure our hook.

  define_hook :after_save, halts_on_falsey: true

This will – surprisingly – suppress execution of the two callbacks when #check returns a falsey value.

Return Values Do Matter.

Another nice feature is that #run_hook returns a list of return values from the callbacks.

results = run_hook(:after_save, @file) 
#=> [true, 1024.kb, "200x300"]

Extremely useful if you don’t want to store callback results in a global (brrr) variable. And, just to maximize awesomeness, the results know if the chain was halted or not.

results.halted? #=> false

This gives you a flexible way for an orthogonal callback architecture with a very clean implementation.

Party on, Wayne!

2 Responses to “Hooks Got Better.”

  1. Why just not implement regular methods Doing Their Jobs ™? Hooks for me make code harder to read, they hide, what is really workflow. You might treat them as kind of observer, while observer and observable are the same object. But anyway they seem as code smell to me.

  2. nick

    Artur: Hooks can be easily documented in the providing class and extended by subclasses without any knowledge about the implementation.

    Overriding “Regular Methods™” works as well, but you need to call super at the right point, you cannot change the order how they’re processed and you cannot halt execution.

Leave a Reply