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
User | Most Recent Commit | # Commits |
---|
Other Committers
User | Most Recent Commit | # Commits |
---|
:warning: Please read the migration notes before upgrading to a new major version.
If you miss String parsing, check out the new monetize gem.
See the Contribution Guidelines
A Ruby Library for dealing with money and currency conversion.
Money
class which encapsulates all information about an certain
amount of money, such as its value and its currency.Money::Currency
class which encapsulates all information about
a monetary unit.Money::Currency
instances providing a high level of
flexibility.gem "json"
to your Gemfile or similar.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
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"
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:
:priority
a numerical value you can use to sort/group the currency list:iso_code
the international 3-letter code as defined by the ISO 4217 standard:iso_numeric
the international 3-digit code as defined by the ISO 4217 standard:name
the currency name:symbol
the currency symbol (UTF-8 encoded):subunit
the name of the fractional monetary unit:subunit_to_unit
the proportion between the unit and the subunit:decimal_mark
character between the whole and fraction amounts:thousands_separator
character between each thousands placeAll 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.
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, ...]
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.
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
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)
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")
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!
The following is a list of Money.gem compatible currency exchange rate implementations.
To integrate money in a Rails application use money-rails.
For deprecated methods of integrating with Rails, check the wiki.
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
If you get a runtime error such as:
I18n::InvalidLocale: :en is not a valid locale
Set the following:
.enforce_available_locales = false
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"
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.
Money#dollars
and Money#amount
methods now return instances of
BigDecimal
rather than Float
. We should avoid representing monetary
values with floating point types so to avoid a whole class of errors relating
to lack of precision. There are two migration options for this change:BigDecimal
return value. This is the recommended
path.#amount
and #dollars
methods to use
the #to_f
method instead. This option should only be used where Float
is the desired type and nothing else will do for your application's
requirements.