Rails Tip: Precision and scale for decimals
April 11th, 2008
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
Migration Timestamps deleted_at Magic Field
July 3rd, 2007
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.
Beforeclass 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.
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.