What is the best method of handling currency/money?
You'll probably want to use a DECIMAL
type in your database. In your migration, do something like this:
# precision is the total number of digits# scale is the number of digits to the right of the decimal pointadd_column :items, :price, :decimal, :precision => 8, :scale => 2
In Rails, the :decimal
type is returned as BigDecimal
, which is great for price calculation.
If you insist on using integers, you will have to manually convert to and from BigDecimal
s everywhere, which will probably just become a pain.
As pointed out by mcl, to print the price, use:
number_to_currency(price, :unit => "€")#=> €1,234.01
Here's a fine, simple approach that leverages composed_of
(part of ActiveRecord, using the ValueObject pattern) and the Money gem
You'll need
- The Money gem (version 4.1.0)
- A model, for example
Product
- An
integer
column in your model (and database), for example:price
Write this in your product.rb
file:
class Product > ActiveRecord::Base composed_of :price, :class_name => 'Money', :mapping => %w(price cents), :converter => Proc.new { |value| Money.new(value) } # ...
What you'll get:
- Without any extra changes, all of your forms will show dollars and cents, but the internal representation is still just cents. The forms will accept values like "$12,034.95" and convert it for you. There's no need to add extra handlers or attributes to your model, or helpers in your view.
product.price = "$12.00"
automatically converts to the Money classproduct.price.to_s
displays a decimal formatted number ("1234.00")product.price.format
displays a properly formatted string for the currency- If you need to send cents (to a payment gateway that wants pennies),
product.price.cents.to_s
- Currency conversion for free
Common practice for handling currency is to use decimal type.Here is a simple example from "Agile Web Development with Rails"
add_column :products, :price, :decimal, :precision => 8, :scale => 2
This will allow you to handle prices from -999,999.99 to 999,999.99
You may also want to include a validation in your items like
def validate errors.add(:price, "should be at least 0.01") if price.nil? || price < 0.01 end
to sanity-check your values.