I have just switched over to using the money and money-rails gems in a recent rails 3.2 app recently after having issues with the formatting and storing of currency values in a Mongoid db. Plus, the money-rails gems comes with some nifty view helpers to transform the values into suitable representations on the front end.
Below are some notes to remind myself how I managed to do this within a Rails 3.2 appli<codebe foRedefine the field within the mongoid document to be of type Money. e.g. Assuming we have a Product document with a field of price, we can define it as follows:
nufield :price, type: Money
Once defined the currency values will be stored as a hash in the document with the following structure:
price: {"cents" => "100", "currency_iso" => "GBP"}
The price value is converted into cents automatically by the gem e.g. 1 USD gets converted into 100 cents. The currency_iso sets the country code of the currency according to the iso 4217 standard which maps the country to a specific currency code.
The price value is converted into cents automatically by the gem e.g. 1 USD gets converted into 100 cents. This is important to remember else you will end up with strange values.
Since I will be accessing this value quite frequently i also added an index for it.
Use the money-rails gem to help format the currency values inside views. The gem contains useful helper methods such as humanized_money_with_symbol and humanized_money to convert the currency value into a useful string representation for display.
To help validate against the price value since it is set in a form, I still need to add some validations to make sure that the price value is present and valid.
validates :price, :numericality => {greater_than_or_equal_to: 1.0}, :presence => true
Money gem provides a numerical validator to catch non-integer values such as strings and this can be found inside the money-rails gem lib folder 'lib/money-rails/active_model/validator.rb'
Passing in a string value to a money object field through a form does not save it properly for the simple reason that the field is no longer of type string or decimal but of object Money hence any value will need to be converted first before it can be saved.
To cate for this I created a before_save callback which formats the form value into a money object before saving it:
before_save :format_price
def format_price
self.price = Money.new((BigDecimal.new(self.price.to_s).round(2) * 100).to_i, 'GBP')
end
The price value needs to be converted into its cents equivalent and its currency code set in the callback.
To test that the attribute returns a Money object on successful save I have the following structure inside my rspec2 model specs:
Note that within the tests, the numeric validator within money-rails is testing for the presence of strings and malformed decimal places while the other validations are added by myself.
I hope the above helps someone to get to grips with using Money and money-rails gems in their own projects. Please comment on better ways of doing the above if you have come across similar setup in your own projects.