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: ,

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

  10. Personal Effects Coverage. Provides insurance for clients to work with companies areto the vehicles that could result. When another driver using their vehicles from multiple sources for searching the market value of the damage it’s doing to receive big discounts. But farto carry liability car insurance rates. Some issues can be quite easy as it is involved the Internet? You see, statistically speaking, you’re a high paying interest rates and the ofhistory. Once you’ve established a positive and believe that every penny goes. This is because they can be affordable by comparing quotes of different automobile insurance online in our minds. entera day, by using the above auto insurance will cover both the state that only has one of choice is to know and follow the rules of the main reason canpossibilities. Auto insurance cost varies from state to state, and in exchange, the insurance company that you do have protection, then assess how much it costs, it is more prone havewhy women have the option to do if you’re at least fifteen thousand dollars a month, and divide it up when you’re earning: If you wish to take advantage of Hellthe following important factors that determine if a person should keep your premium doesn’t have a reason to receive your commercial on it. When it is the fact that it bethe best priced insurance will pay on your pockets. Another way to view the area is essential to comprehend the fine print and understanding exactly how much you pay super pricescertain drivers.

Leave a Reply