Hooks and callbacks for Ruby. But simple.

Callbacks are executable things, like a Proc or methods in Ruby, whereas a hook is usually a spot in your code where you want to execute a certain subset of callbacks.

Or, precisely, callbacks that are associated to the hook.

I have lots of these places throughout my libraries, and after playing around with ActiveSupport::Callbacks (both 2.3 and 3.0) I wrote a minimal gem to do exactly that:

Declaratively define hooks, add callbacks and run them with the options you like.

Let’s see how it works. First, install hooks.

$ gem install hooks

Using hooks

Usually you’d define hooks declaratively in your class.

class Coder
  include Hooks
  define_hook :after_work

You’re now ready to add callbacks, as your class now has a Class.after_work method. Nothing more.

class Coder
  after_work do
    puts "Yeah!"

Instance methods are also callback’able.

  after_work :have_a_beer
  def have_a_beer
    puts "One for me, one for Michał!"

Now, run a hook somewhere in your code.

  def finish
    run_hook :after_work

The callbacks will be invoked in the order you added them. Sweet.

Running a hook with arguments

If you need objects in your callbacks, just pass ’em.

    run_hook :at_work, self, Time.now

And be sure to equip your callbacks with the right parameters.

  at_work do |who, when| ... end


So, basically a filter as found in Rails controllers is a hook. You – the user – use Controller.after_login to add callbacks. And somewhere in the Rails core, there’d be a call to

  run_hook :after_login, self

…if they’d be using hooks.


9 thoughts on “Hooks and callbacks for Ruby. But simple.

  1. Hey Andrius,

    from the functional point of view, passing params is the only advantage. ActiveSupport::Callbacks is even “better” as it allows more types of callbacks (strings that are evaluated and more bullshit).

    However, the AS module is extremely complex, full of method compilations and is overally hard to read, to maintain and to understand. It’s getting even worse in the 3.0 version.

    Hooks by contrast is stupid simple and will stay stupid simple.


  2. You are absolutely right about the extreme complexity of callbacks in rails 3. Over there reside some behemot-methods made of evaluated strings.

    Greg Pollack says that the heavy use of method compilation particularly in the callbacks, was chosen for better performance when compared with method_missing.

    Anyhow, it is difficult to me to believe that it could be significantly faster than define_method at the singleton class level, in addition to be significantly less readable. Jay Field wrote a really interesting piece about the “meta_def” method.

    I was really intrigued by this topic, and when I found a question on stackoverflow, I tried to answer with a simple implementation of the pattern:

    So I am really pleased that you made this gem, I am stunned by the simplicity of the code, especially if compared with its functionality.

    Don’t get me wrong, Rails is a damned piece of genius’ work, but your work demonstrates that maybe one day its internals will be again more approachable for the non-guru guys.


  3. @LucaB your’re totally right. Rails core team seems to get rid of clear code for micro speed improvements. It’s ridiculous, especially in ruby world.

    @Andirus hooks technique is way more powerful than callbacks, but less convenient. In most cases hooks are not called automatically like callbacks. I would say than Nick has created something between closures and callbacks


  4. @LucaB: Good point! Heavy usage of method compilation as well as method_missing is WRONG. There is no excuse for unmaintainable code, even the performance argument is ridiculous. I mean, we are talking about “nano-seconds” here 😉

    So, I prefer a good architecture and simple code in exchange for having to buy a faster server. Or a cloud. Or more RAM. Or whatever.


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