class Money::Bank::VariableExchange

Class for aiding in exchanging money between different currencies. By default, the Money class uses an object of this class (accessible through +Money#bank+) for performing currency exchanges.

By default, Money::Bank::VariableExchange has no knowledge about conversion rates. One must manually specify them with add_rate, after which one can perform exchanges with #exchange_with.

Exchange rates are stored in memory using Money::RatesStore::Memory by default. Pass custom rates stores for other types of storage (file, database, etc)

@example

bank = Money::Bank::VariableExchange.new
bank.add_rate("USD", "CAD", 1.24515)
bank.add_rate("CAD", "USD", 0.803115)

c1 = Money.new(100_00, "USD")
c2 = Money.new(100_00, "CAD")

# Exchange 100 USD to CAD:
bank.exchange_with(c1, "CAD") #=> #<Money fractional:12451 currency:CAD>

# Exchange 100 CAD to USD:
bank.exchange_with(c2, "USD") #=> #<Money fractional:8031 currency:USD>

# With custom exchange rates storage
redis_store = MyCustomRedisStore.new(host: 'localhost:6379')
bank = Money::Bank::VariableExchange.new(redis_store)
# Store rates in redis
bank.add_rate 'USD', 'CAD', 0.98
# Get rate from redis
bank.get_rate 'USD', 'CAD'

Constants

FORMAT_SERIALIZERS
RATE_FORMATS

Available formats for importing/exporting rates.

SERIALIZER_SEPARATOR

Attributes

mutex[R]
store[R]

Public Class Methods

new(st = Money::RatesStore::Memory.new, &block) click to toggle source

Initializes a new Money::Bank::VariableExchange object. It defaults to using an in-memory, thread safe store instance for storing exchange rates.

@param [RateStore] st An exchange rate store, used to persist exchange rate pairs. @yield [n] Optional block to use when rounding after exchanging one

currency for another. See +Money::bank::base+
Calls superclass method Money::Bank::Base::new
# File lib/money/bank/variable_exchange.rb, line 59
def initialize(st = Money::RatesStore::Memory.new, &block)
  @store = st
  super(&block)
end

Public Instance Methods

add_rate(from, to, rate) click to toggle source

Registers a conversion rate and returns it (uses #set_rate). Delegates to Money::RatesStore::Memory

@param [Currency, String, Symbol] from Currency to exchange from. @param [Currency, String, Symbol] to Currency to exchange to. @param [Numeric] rate Rate to use when exchanging currencies.

@return [Numeric]

@example

bank = Money::Bank::VariableExchange.new
bank.add_rate("USD", "CAD", 1.24515)
bank.add_rate("CAD", "USD", 0.803115)
# File lib/money/bank/variable_exchange.rb, line 152
def add_rate(from, to, rate)
  set_rate(from, to, rate)
end
calculate_fractional(from, to_currency) click to toggle source
# File lib/money/bank/variable_exchange.rb, line 121
def calculate_fractional(from, to_currency)
  BigDecimal(from.fractional.to_s) / (
    BigDecimal(from.currency.subunit_to_unit.to_s) /
    BigDecimal(to_currency.subunit_to_unit.to_s)
  )
end
exchange(fractional, rate) { |ex| ... } click to toggle source
# File lib/money/bank/variable_exchange.rb, line 128
def exchange(fractional, rate, &block)
  ex = fractional * BigDecimal(rate.to_s)
  if block_given?
    yield ex
  elsif @rounding_method
    @rounding_method.call(ex)
  else
    ex
  end
end
exchange_with(from, to_currency, &block) click to toggle source

Exchanges the given Money object to a new Money object in to_currency.

@param [Money] from

The +Money+ object to exchange.

@param [Currency, String, Symbol] to_currency

The currency to exchange to.

@yield [n] Optional block to use when rounding after exchanging one

currency for another.

@yieldparam [Float] n The resulting float after exchanging one currency

for another.

@yieldreturn [Integer]

@return [Money]

@raise Money::Bank::UnknownRate if the conversion rate is unknown.

@example

bank = Money::Bank::VariableExchange.new
bank.add_rate("USD", "CAD", 1.24515)
bank.add_rate("CAD", "USD", 0.803115)

c1 = Money.new(100_00, "USD")
c2 = Money.new(100_00, "CAD")

# Exchange 100 USD to CAD:
bank.exchange_with(c1, "CAD") #=> #<Money fractional:12451 currency:CAD>

# Exchange 100 CAD to USD:
bank.exchange_with(c2, "USD") #=> #<Money fractional:8031 currency:USD>
# File lib/money/bank/variable_exchange.rb, line 105
def exchange_with(from, to_currency, &block)
  to_currency = Currency.wrap(to_currency)
  if from.currency == to_currency
    from
  else
    if rate = get_rate(from.currency, to_currency)
      fractional = calculate_fractional(from, to_currency)
      from.class.new(
        exchange(fractional, rate, &block), to_currency
      )
    else
      raise UnknownRate, "No conversion rate known for '#{from.currency.iso_code}' -> '#{to_currency}'"
    end
  end
end
export_rates(format, file = nil, opts = {}) click to toggle source

Return the known rates as a string in the format specified. If file is given will also write the string out to the file specified. Available formats are :json, :ruby and :yaml.

@param [Symbol] format Request format for the resulting string. @param [String] file Optional file location to write the rates to. @param [Hash] opts Options hash to set special parameters. Backwards compatibility only.

@return [String]

@raise Money::Bank::UnknownRateFormat if format is unknown.

@example

bank = Money::Bank::VariableExchange.new
bank.set_rate("USD", "CAD", 1.24515)
bank.set_rate("CAD", "USD", 0.803115)

s = bank.export_rates(:json)
s #=> "{\"USD_TO_CAD\":1.24515,\"CAD_TO_USD\":0.803115}"
# File lib/money/bank/variable_exchange.rb, line 215
def export_rates(format, file = nil, opts = {})
  raise Money::Bank::UnknownRateFormat unless
    RATE_FORMATS.include? format

  store.transaction do
    s = FORMAT_SERIALIZERS[format].dump(rates)

    unless file.nil?
      File.open(file, "w") {|f| f.write(s) }
    end

    s
  end
end
get_rate(from, to, opts = {}) click to toggle source

Retrieve the rate for the given currencies. data access. Delegates to Money::RatesStore::Memory

@param [Currency, String, Symbol] from Currency to exchange from. @param [Currency, String, Symbol] to Currency to exchange to. @param [Hash] opts Options hash to set special parameters. Backwards compatibility only.

@return [Numeric]

@example

bank = Money::Bank::VariableExchange.new
bank.set_rate("USD", "CAD", 1.24515)
bank.set_rate("CAD", "USD", 0.803115)

bank.get_rate("USD", "CAD") #=> 1.24515
bank.get_rate("CAD", "USD") #=> 0.803115
# File lib/money/bank/variable_exchange.rb, line 192
def get_rate(from, to, opts = {})
  store.get_rate(Currency.wrap(from).iso_code, Currency.wrap(to).iso_code)
end
import_rates(format, s, opts = {}) click to toggle source

Loads rates provided in s given the specified format. Available formats are :json, :ruby and :yaml. Delegates to Money::RatesStore::Memory

@param [Symbol] format The format of s. @param [String] s The rates string. @param [Hash] opts Options hash to set special parameters. Backwards compatibility only.

@return [self]

@raise Money::Bank::UnknownRateFormat if format is unknown.

@example

s = "{\"USD_TO_CAD\":1.24515,\"CAD_TO_USD\":0.803115}"
bank = Money::Bank::VariableExchange.new
bank.import_rates(:json, s)

bank.get_rate("USD", "CAD") #=> 1.24515
bank.get_rate("CAD", "USD") #=> 0.803115
# File lib/money/bank/variable_exchange.rb, line 256
def import_rates(format, s, opts = {})
  raise Money::Bank::UnknownRateFormat unless
    RATE_FORMATS.include? format

  store.transaction do
    data = FORMAT_SERIALIZERS[format].load(s)

    data.each do |key, rate|
      from, to = key.split(SERIALIZER_SEPARATOR)
      store.add_rate from, to, rate
    end
  end

  self
end
marshal_dump() click to toggle source
# File lib/money/bank/variable_exchange.rb, line 64
def marshal_dump
  [store.marshal_dump, @rounding_method]
end
marshal_load(arr) click to toggle source
# File lib/money/bank/variable_exchange.rb, line 68
def marshal_load(arr)
  store_info = arr[0]
  @store = store_info.shift.new(*store_info)
  @rounding_method = arr[1]
end
rates() click to toggle source

This should be deprecated.

# File lib/money/bank/variable_exchange.rb, line 231
def rates
  store.each_rate.each_with_object({}) do |(from,to,rate),hash|
    hash[[from, to].join(SERIALIZER_SEPARATOR)] = rate
  end
end
set_rate(from, to, rate, opts = {}) click to toggle source

Set the rate for the given currencies. access. Delegates to Money::RatesStore::Memory

@param [Currency, String, Symbol] from Currency to exchange from. @param [Currency, String, Symbol] to Currency to exchange to. @param [Numeric] rate Rate to use when exchanging currencies. @param [Hash] opts Options hash to set special parameters. Backwards compatibility only.

@return [Numeric]

@example

bank = Money::Bank::VariableExchange.new
bank.set_rate("USD", "CAD", 1.24515)
bank.set_rate("CAD", "USD", 0.803115)
# File lib/money/bank/variable_exchange.rb, line 171
def set_rate(from, to, rate, opts = {})
  store.add_rate(Currency.wrap(from).iso_code, Currency.wrap(to).iso_code, rate)
end