The project I’m working on will be using quite a few currency values. Because the framework maps decimal columns to (imprecise) floats, the Rails book recommends storing money as integers. I’ve been trying to work out a way to do this and use the fields directly in forms. Another reason for using integers is that I’m trying to represent the database schema entirely in migrations.
For a background on aggregation see UnderstandingAggregation. Also, TobiasLuetke has released a Money class for just such a purpose.
The closest example I could find was for LocalizingDates, so I’ve based my solution on that. I’ve been hacking about with the Depot application as an experiment, and this is what I came up with.
First of all, in the products table, I replace the price decimal(10,2) field with price_cents integer.
I add a Money class, inside lib/money.rb. This also includes an extension to the String class, similar to the LocalizingDates example:
class Money
attr_reader :cents
def initialize(cents)
@cents = cents
end
def to_s
sprintf("%0.2f", @cents / 100.0)
end
end
class String
def cents
Integer(sprintf("%0.0f", self.to_f * 100.0))
end
end
Then the Product model (model/product.rb) can be adjusted to work with this shadow-class.
class Product < ActiveRecord::Base
require 'money'
validates_presence_of :title, :description, :image_url
validates_numericality_of :price
composed_of :price, :class_name => Money,
:mapping => [:price_cents, :cents]
private #Need this for v_n_o to work!
def price_before_type_cast
self.price
end
end
Now, the price attribute can be used as normal in templates like this …
<td><%=h product.price %></td>
… or this …
<%= text_field 'product', 'price' %>
I have no doubt this can be massively improved upon, but I’ve only been experimenting with RoR for the last week or so and couldn’t find any other examples. I think this especially needs some methods/operators on the Money class and some range checking.
Another way: http://blog.codahale.com/2006/05/18/dollars_and_cents-a-rails-plugin/ it’s a plugin that does the same thing