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,

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.

Tags: ,

9 Responses to “Hooks and callbacks for Ruby. But simple.”

  1. Thanks for making this dead simple! Hopefully this will lower the bar to make libraries more extensible!

  2. Nice stuff! Improves semantics and understanding, though it feels a bit clumsy.

  3. It’s nice to see projects like this. I’ve been thinking about looking into building hooks-like gem, but I’m so glad you did the work for me!

  4. Andrius Chamentauskas

    Is ability to pass params the only thing that is better than is ActiveSupport callbacks?

  5. nick

    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.

  6. LucaB

    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.

  7. Michał

    @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

  8. nick

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

  9. nick

    We’re featured in a TV show here (min 28:20) – check out the show, it’s quite amusing.

Leave a Reply