Money class

This money class is based on the example from the ActiveRecord doc: api.rubyonrails.org/classes/ActiveRecord/Aggregations/ClassMethods.html

Its in production use at www.snowdevil.ca and I haven’t found any major issues so far. The main reason to open source it is because It might be useful to other people and I hope i’ll get some feedback on how to improve the class.

I bundled the exporter with the money class since some tests depend on it and I figured that most applications which need to deal with Money also need to deal with proper exporting.

Download

Preferred method of installation is gem:

gem install --source http://dist.leetsoft.com money

Alternatively you can get the library packed

http://dist.leetsoft.com/pkg/

Usage

Use the compose_of helper to let active record deal with embedding the money object in your models. The following example requires a cents and a currency field.

class ProductUnit < ActiveRecord::Base
  belongs_to :product
  composed_of :price, :class_name => "Money", :mapping => [%w(cents cents) %(currency currency)]

  private        
    validate :cents_not_zero

    def cents_not_zero
      errors.add("cents", "cannot be zero or less") unless cents > 0
    end

    validates_presence_of :sku, :currency
    validates_uniqueness_of :sku        
end

Class configuration

Two const class variables are available to tailor Money to your needs. If you don’t need currency exchange at all, just ignore those.

Default Currency

By default Money defaults to USD as its currency. This can be overwritten using

Money.default_currency = "CAD"

If you use rails, the environment.rb is a very good place to put this.

Currency Exchange

The second parameter is a bit more complex. It lets you provide your own implementation of the currency exchange service. By default Money throws an exception when trying to call .exchange_to.

A second minimalist implementation is provided which lets you supply custom exchange rates:

Money.bank = VariableExchangeBank.new 
Money.bank.add_rate("USD", "CAD", 1.24515)
Money.bank.add_rate("CAD", "USD", 0.803115)
Money.us_dollar(100).exchange_to("CAD") => Money.ca_dollar(124)
Money.ca_dollar(100).exchange_to("USD") => Money.us_dollar(80)

There is nothing stopping you from creating bank objects which scrape www.xe.com for the current rates or just return rand(2)

Code

If you have any improvements please email them to tobi [at] leetsoft.com