Hooks Got Better.

The “hooks”:https://github.com/apotonick/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.

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

h3. Stop It!

My friend “Fred Wu”:https://github.com/fredwu/datamappify 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.

h3. 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 thoughts on “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. 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

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s