Rails 1.2 added native support for fixed decimal variables using the BigDecimal class. This is great news for anyone who needed to deal with currency in their Rails applications because it means we can move away from storing fixed decimal values as integers in the database.

But wouldn't it be nice if you could automagically migrate your existing fixed decimal columns to use the new-fangled decimal type?

I sure thought it would be, so I wrote a couple of migration helper methods so that I could write a one-liner in a migration file to convert a column from fixed decimal as integer to a real decimal column. All the gory details are available after the jump.

I apologize if reading this post made your eyes glaze over.

Here is a usage example from a migration script I wrote. It's pretty straightforward, you just feed the method the model it's working with, the existing column name, and the new column name.

require "migration_helpers"

class ChangePriceColumnsToDecimal < ActiveRecord::Migration
  extend MigrationHelpers

  def self.up
    decimalize_column(Item, :price_in_cents, :price)
  end

  def self.down
    undecimalize_column(Item, :price_in_cents, :price)
  end
end

And here are the actual helper methods. These should be placed in lib/migration_helpers.rb for your app. (Create the file if you don't have one already.)

module MigrationHelpers
  def decimalize_column(model_name, old_column, new_column)
    rename_column model_name.table_name, old_column, new_column
    change_column model_name.table_name, new_column, :decimal, :precision => 8, :scale => 2
    model_name.reset_column_information

    model_name.find(:all).each do |row|
      unless row[new_column] == nil
        row.update_attribute new_column, row[new_column] / 100
      end
    end
  end

  def undecimalize_column(model_name, old_column, new_column)
    model_name.find(:all).each do |row|
      unless row[new_column] == nil
        row.update_attribute new_column, row[new_column] * 100
      end
    end

    change_column model_name.table_name, new_column, :integer
    rename_column model_name.table_name, new_column, old_column
  end
end

Hope someone else finds this helpful.

1 Comment

I see that you have foolishly allowed comments on this article. I shall begin abusing that privilege forthwith.

Leave a Comment

back to top

micro theme by seaofclouds, and powered with Mephisto