Money Open Exchange Rates

A gem that calculates the exchange rate using published rates from open-exchange-rates. Compatible with Money currency exchange.

Check api documentation

  • Live and historical exchange rates for 180 currencies.
  • Free plan hourly updates, with USD base and up to 1,000 requests/month.
  • Automatically caches API results to file or Rails cache.
  • Calculate pair rates.
  • Automatically fetches new data from API if data becomes stale when ttl_in_seconds option is provided.
  • Support for black market and digital currency rates with show_alternative option.

Installation

Add this line to your application's Gemfile:

gem 'money-open-exchange-rates'

And then execute:

bundle

Or install it yourself as:

gem install money-open-exchange-rates

Usage

require 'money/bank/open_exchange_rates_bank'

# Memory store per default; for others just pass as argument a class like
# explained in https://github.com/RubyMoney/money#exchange-rate-stores
oxr = Money::Bank::OpenExchangeRatesBank.new
oxr.app_id = 'your app id from https://openexchangerates.org/signup'
oxr.update_rates

# (optional)
# See https://github.com/spk/money-open-exchange-rates#cache for more info
# Updated only when `refresh_rates` is called
oxr.cache = 'path/to/file/cache.json'

# (optional)
# Set the seconds after than the current rates are automatically expired
# by default, they never expire, in this example 1 day.
# This ttl is about money store (memory, database ...) passed though
# `Money::Bank::OpenExchangeRatesBank` as argument not about `cache` option.
# The base time is the timestamp fetched from API.
oxr.ttl_in_seconds = 86400

# (optional)
# Set historical date of the rate
# see https://openexchangerates.org/documentation#historical-data
oxr.date = '2015-01-01'

# (optional)
# Set the base currency for all rates. By default, USD is used.
# OpenExchangeRates only allows USD as base currency
# for the free plan users.
oxr.source = 'USD'

# (optional)
# Extend returned values with alternative, black market and digital currency
# rates. By default, false is used
# see: https://docs.openexchangerates.org/docs/alternative-currencies
oxr.show_alternative = true

# (optional)
# Minified Response ('prettyprint')
# see https://docs.openexchangerates.org/docs/prettyprint
oxr.prettyprint = false

# (optional)
# Refresh rates, store in cache and update rates
# Should be used on crontab/worker/scheduler `Money.default_bank.refresh_rates`
# If you are using unicorn-worker-killer gem or on Heroku like platform,
# you should avoid to put this on the initializer of your Rails application,
# because will increase your OXR API usage.
oxr.refresh_rates

# (optional)
# Force refresh rates cache and store on the fly when ttl is expired
# This will slow down request on get_rate, so use at your on risk, if you don't
# want to setup crontab/worker/scheduler for your application
oxr.force_refresh_rate_on_expire = true

Money.default_bank = oxr

Money.default_bank.get_rate('USD', 'CAD')

Cache

You can also provide a Proc as a cache to provide your own caching mechanism perhaps with Redis or just a thread safe Hash (global). For example:

oxr.cache = Proc.new do |v|
  key = 'money:exchange_rates'
  if v
    Thread.current[key] = v
  else
    Thread.current[key]
  end
end

With Rails cache example:

OXR_CACHE_KEY = "#{Rails.env}:money:exchange_rates".freeze
oxr.ttl_in_seconds = 86400
oxr.cache = Proc.new do |text|
  if text
    Rails.cache.write(OXR_CACHE_KEY, text)
  else
    Rails.cache.read(OXR_CACHE_KEY)
  end
end

To update the cache call Money.default_bank.refresh_rates on crontab/worker/scheduler. This have to be done this way because the fetch can take some time (HTTP call) and can fail.

Full example configuration initializer with Rails and cache

require 'money/bank/open_exchange_rates_bank'

OXR_CACHE_KEY = "#{Rails.env}:money:exchange_rates".freeze
# ExchangeRate is an ActiveRecord model
# more info at https://github.com/RubyMoney/money#exchange-rate-stores
oxr = Money::Bank::OpenExchangeRatesBank.new(ExchangeRate)
oxr.ttl_in_seconds = 86400
oxr.cache = Proc.new do |text|
  if text
    # only expire when refresh_rates is called or `force_refresh_rate_on_expire`
    # option is enabled
    # you can also set `expires_in` option on write to force fetch new rates
    Rails.cache.write(OXR_CACHE_KEY, text)
  else
    Rails.cache.read(OXR_CACHE_KEY)
  end
end
oxr.app_id = ENV['OXR_API_KEY']
oxr.show_alternative = true
oxr.prettyprint = false
oxr.update_rates

Money.default_bank = oxr

Tests

To avoid to hit the API we can use the cache option with a saved file like this:

OXR_CACHE_KEY = "#{Rails.env}:money:exchange_rates".freeze
if Rails.env.test?
  oxr.cache = Rails.root.join("test/fixtures/currency-rates.json").to_s
else
  oxr.ttl_in_seconds = 5.minutes.to_i
  oxr.cache = Proc.new do |text|
    if text
      Rails.cache.write(OXR_CACHE_KEY, text)
    else
      Rails.cache.read(OXR_CACHE_KEY)
    end
  end
end

Pair rates

Unknown pair rates are transparently calculated: using inverse rate (if known), or using base currency rate to both currencies forming the pair.

Tests

bundle exec rake

Refs

Contributors

See GitHub.

License

The MIT License

Copyright © 2011-2019 Laurent Arnoud [email protected]


Build Version Documentation License Coverage Status Inline docs