37
votes

I've been going through the rails source for a while now, and I don't think there's a better way of getting the list of all callbacks other than: ActiveRecord::Callbacks::CALLBACKS – which is a constant list.

Meaning if you're using a gem like devise_invitable that adds a new callback called :invitation_accepted with the score :after and :before then ActiveRecord::Callbacks::CALLBACKS will not work.

Do you know of an easy fix, other than opening up rails modules and making sure there's an internal list of call-backs per model class?

6

6 Answers

50
votes

You can call Model._save_callbacks to get a list of all callbacks on save. You can then filter it down to what kind you need e.g. :before or :after like this:

Model._save_callbacks.select {|cb| cb.kind == :before}

Works the same for Model._destroy_callbacks etc.

20
votes

The docs say "There are nineteen callbacks in total"... but they don't seem to say what all of those 19 actually are!

For those who Googled "list of all ActiveRecord callbacks" like I did, here's the list (found by using ActiveRecord::Callbacks::CALLBACKS as described in the question):

:after_initialize
:after_find
:after_touch
:before_validation
:after_validation
:before_save
:around_save
:after_save
:before_create
:around_create
:after_create
:before_update
:around_update
:after_update
:before_destroy
:around_destroy
:after_destroy
:after_commit
:after_rollback
3
votes

Note that if you simply want to trigger callbacks, you can use the #run_callbacks(kind) method:

o = Order.find 123 # Created with SQL
o.run_callbacks(:create)
o.run_callbacks(:save)
o.run_callbacks(:commit)
3
votes

If you're working in a Rails version prior to the ._save_callbacks method, you can use the following:

# list of callback_chain methods that return a CallbackChain
Model.methods.select { |m| m.to_s.include? "callback" }.sort

# get all methods in specific call back chain, like after_save
Model.after_save_callback_chain.collect(&:method)
0
votes

I am going to propose most universal solution.

It works even when gems are declaring custom callbacks e.g. paranoia and after_real_destroy

To list all callbacks

Model.methods.select { |m| m.to_s.include? "callback" }.sort

Then you can get given callbacks if you type method name e.g.

Model._update_callbacks
Model._real_destroy_callbacks
Model._destroy_callbacks

If you list all callbacks, then you can find callback you need by checking @filter instance variable e.g.

require 'pp'
Activity._destroy_callbacks.each_with_index { |clbk,index| puts "#{index}-------\n#{clbk.pretty_inspect}" } ; nil 

# [...]

#<ActiveSupport::Callbacks::Callback:0x00007ff14ee7a968
 @chain_config=
  {:scope=>[:kind, :name],
   :terminator=>
    #<Proc:0x00007ff13fb825f8@/Users/mypc/.rbenv/versions/2.3.7/lib/ruby/gems/2.3.0/gems/activemodel-4.1.16/lib/active_model/callbacks.rb:103 (lambda)>,
   :skip_after_callbacks_if_terminated=>true},
 @filter=
  #<Proc:0x00007ff14ee7ac10@/Users/mypc/.rbenv/versions/2.3.7/lib/ruby/gems/2.3.0/gems/activerecord-4.1.16/lib/active_record/associations/builder/association.rb:135 (lambda)>,
 @if=[],
 @key=70337193825800,
 @kind=:before,
 @name=:destroy,
 @unless=[]>
4-------
#<ActiveSupport::Callbacks::Callback:0x00007ff14ee3a228
 @chain_config=
  {:scope=>[:kind, :name],
   :terminator=>
    #<Proc:0x00007ff13fb825f8@/Users/mypc/.rbenv/versions/2.3.7/lib/ruby/gems/2.3.0/gems/activemodel-4.1.16/lib/active_model/callbacks.rb:103 (lambda)>,
   :skip_after_callbacks_if_terminated=>true},
 @filter=:audit_destroy,
 @if=[],
 @key=:audit_destroy,
 @kind=:before,
 @name=:destroy,
 @unless=[]>
5-------
0
votes

For after_commit callbacks, call Model._commit_callbacks.

Be mindful, however, that there's a known bug in Rails (still present in Rails 5.2.2) that after_commit callbacks are not run in the order they are declared in the model, even tough they appear in the correct order in that _commit_callbacks call.

More info: Execution order of multiple after_commit callbacks (Rails) and https://github.com/rails/rails/issues/20911