Skipping ActiveRecord Callback Methods

ActiveRecord provides a great way to tie into a model’s lifecycle through various callbacks. However, in certain cases you may want to skip specific callbacks you’ve defined on a model similar to skipping filters in ActionController. Rails doesn’t support this natively, but its very easy to extend ActiveRecord to add this feature. The solution we ended up using was inspired by this post on thatswhatimtalkingabout.org.

Frist, we’ll need to open up ActiveRecord and add the method that will allow us to skip the callback. There are a few ways you can go about doing this, but we use an initializer. So create a file in config/initializers called active_record_extensions.rb with the following code:

class ActiveRecord::Base
  def self.skip_callback(callback, &block)
    method = instance_method(callback)
    remove_method(callback) if respond_to?(callback)
    define_method(callback){ true }
    yield
    remove_method(callback)
    define_method(callback, method)
  end
end

All this code does is store off the callback method, yields the passed in block and then adds the callback back in.

So, lets say we have the following model…

class User < ActiveRecord::Base
  after_create :send_notification
end

You may have some process that is creating Users where you don't want the notification to be sent after the user gets created. In order to do this, simply wrap your create or save in the skip_callback method like so:

User.skip_callback(:send_notification) do
  User.create(:name => 'Josh')
end

Now you've successfully created a User without the send_notification callback getting executed.

6 thoughts on “Skipping ActiveRecord Callback Methods

  1. Thank you for this tip! But: Your skip_callback method does not handle the result of the yielded block. If have fixed it with this code:

      def self.skip_callback(callback, &block)
        method = instance_method(callback)
        remove_method(callback) if respond_to?(callback)
        define_method(callback){ true }
        result = yield
        remove_method(callback)
        define_method(callback, method)
        result
      end
    
  2. One more addition: Bad things happen if an exception is raised in the yielded block – the former callback is not restored. To avoid this I have changed the method like this:

      def self.skip_callback(callback, &block)
        method = instance_method(callback)
        remove_method(callback) if respond_to?(callback)
        define_method(callback){ true }
        begin
          result = yield
        ensure
          # Always redefine the old callback, even if
          # there were exceptions raised in the yielded block
          remove_method(callback)
          define_method(callback, method)
        end
        result
      end
    
  3. What i find even more interesting is that it works even it the callback itself.

    class Person < ActiveRecord::Base
     after_save :create_another
    
     def create_another
      Person.skip_callback("create_brother") do
       Person.create(:name => "peter")
      end
     end
    end
    
    Person.create :name => "ronald"
    This code will not explode, even though the method removes itself and then add itself back (gives me headache).