class Money::Bank::TransferwiseBank

TransferwiseBank base class

Constants

TW_SANDBOX_SERVICE_HOST
TW_SERVICE_HOST

TransferwiseBank url components

TW_SERVICE_PATH
TW_SERVICE_SSL_VERSION

Default SSL Version

TW_SOURCE

Default base currency

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

raise_on_failure[W]

Option to raise an error on failure to connect to the API or parse the response. By default, this is true, but the ability to disable it is useful when developing without an active internet connection.

@example

raise_on_failure = false

@param value [Boolean] should an error be raised on API failure? @return [Boolean] is an error to be raised on API failure?

rates[R]

Parsed TransferwiseBank result as Hash

rates_mem_timestamp[R]

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

service_ssl_version[W]

Set the SSL Version used for requests to the API. By default, :TLSv1_2 is used.

@example

service_ssl_version = :TLSv1_1

@param value [Symbol] SSL version from OpenSSL::SSL::SSLContext::METHODS @return [Symbol] chosen SSL version

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

use_sandbox[W]

Option to use the Sandbox version of the TransferWise API. By default, this is false, and the live API is used.

@example

use_sandbox = true

@param value [Boolean] should the sandbox api be used? @return [Boolean] is the sandbox api being used?

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/transferwise_bank.rb, line 178
def add_rate(from_currency, to_currency, rate)
  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/transferwise_bank.rb, line 198
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/transferwise_bank.rb, line 212
def expired?
  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/transferwise_bank.rb, line 190
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
raise_on_failure() click to toggle source

Option to raise an error on failure to connect to the API or parse the response. By default, this is true, but the ability to disable it is useful when developing without an active internet connection. @return [Boolean] is an error to be raised on API failure?

# File lib/money/bank/transferwise_bank.rb, line 150
def raise_on_failure
  @raise_on_failure = true if @raise_on_failure.nil?
  @raise_on_failure
end
rates_expiration() click to toggle source

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

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

Get the timestamp of rates from first listed rate @return [Time] time object or nil

# File lib/money/bank/transferwise_bank.rb, line 251
def rates_timestamp
  raw = raw_rates_careful
  raw.first.key?('time') ? Time.parse(raw.first['time']) : Time.at(0)
end
service_host() click to toggle source

Service host of TransferwiseBank API based on value of 'use_sandbox' option. @return [String] the remote API service host

# File lib/money/bank/transferwise_bank.rb, line 227
def service_host
  use_sandbox ? TW_SANDBOX_SERVICE_HOST : TW_SERVICE_HOST
end
service_ssl_version() click to toggle source

Set the SSL Version used for requests to the API. By default, :TLSv1_2 is used. @return [Symbol] chosen SSL version

# File lib/money/bank/transferwise_bank.rb, line 134
def service_ssl_version
  @service_ssl_version ||= TW_SERVICE_SSL_VERSION
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/transferwise_bank.rb, line 120
def source
  @source ||= TW_SOURCE
end
source=(value) click to toggle source

Set the base currency for all rates. By default, USD is used. TransferwiseBank 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/transferwise_bank.rb, line 114
def source=(value)
  @source = Money::Currency.find(value.to_s).try(:iso_code) || TW_SOURCE
end
source_url() click to toggle source

Source url of TransferwiseBank @return [String] the remote API url

# File lib/money/bank/transferwise_bank.rb, line 233
def source_url
  raise NoAccessKey if access_key.nil? || access_key.empty?
  url_componenets = {
    host: service_host,
    path: TW_SERVICE_PATH,
    query: "source=#{source}"
  }
  URI::HTTPS.build(url_componenets)
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/transferwise_bank.rb, line 220
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/transferwise_bank.rb, line 127
def ttl_in_seconds
  @ttl_in_seconds ||= 0
end
update_rates(straight = false) click to toggle source

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

# File lib/money/bank/transferwise_bank.rb, line 157
def update_rates(straight = false)
  new_rates = exchange_rates(straight)
  return if new_rates.first.empty?
  store.reset!
  rates = new_rates.each do |exchange_rate|
    currency = exchange_rate['target']
    rate = exchange_rate['rate']
    next unless Money::Currency.find(currency)
    add_rate(source, currency, rate)
    add_rate(currency, source, 1.0 / rate)
  end
  add_rate(source, source, 1.0)
  @rates_mem_timestamp = rates_timestamp
  rates
end
use_sandbox() click to toggle source

Option to use the Sandbox version of the TransferWise API. By default, this is false, and the live API is used. @return [Boolean] is the sandbox api being used?

# File lib/money/bank/transferwise_bank.rb, line 141
def use_sandbox
  @use_sandbox = false if @use_sandbox.nil?
  @use_sandbox
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/transferwise_bank.rb, line 394
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/transferwise_bank.rb, line 348
def exchange_rates(straight = false)
  @rates = if straight
             raw_rates_straight
           else
             raw_rates_careful
           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/transferwise_bank.rb, line 377
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_file() click to toggle source

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

# File lib/money/bank/transferwise_bank.rb, line 306
def open_file
  open(source_url).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/transferwise_bank.rb, line 359
def raw_rates_careful(rescue_straight = true)
  JSON.parse(read_from_cache.to_s)
rescue JSON::ParserError, TypeError
  rescue_straight ? raw_rates_straight : [{}]
end
raw_rates_straight() click to toggle source

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

# File lib/money/bank/transferwise_bank.rb, line 367
def raw_rates_straight
  JSON.parse(read_from_url)
rescue JSON::ParserError, TypeError
  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/transferwise_bank.rb, line 287
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/transferwise_bank.rb, line 298
def read_from_url
  rates = source_url.is_a?(URI::HTTPS) ? retrieve_rates : open_file
  store_in_cache(rates) if valid_rates?(rates) && cache
  rates
end
retrieve_rates() click to toggle source

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

# File lib/money/bank/transferwise_bank.rb, line 314
def retrieve_rates
  response = HTTParty.get(
    source_url,
    headers: { 'Authorization' => "Bearer #{access_key}" },
    ssl_version: service_ssl_version
  )
  response.body
rescue HTTParty::Error, SocketError => e
  raise e if raise_on_failure
  [{}]
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/transferwise_bank.rb, line 266
def store_in_cache(text)
  if cache.is_a?(Proc)
    cache.call(text)
  elsif cache.is_a?(String) || cache.is_a?(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/transferwise_bank.rb, line 333
def valid_rates?(text)
  parsed = JSON.parse(text)
  parsed && parsed.is_a?(Array) && !parsed.first.empty?
rescue JSON::ParserError, TypeError
  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/transferwise_bank.rb, line 277
def write_to_file(text)
  open(cache, 'w') do |f|
    f.write(text)
  end
rescue Errno::ENOENT
  raise InvalidCache
end