Back to Blog

Get in Touch

Temporarily disable ActiveRecord callbacks

By Brent Collier March 12, 2009 in ActiveRecord, callbacks

Missing

ActiveRecord callbacks can be super-handy, but every once in a while, they get in the way.

I was recently working on a client project and I had to create a rake task to import a large set of data from a spreadsheet.  One of the models that was being imported had an after_save callback that sent out an email notification.  I didn't really want 3500 emails to be sent out whenever this rake task was ran, so I needed to disable the callback while the import task was running.

Fortunately, this is easy to do.

Say you've got a model like so...

class Thing < ActiveRecord::Base
  before_save :do_stuff  
  def do_stuff
    raise "This thing is doing stuff..."
  end
end

In the config/initializers directory, I created a file called extensions.rb.  You can call it whatever you like.  I chose 'extensions' because I use the same file for any minor Ruby or Rails class extensions.  In it, I put this...

class ActiveRecord::Base
  def self.without_callback(callback, &block)
    method = self.send(:instance_method, callback)
    self.send(:remove_method, callback)
    self.send(:define_method, callback) {true}
    yield
    self.send(:remove_method, callback)
    self.send(:define_method, callback, method)
  end
end 

What this does is grab the callback and store it in the method variable.  Remember, this code only circumvents the 'do_stuff' method, not anything declared as a before_save callback.  It then redefines the method to merely return true, yields to the block, and then redifines the the method with the contents of the method variable. 

Yielding to the block allows you to not have to worry about maintaining the method contents, or restoring it once you're done.  Also, if you were so inclined, you could easily modify this code to accept an array of method names and disable all of them

Once the extension is in place, you can do this...

Thing.without_callback(:do_stuff) do
  thing = Thing.new
  thing.save
end

...without 'do_stuff' ever being called.

Some people will probably argue that if you need to disable your callback, then your model should be defined differently, but I disagree.  I think this is perfectly acceptable in many cases.  Granted you probably wouldn't want to do this all throughout your application code, but I see no problem with using this technique in a test, or data migration, or in my case, a rake task.

Medium

Brent Collier

Brent spent several years working on J2EE projects doing mostly server-side development for large clients in the financial sector. After leaving the enterprise world for a mobile startup, a coworker turned him on to Ruby on Rails. With this newfound interest, Brent went on to co-found and develop Yappd, a microblogging social network built on Rails. He has since continued to hone his Rails skills, concentrating on social content and collaboration. As a Senior Engineer, Brent employs agile methodologies whenever possible and strives to produce simple and effective solutions.

More posts by Brent Collier

Brent Collier

I tell myself that I write JS every day, but I don’t. I write jQuery. Don...

Brent Collier

As a software engineer, I'm like an over-caffeinated monkey for two reason...

Brent Collier

I recently spent a day hanging out with a few of the guys at Viget Labs hacki...