RubyMoney/money

Name: money

Owner: RubyMoney

Description: A Ruby Library for dealing with money and currency conversion.

Created: 2008-10-31 15:05:03.0

Updated: 2018-01-19 09:02:25.0

Pushed: 2018-01-18 22:33:01.0

Homepage: http://rubymoney.github.io/money

Size: 2077

Language: Ruby

GitHub Committers

UserMost Recent Commit# Commits

Other Committers

UserEmailMost Recent Commit# Commits

README

RubyMoney - Money

Gem Version Build Status Code Climate Coverage Status Inline docs Dependency Status License

:warning: Please read the migration notes before upgrading to a new major version.

If you miss String parsing, check out the new monetize gem.

Contributing

See the Contribution Guidelines

Introduction

A Ruby Library for dealing with money and currency conversion.

Features
Resources
Notes
Downloading

Install stable releases with the following command:

gem install money

The development version (hosted on Github) can be installed with:

git clone git://github.com/RubyMoney/money.git
cd money
rake install
Usage
ire 'money'

.00 USD
y = Money.new(1000, "USD")
y.cents     #=> 1000
y.currency  #=> Currency.new("USD")

mparisons
y.new(1000, "USD") == Money.new(1000, "USD")   #=> true
y.new(1000, "USD") == Money.new(100, "USD")    #=> false
y.new(1000, "USD") == Money.new(1000, "EUR")   #=> false
y.new(1000, "USD") != Money.new(1000, "EUR")   #=> true

ithmetic
y.new(1000, "USD") + Money.new(500, "USD") == Money.new(1500, "USD")
y.new(1000, "USD") - Money.new(200, "USD") == Money.new(800, "USD")
y.new(1000, "USD") / 5                     == Money.new(200, "USD")
y.new(1000, "USD") * 5                     == Money.new(5000, "USD")

it to subunit conversions
y.from_amount(5, "USD") == Money.new(500, "USD")  # 5 USD
y.from_amount(5, "JPY") == Money.new(5, "JPY")    # 5 JPY
y.from_amount(5, "TND") == Money.new(5000, "TND") # 5 TND

rrency conversions
_code_to_setup_exchange_rates
y.new(1000, "USD").exchange_to("EUR") == Money.new(some_value, "EUR")

rmatting (see Formatting section for more options)
y.new(100, "USD").format #=> "$1.00"
y.new(100, "GBP").format #=> "£1.00"
y.new(100, "EUR").format #=> "?1.00"
Currency

Currencies are consistently represented as instances of Money::Currency. The most part of Money APIs allows you to supply either a String or a Money::Currency.

y.new(1000, "USD") == Money.new(1000, Money::Currency.new("USD"))
y.new(1000, "EUR").currency == Money::Currency.new("EUR")

A Money::Currency instance holds all the information about the currency, including the currency symbol, name and much more.

ency = Money.new(1000, "USD").currency
ency.iso_code #=> "USD"
ency.name     #=> "United States Dollar"

To define a new Money::Currency use Money::Currency.register as shown below.

 = {
riority            => 1,
so_code            => "USD",
so_numeric         => "840",
ame                => "United States Dollar",
ymbol              => "$",
ubunit             => "Cent",
ubunit_to_unit     => 100,
ecimal_mark        => ".",
housands_separator => ","


y::Currency.register(curr)

The pre-defined set of attributes includes:

All attributes except :iso_code are optional. Some attributes, such as :symbol, are used by the Money class to print out a representation of the object. Other attributes, such as :name or :priority, exist to provide a basic API you can take advantage of to build your application.

:priority

The priority attribute is an arbitrary numerical value you can assign to the Money::Currency and use in sorting/grouping operation.

For instance, let's assume your Rails application needs to render a currency selector like the one available here. You can create a couple of custom methods to return the list of major currencies and all currencies as follows:

turns an array of currency id where
iority < 10
major_currencies(hash)
sh.inject([]) do |array, (id, attributes)|
priority = attributes[:priority]
if priority && priority < 10
  array[priority] ||= []
  array[priority] << id
end
array
d.compact.flatten


turns an array of all currency id
all_currencies(hash)
sh.keys


r_currencies(Money::Currency.table)
 [:usd, :eur, :gbp, :aud, :cad, :jpy]

currencies(Money::Currency.table)
 [:aed, :afn, :all, ...]
Default Currency

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

y.default_currency = Money::Currency.new("CAD")

If you use Rails, then environment.rb is a very good place to put this.

Currency Exponent

The exponent of a money value is the number of digits after the decimal separator (which separates the major unit from the minor unit). See e.g. ISO 4217 for more information. You can find the exponent (as an Integer) by

y::Currency.new("USD").exponent  # => 2
y::Currency.new("JPY").exponent  # => 0
y::Currency.new("MGA").exponent  # => 1
Currency Lookup

To find a given currency by ISO 4217 numeric code (three digits) you can do

y::Currency.find_by_iso_numeric(978) #=> Money::Currency.new(:eur)
Currency Exchange

Exchanging money is performed through an exchange bank object. The default exchange bank object requires one to manually specify the exchange rate. Here's an example of how it works:

y.add_rate("USD", "CAD", 1.24515)
y.add_rate("CAD", "USD", 0.803115)

y.us_dollar(100).exchange_to("CAD")  # => Money.new(124, "CAD")
y.ca_dollar(100).exchange_to("USD")  # => Money.new(80, "USD")

Comparison and arithmetic operations work as expected:

y.new(1000, "USD") <=> Money.new(900, "USD")   # => 1; 9.00 USD is smaller
y.new(1000, "EUR") + Money.new(10, "EUR") == Money.new(1010, "EUR")

y.add_rate("USD", "EUR", 0.5)
y.new(1000, "EUR") + Money.new(1000, "USD") == Money.new(1500, "EUR")
Exchange rate stores

The default bank is initialized with an in-memory store for exchange rates.

y.default_bank = Money::Bank::VariableExchange.new(Money::RatesStore::Memory.new)

You can pass you own store implementation, ie. for storing and retrieving rates off a database, file, cache, etc.

y.default_bank = Money::Bank::VariableExchange.new(MyCustomStore.new)

Stores must implement the following interface:

d new exchange rate.
aram [String] iso_from Currency ISO code. ex. 'USD'
aram [String] iso_to Currency ISO code. ex. 'CAD'
aram [Numeric] rate Exchange rate. ex. 0.0016

eturn [Numeric] rate.
add_rate(iso_from, iso_to, rate); end

t rate. Must be idempotent. ie. adding the same rate must not produce duplicates.
aram [String] iso_from Currency ISO code. ex. 'USD'
aram [String] iso_to Currency ISO code. ex. 'CAD'

eturn [Numeric] rate.
get_rate(iso_from, iso_to); end

erate over rate tuples (iso_from, iso_to, rate)

ieldparam iso_from [String] Currency ISO string.
ieldparam iso_to [String] Currency ISO string.
ieldparam rate [Numeric] Exchange rate.

eturn [Enumerator]

xample
store.each_rate do |iso_from, iso_to, rate|
  puts [iso_from, iso_to, rate].join
end
each_rate(&block); end

ap store operations in a thread-safe transaction
r IO or Database transaction, depending on your implementation)

ield [n] Block that will be wrapped in transaction.

xample
store.transaction do
  store.add_rate('USD', 'CAD', 0.9)
  store.add_rate('USD', 'CLP', 0.0016)
end
transaction(&block); end

rialize store and its content to make Marshal.dump work.

turns an array with store class and any arguments needed to initialize the store in the current state.

eturn [Array] [class, arg1, arg2]
marshal_dump; end

The following example implements an ActiveRecord store to save exchange rates to a database.

 columns :from[String], :to[String], :rate[Float]

s ExchangeRate < ActiveRecord::Base
f self.get_rate(from_iso_code, to_iso_code)
rate = find_by(:from => from_iso_code, :to => to_iso_code)
rate.present? ? rate.rate : nil
d

f self.add_rate(from_iso_code, to_iso_code, rate)
exrate = find_or_initialize_by(:from => from_iso_code, :to => to_iso_code)
exrate.rate = rate
exrate.save!
d

Now you can use it with the default bank.

y.default_bank = Money::Bank::VariableExchange.new(ExchangeRate)

d to the underlying store
y.default_bank.add_rate('USD', 'CAD', 0.9)
trieve from the underlying store
y.default_bank.get_rate('USD', 'CAD') # => 0.9
changing amounts just works.
y.new(1000, 'USD').exchange_to('CAD') #=> #<Money fractional:900 currency:CAD>

There is nothing stopping you from creating store objects which scrapes XE for the current rates or just returns rand(2):

y.default_bank = Money::Bank::VariableExchange.new(StoreWhichScrapesXeDotCom.new)

You can also implement your own Bank to calculate exchanges differently. Different banks can share Stores.

y.default_bank = MyCustomBank.new(Money::RatesStore::Memory.new)

If you wish to disable automatic currency conversion to prevent arithmetic when currencies don't match:

y.disallow_currency_conversion!
Implementations

The following is a list of Money.gem compatible currency exchange rate implementations.

Ruby on Rails

To integrate money in a Rails application use money-rails.

For deprecated methods of integrating with Rails, check the wiki.

I18n

If you want thousands seperator and decimal mark to be same across all currencies this can be defined in your I18n translation files.

In an rails application this may look like:

nfig/locale/en.yml

mber:
format:
  delimiter: ","
  separator: "."
or
mber:
currency:
  format:
    delimiter: ","
    separator: "."

For this example Money.new(123456789, "SEK").format will return 1,234,567.89 kr which otherwise will return 1 234 567,89 kr.

If you wish to disable this feature:

y.use_i18n = false
Troubleshooting

If you get a runtime error such as:

I18n::InvalidLocale: :en is not a valid locale

Set the following:

.enforce_available_locales = false
Formatting

There are several formatting rules for when Money#format is called. For more information, check out the formatting module source, or read the latest release's rdoc version.

If you wish to format money according to the EU's Rules for expressing monetary units in either English, Irish, Latvian or Maltese:

Money.new('123', :gbp) # => #<Money fractional:123 currency:GBP>
rmat( symbol: m.currency.to_s + ' ') # => "GBP 1.23"
Heuristics

Prior to v6.9.0 heuristic analysis of string input was part of this gem. Since then it was extracted in to money-heuristics gem.

Migration Notes
Version 6.0.0

This work is supported by the National Institutes of Health's National Center for Advancing Translational Sciences, Grant Number U24TR002306. This work is solely the responsibility of the creators and does not necessarily represent the official views of the National Institutes of Health.