What Active Record callbacks are registered on some model?

22

May, 2017

Zoran Žabčić

You have just joined as a developer on a bigger project, or you're integrating your app with some rails engine, or you're doing something else that involves callbacks. Anyway, no matter if you love them or hate them, they are part of the application and getting to know and understand them would be a wise thing to do.

Active Record callbacks are hooks into object’s life cycle giving you an ability to execute particular code at certain moments. The most popular (read: those you first meet as a rails programmer) are probably before_save and after_save callbacks, but there are nineteen callbacks in total, which give you a lot of power to do stuff.

Although callbacks are easy to use, the important thing to know is that they are just a tool. Overusing callbacks could quickly lead to a complicated, hard to test, unmaintainable code, so use them responsibly.

Listing Active Record Callbacks

First, let’s create a new rails app so we can try some things. I called mine callback_app :). Move to your app root directory and run rails console. In a console, by typing:

we get back a list of nineteen available Active Record callbacks which also have a corresponding callback macro method. It is also possible for you to define and register a custom callbacks using the define_model_callbacks method, but that’s beyond the scope of this article.

In this scenario when the save method is called on Manager instance, both :do_something_with_person and :do_something_with_manager are triggered.

Considering all this, how to find out which callbacks are registered on a particular object and in which order are they executed?

Debugging Active Record callbacks order

Callbacks are run in the order they are defined, with the exception of callbacks defined as methods on the model, which are called last. Another exception is Transaction Callbacks after_commit and after_rollback which are executed in reverse order.

Let’s create a Post model in our app and register some callbacks on it. To make things faster, we’ll use rails model generator.

What we did here is we registered callbacks using callback macros (before_save, around_save, and after_save) with method references (:before_save_method_1, :before_save_method_2, around_save_method_1, etc.). We defined our callbacks as private methods at the bottom. Before and after callback methods just print out method name: puts __method__, and around callback methods print out method name with suffix IN, yield block and then print out the method name again, but this time with suffix OUT.

Back to the console! We can check which callbacks are registered on our class by calling _*_callbacks method on it. Just calling the _save_callbacks method on the Post will return an array of ActiveSupport::Callbacks::Callback objects, but we are only interested in which methods are run in the save callback chain for now, so we are going to filter them out:

Seems like the order is wrong, :after_save_method_1 is defined before :after_save_method_2, therefore, it should be run before :after_save_method_2? Let’s do another test, we can manually run save callbacks:

Ok, we can confirm that all callbacks are executed in correct order and :after_save_method_1 is run before :after_save_method_2.

What the heck is going on here? Well, the trick is in the run_callbacks method. It calls the before and around callbacks in the order they were set, yields the block (if any), and then runs the after callbacks in reverse order.

Finally, the interesting thing about around filters is that they are nested:

Conclusion

When you have a simple model with just a few or none registered callbacks it’s pretty much easy to spot which callbacks are registered on it and understand how it works. On the other hand, when you have a much more complicated situation, let’s say, you inherited a project from another developer/company with a lot of registered callbacks, defined associations, included gems that add their own callbacks, it can become very messy to understand what’s going on in an application. Knowing which callbacks and in which order are executed would be very useful.

I wrote a simple gem cb_list which is basically a rake task that prints out all registered callbacks for a given class. It will help you debugging Active Record callbacks.

Let's say we are integrating some CMS with Spree (Solidus) and Spree::Product is accessible from both sides. If we want to see which callbacks are registered on it, we can run rake task provided by cb_list gem: