Random Ramblings

There are a few times, not many, when you really don't want to trigger AR callbacks on a save. Take the following simple example:

class Article < ActiveRecord::Base
  include ActionView::Helpers::SanitizeHelper
  before_save :sanitize_body_text

  def sanitize_body_text
     self.body_text = sanitize(self.body_text)
  end
end

article.update_attribute(:posted_at, Time.now - 100)

You probably don't want to fire up an expensive method like sanitize when updating the posted_at time.

save_without_callbacks solves that problem.

To Install

./script/plugin install https://tangofoxtrot.com/svn/plugins/save_without_callbacks

On any AR.object that you want to skip the callbacks for:

@something.skip_callbacks = true

*note you cannot set this on mass assignment*

Then call:

@something.save

This plugin can be potentially dangerous! Make sure this is the behavior you want.

For example acts_as_list:

before_create  :add_to_list_bottom
before_destroy :remove_from_list

def remove_from_list
  if in_list?
    decrement_positions_on_lower_items
    update_attribute position_column, nil
  end
end

If you set skip_callbacks to true and updated the position or created/destroyed an object the positions will be off.

This is best used in a situation where you are updating some arbitrary, non relationship, data.

If you are new to rails you've probably seen the method alias_method_chain but dont know exactly what it does. Here is simplified example.

require 'rubygems'
require 'active_support'

class Breakfast
  def cereal
    "Lucky Charms"
  end
end

Produces:
irb(main):003:0> Breakfast.new.cereal
=> "Lucky Charms"

Nothing exotic here. Just a breakfast class with one method. Lets bring in alias_method_chain.

For that we will need to have a method to 'alias to'.

module Milk
  
  def self.included(base)
    base.alias_method_chain :cereal, :milk
  end
  
  def cereal_with_milk
    "Milk and #{cereal_without_milk}"
  end
  
end

The previous code does the following:

  1. Creates an alias (think shortcut) to the method 'cereal' with the name 'cereal_without_milk'
  2. Creates another alias for cereal_with_milk with the name 'cereal'
  3. Defines the method cereal_with_milk

Now when we call the method breakfast.cereal our newly created action, 'cereal_with_milk' will be called. The code in action:

irb(main):002:0> Breakfast.send(:include, Milk)
=> Breakfast
irb(main):003:0> Breakfast.new.cereal
=> "Milk and Lucky Charms"

The end result, we have milk with our Lucky Charms.

Download the original Code: alias_method_chain_example.rb