For when you need that little bit of extra accuracy, specifying precision and scale for a decimal column in your Ruby on Rails migration is pretty simple. The precision represents the total number of digits in the number, whereas scale represents the number of digits following the decimal point. To specify the precision and scale, simply pass those as options to your column definition.

For example:
class AddLatLngToAddresses < ActiveRecord::Migration
  def self.up
    add_column :addresses, :lat, :decimal, :precision => 15, :scale => 10
    add_column :addresses, :lng, :decimal, :precision => 15, :scale => 10
  end

  def self.down
    remove_column :addresses, :lat
    remove_column :addresses, :lng
  end
end

This will allow you to have 10 digits after the decimal point and 15 digits max.

One thing to note, however is that Rails will use BigDecimal as the type for the column. BigDecimal provides support for very large or very accurate floating point numbers. Remember those pesky floating point imprecision errors?

>> 1.2 - 1.0 == 0.2
=> false

Yep, BigDecimal handles that…

>> BigDecimal.new('1.2') - BigDecimal.new('1.0') == BigDecimal.new('0.2')
=> true

So now, go forth and be accurate.

Also see

I was recently working on a project using edge rails and saw that it supported something like what used to be called sexy migrations. Basically, you can make your migration files look a whole lot cleaner.

Before
class CreateUsers < ActiveRecord::Migration
  def self.up
    create_table :users do |t|
      t.column :first_name, string
      t.column :last_name, string
      t.column :birthday, date
      t.column :created_at, datetime
      t.column :updated_at, datetime
    end
  end

  def self.down
    drop_table :users
  end
end
After
class CreateUsers < ActiveRecord::Migration
  def self.up
    create_table :users do |t|
      t.string      :first_name, :last_name
      t.date        :birthday
      t.timestamps
    end
  end

  def self.down
    drop_table :users
  end
end

Looks much better doesn’t it? For what I was doing though, I wanted to use the acts_as_paranoid functionality to never delete anything. The acts_as_paranoid plugin updates the row with a deleted_at timestamp and then filters out “deleted” ones whenever it queries the database. Of course, I could just add t.datetime :deleted_at to each table. But instead of going back to the old way of doing things, I simply extended the built in timestamps functionality by placing the following in a file in the lib directory and then requiring it in the environment.

lib/custom_schema_definitions.rb
module ActiveRecord
  module ConnectionAdapters #:nodoc:
    class TableDefinition

      # Adds a deleted_at column when timestamps is called from a migration.
      def timestamps_with_deleted_at
        timestamps_without_deleted_at
        column(:deleted_at, :datetime)
      end

      alias_method_chain :timestamps, :deleted_at
    end
  end
end
config/environment.rb (add)
require "custom_schema_definitions"

Now, we can run our migration as we did before and the t.timestamps line will also give us a deleted_at column. Share your paranoid ramblings in the comments.

Note: I believe this only works in Edge Rails for the moment as the new migrations and alias_method_chain are not present in the latest stable version of Rails.