class Money::Bank::CurrencylayerBank

CurrencylayerBank base class

Constants

SOURCE

Default base currency

URL_AL

Apilayer url

URL_CL

CurrencylayerBank url

Attributes

access_key[RW]

API must have a valid access_key

@param value [String] API access key @return [String] chosen API access key

cache[RW]

Cache accessor, can be a String or a Proc

@param value [String,Pathname,Proc] cache system @return [String,Pathname,Proc] chosen cache system

currencylayer[RW]

Use new or old endpoint. new: api.apilayer.com old: api.currencylayer.com

@param value [Boolean] true for old endpoint @return [Boolean] chosen old endpoint if true

rates[R]

Parsed CurrencylayerBank result as Hash

rates_mem_timestamp[R]

Get the timestamp of rates in memory @return [Time] time object or nil

rescue_with_cache[RW]

Rescue with rates from the cache instead of reporting an error when the endpoint fails.

@param value [Boolean] true for rescue error with cache rates @return [Boolean] chosen rescue with cache rates

secure_connection[RW]

Use https to fetch rates from CurrencylayerBank CurrencylayerBank only allows http as connection for the free plan users.

@param value [Boolean] true for secure connection @return [Boolean] chosen secure connection

ttl_in_seconds[W]

Set the seconds after than the current rates are automatically expired by default, they never expire.

@example

ttl_in_seconds = 86400 # will expire the rates in one day

@param value [Integer] time to live in seconds @return [Integer] chosen time to live in seconds

Public Instance Methods

add_rate(from_currency, to_currency, rate) click to toggle source

Override Money ‘add_rate` method for caching @param [String] from_currency Currency ISO code. ex. ’USD’ @param [String] to_currency Currency ISO code. ex. ‘CAD’ @param [Numeric] rate Rate to use when exchanging currencies. @return [Numeric] rate.

Calls superclass method
# File lib/money/bank/currencylayer_bank.rb, line 138
def add_rate(from_currency, to_currency, rate) # rubocop:disable Lint/UselessMethodDefinition
  super
end
expire_rates!() click to toggle source

Fetch new rates if cached rates are expired or stale @return [Boolean] true if rates are expired and updated from remote

# File lib/money/bank/currencylayer_bank.rb, line 158
def expire_rates!
  if expired?
    update_rates(true)
    true
  elsif stale?
    update_rates
    true
  else
    false
  end
end
expired?() click to toggle source

Check if rates are expired @return [Boolean] true if rates are expired

# File lib/money/bank/currencylayer_bank.rb, line 172
def expired?
  return false if ttl_in_seconds.zero?

  Time.now > rates_expiration
end
get_rate(from_currency, to_currency, opts = {}) click to toggle source

Override Money ‘get_rate` method for caching @param [String] from_currency Currency ISO code. ex. ’USD’ @param [String] to_currency Currency ISO code. ex. ‘CAD’ @param [Hash] opts Options hash to set special parameters. @return [Numeric] rate.

# File lib/money/bank/currencylayer_bank.rb, line 150
def get_rate(from_currency, to_currency, opts = {})
  expire_rates!
  rate = get_rate_or_calc_inverse(from_currency, to_currency, opts)
  rate || calc_pair_rate_using_base(from_currency, to_currency, opts)
end
Also aliased as: super_get_rate
rates_expiration() click to toggle source

Get rates expiration time based on ttl @return [Time] rates expiration time

# File lib/money/bank/currencylayer_bank.rb, line 200
def rates_expiration
  rates_timestamp + ttl_in_seconds
end
rates_timestamp() click to toggle source

Get the timestamp of rates @return [Time] time object or nil

# File lib/money/bank/currencylayer_bank.rb, line 206
def rates_timestamp
  raw = raw_rates_careful
  raw.key?('timestamp') ? Time.at(raw['timestamp']) : Time.at(0)
end
source() click to toggle source

Get the base currency for all rates. By default, USD is used. @return [String] base currency

# File lib/money/bank/currencylayer_bank.rb, line 106
def source
  @source ||= SOURCE
end
source=(value) click to toggle source

Set the base currency for all rates. By default, USD is used. CurrencylayerBank only allows USD as base currency for the free plan users.

@example

source = 'USD'

@param value [String] Currency code, ISO 3166-1 alpha-3 @return [String] chosen base currency

# File lib/money/bank/currencylayer_bank.rb, line 100
def source=(value)
  @source = Money::Currency.find(value.to_s).try(:iso_code) || SOURCE
end
source_url() click to toggle source

Source url of CurrencylayerBank defined with access_key and secure_connection @return [String] the remote API url

# File lib/money/bank/currencylayer_bank.rb, line 189
def source_url
  raise NoAccessKey if access_key.nil? || access_key.empty?

  url = "#{currencylayer ? URL_CL : URL_AL}?source=#{source}"
  url = url.sub('http:', 'https:') if secure_connection
  url = "#{url}&access_key=#{access_key}" if currencylayer
  url
end
stale?() click to toggle source

Check if rates are stale Stale is true if rates are updated straight by another thread. The actual thread has always old rates in memory store. @return [Boolean] true if rates are stale

# File lib/money/bank/currencylayer_bank.rb, line 182
def stale?
  rates_timestamp != rates_mem_timestamp
end
super_get_rate(from_currency, to_currency, opts = {})

Alias super method

Alias for: get_rate
ttl_in_seconds() click to toggle source

Get the seconds after than the current rates are automatically expired by default, they never expire. @return [Integer] chosen time to live in seconds

# File lib/money/bank/currencylayer_bank.rb, line 113
def ttl_in_seconds
  @ttl_in_seconds ||= 0
end
update_rates(straight = false) click to toggle source

Update all rates from CurrencylayerBank JSON @return [Array] array of exchange rates

# File lib/money/bank/currencylayer_bank.rb, line 119
def update_rates(straight = false)
  store.reset!
  rates = exchange_rates(straight).each do |exchange_rate|
    currency = exchange_rate.first[3..-1]
    rate = exchange_rate.last
    next unless Money::Currency.find(currency)

    add_rate(source, currency, rate)
    add_rate(currency, source, 1.0 / rate)
  end
  @rates_mem_timestamp = rates_timestamp
  rates
end

Protected Instance Methods

calc_pair_rate_using_base(from_currency, to_currency, opts = {}) click to toggle source

Tries to calculate a pair rate using base currency rate @param [String] from_currency Currency ISO code. ex. ‘USD’ @param [String] to_currency Currency ISO code. ex. ‘CAD’ @return [Numeric] rate or nil if cannot calculate rate.

# File lib/money/bank/currencylayer_bank.rb, line 342
def calc_pair_rate_using_base(from_currency, to_currency, opts = {})
  from_base_rate = get_rate_or_calc_inverse(source, from_currency, opts)
  to_base_rate   = get_rate_or_calc_inverse(source, to_currency, opts)
  if to_base_rate && from_base_rate
    rate = to_base_rate / from_base_rate
    add_rate(from_currency, to_currency, rate)
    return rate
  end
  nil
end
exchange_rates(straight = false) click to toggle source

Get exchange rates with different strategies

@example

exchange_rates(true)
exchange_rates

@param straight [Boolean] true for straight, default is careful @return [Hash] key is country code (ISO 3166-1 alpha-3) value Float

# File lib/money/bank/currencylayer_bank.rb, line 290
def exchange_rates(straight = false)
  rates = straight ? raw_rates_straight : raw_rates_careful
  if rates.key?('quotes')
    @rates = rates['quotes']
  elsif rates.key?('error')
    raise Error, rates.dig('error', 'info') unless rescue_with_cache

    rates = raw_rates_careful(false)
    @rates = rates.key?('quotes') ? rates['quotes'] : { 'quotes' => {} }
  else
    raise Error, 'Unknown rates situation!'
  end
end
get_rate_or_calc_inverse(from_currency, to_currency, opts = {}) click to toggle source

Get rate or calculate it as inverse rate @param [String] from_currency Currency ISO code. ex. ‘USD’ @param [String] to_currency Currency ISO code. ex. ‘CAD’ @return [Numeric] rate or rate calculated as inverse rate.

# File lib/money/bank/currencylayer_bank.rb, line 325
def get_rate_or_calc_inverse(from_currency, to_currency, opts = {})
  rate = super_get_rate(from_currency, to_currency, opts)
  unless rate
    # Tries to calculate an inverse rate
    inverse_rate = super_get_rate(to_currency, from_currency, opts)
    if inverse_rate
      rate = 1.0 / inverse_rate
      add_rate(from_currency, to_currency, rate)
    end
  end
  rate
end
open_url() click to toggle source

Opens an url and reads the content @return [String] unparsed JSON content

# File lib/money/bank/currencylayer_bank.rb, line 262
def open_url
  currencylayer ? URI.open(source_url).read : URI.open(source_url, 'apikey' => access_key).read
rescue OpenURI::HTTPError
  ''
end
raw_rates_careful(rescue_straight = true) click to toggle source

Get raw exchange rates from cache and then from url @param rescue_straight [Boolean] true for rescue straight, default true @return [String] JSON content

# File lib/money/bank/currencylayer_bank.rb, line 307
def raw_rates_careful(rescue_straight = true)
  JSON.parse(read_from_cache.to_s)
rescue JSON::ParserError
  rescue_straight ? raw_rates_straight : { 'quotes' => {} }
end
raw_rates_straight() click to toggle source

Get raw exchange rates from url @return [String] JSON content

# File lib/money/bank/currencylayer_bank.rb, line 315
def raw_rates_straight
  JSON.parse(read_from_url)
rescue JSON::ParserError
  raw_rates_careful(false)
end
read_from_cache() click to toggle source

Read from cache when exist @return [Proc,String] parsed JSON content

# File lib/money/bank/currencylayer_bank.rb, line 243
def read_from_cache
  if cache.is_a?(Proc)
    cache.call(nil)
  elsif (cache.is_a?(String) || cache.is_a?(Pathname)) &&
        File.exist?(cache)
    open(cache).read
  end
end
read_from_url() click to toggle source

Get remote content and store in cache @return [String] unparsed JSON content

# File lib/money/bank/currencylayer_bank.rb, line 254
def read_from_url
  text = open_url
  store_in_cache(text) if valid_rates?(text) && cache
  text
end
store_in_cache(text) click to toggle source

Store the provided text data by calling the proc method provided for the cache, or write to the cache file.

@example

store_in_cache("{\"quotes\": {\"USDAED\": 3.67304}}")

@param text [String] parsed JSON content @return [String,Integer]

# File lib/money/bank/currencylayer_bank.rb, line 221
def store_in_cache(text)
  case cache
  when Proc
    cache.call(text)
  when String, Pathname
    write_to_file(text)
  end
end
valid_rates?(text) click to toggle source

Check validity of rates response only for store in cache

@example

valid_rates?("{\"quotes\": {\"USDAED\": 3.67304}}")

@param [String] text is JSON content @return [Boolean] valid or not

# File lib/money/bank/currencylayer_bank.rb, line 275
def valid_rates?(text)
  parsed = JSON.parse(text)
  parsed && parsed.key?('quotes')
rescue JSON::ParserError
  false
end
write_to_file(text) click to toggle source

Writes content to file cache @param text [String] parsed JSON content @return [String,Integer]

# File lib/money/bank/currencylayer_bank.rb, line 233
def write_to_file(text)
  open(cache, 'w') do |f|
    f.write(text)
  end
rescue Errno::ENOENT
  raise InvalidCache
end