overture8/fx_rates

View on GitHub
lib/fx_rates.rb

Summary

Maintainability
A
45 mins
Test Coverage
require "fx_rates/version"
require 'fx_rates/configuration'
require 'fx_rates/api/parser/ecb'
require 'fx_rates/railtie' if defined?(Rails)
require 'moneta'

module FxRates
  # Exception to be raised if no date is found as the key in the
  # key/value store.
  class NoDateFoundError < StandardError; end

  # Exception to be raised if either the base or counter currency
  # code is not found.
  class NoCurrencyCodeFoundError < StandardError
    attr_reader :type
    def initialize(type)
      @type = type
    end
  end

  # Exception to be raised if either the base or counter currency
  # code is of an invalid type. i.e. not numeric
  class RateInvalidTypeError < StandardError
    attr_reader :type
    def initialize(type)
      @type = type
    end
  end

  class ExchangeRate
    # Looks up the date from the key/value store then extract
    # the relevant base and counter rates to calculate the resulting
    # amount.
    #
    # date - The date for which the rates should be used. 
    # from_country_code - The country code for the from currency.
    # to_country_code - The country code for the to currency.
    #
    # Examples:
    #
    #   ExchangeRate.at(Date.today,'GBP','USD')
    #
    # Returns the calculated value in the require currency.
    def self.at(date, from_country_code, to_country_code)
      store = FxRates.configuration.store
      raise NoDateFoundError.new unless store.key?(date)

      rates = store[date]
      base_rate = rates[from_country_code]
      counter_rate = rates[to_country_code]
      raise NoCurrencyCodeFoundError.new(:base) if base_rate.nil?
      raise NoCurrencyCodeFoundError.new(:counter) if counter_rate.nil?
      raise RateInvalidTypeError.new(:base) unless base_rate.is_a? Numeric
      raise RateInvalidTypeError.new(:counter) unless counter_rate.is_a? Numeric

      counter_rate / base_rate
    end
  end

  # Interface for interacting with global configuation.
  class << self
    attr_writer :configuration

    # Provides singleton access to the Configuration instance.
    def configuration
      @configuration ||= Configuration.new
    end

    # Allows some sugar for defining configuration using a block.
    def configure
      yield(configuration)
    end
  end

end