Yehuda Katz is a member of the Ruby on Rails core team, and lead developer of the Merb project. He is a member of the jQuery Core Team, and a core contributor to DataMapper. He contributes to many open source projects, like Rubinius and Johnson, and works on some he created himself, like Thor.
Callbacks, Redux
January 26th, 2009
First off, apologies for the long delay in posting. Last week, I was giving a training down at Integrum in Phoenix. It was a nice three-day training that starting with building a Merb application, and ended with a walk through the BootLoader and Request path. All in all, a very successful endeavor, but definitely one that took up quite a bit of time (and sadly left me with no time for blogging).
At the end of last week, I continued my work on callbacks, which was actually pretty interesting. The initial approach, if you recall, compiled before, around, and after callbacks, with conditions, into a method that could be called. This effectively unrolled the loop and a number of checks into a nice hardcoded method that was about 10x faster than the previous technique. Take a look at my first post on this topic for more.
When I started to look at integrating it with ActionPack, I realized that the API there is a bit different, in an interesting way. In addition to allowing the typical if/unless conditions, ActionController filters allow you to supply :only/:except conditions. At first glance, these compile as follows:
before_filter :authenticate, :except => :index # to dispatch_callback :before, :authenticate, :unless => proc {|c| c.action_name == :index }
However, this would force the check every time, even though these conditions are always the same, for each action. In other words, every trip through index will run exactly the same set of callbacks (as for as :only/:except conditions are concerned). I could have hardcoded this optimization into ActionPack’s usage of the ActiveSupport callbacks, but I decided instead to make it generic.
After thinking about the problem for a couple of days, I realized that it reduces down to “compile a specialized method for these callbacks, per key.” In this case, the “key” is the action name. Now, instead of having the AP code call run_dispatch_callbacks { ... stuff ... }, it calls run_dispatch_callbacks(action_name) { ... stuff ... }. Every time the callbacks are run with a new key, it compiles a version of the method for that key.
The last piece of the puzzle is in specifying these conditions. Now:
before_filter :authenticate, :except => :index # compiles to dispatch_callback :before, :authenticate, :per_key => {:unless => proc {|c| c.action_name == :index } }
Per-key options are calculated when the method is compiled, while regular options are compiled into the method. Of course, the vagaries of the compiler are a bit messy, but I plan to clean it up, document it, and integrate it in the next few days.
Tomorrow, I fly out to Chicago to hack with David, Josh, and Rick. Wish us luck!

Gaspard Bucher, Posted January 26, 2009, 8:37 am
Oh boy… can’t wait to see what you come up with in Chicago.
How’s the work planned ? Everyone works on his side and the, eventually, things get merged back into rails ? Or is it that each one has an area to work on ?
Good luck anyway !
Gaspard
Soleone, Posted January 26, 2009, 12:31 pm
Awesome, thanks for all the optimization work! And also for writing about it, it’s really quite interesting.
Jason Newlin, Posted January 27, 2009, 5:55 pm
Yehuda, it was real nice to meet you at the Merb Class. I really learned a lot, you are one smart guy. Thanks for coming down to Integrum and schooling everyone.