vlado/open_exchange_rates

View on GitHub
lib/open_exchange_rates/rates.rb

Summary

Maintainability
A
1 hr
Test Coverage
require "open-uri"
require "date"
require "json"

module OpenExchangeRates
  class Rates

    class MissingAppIdError < StandardError
      def initialize(msg = "Go to https://openexchangerates.org/signup to get App ID and then add it to the configuration: OpenExchangeRates.configuration.app_id = 'your app id'")
        super(msg)
      end
    end

    class RateNotFoundError < StandardError
      def initialize(from_curr, to_curr)
        msg = "Rate not found for #{from_curr} => #{to_curr}"
        super(msg)
      end
    end

    attr_reader :app_id

    def initialize(options = {})
      if options.kind_of? Hash
        @app_id = options[:app_id] || OpenExchangeRates.configuration.app_id
      else
        warn "[DEPRECATION] `OpenExchangeRates::Rates.new('myappid')` is deprecated.  Please use `OpenExchangeRates::Rates.new(:app_id => 'myappid')` instead."
        @app_id = options
      end
      raise MissingAppIdError unless @app_id
    end

    def exchange_rate(options = {})
      from_curr = options[:from].to_s.upcase
      to_curr = options[:to].to_s.upcase

      response = options[:on] ? on(options[:on]) : latest
      rates = response.rates

      from_curr = response.base_currency if from_curr.empty?
      to_curr = response.base_currency if to_curr.empty?

      unless rates[from_curr] && rates[to_curr]
        raise RateNotFoundError.new(from_curr, to_curr)
      end

      if from_curr == to_curr
        rate = 1.0
      elsif from_curr == response.base_currency
        rate = rates[to_curr]
      elsif to_curr == response.base_currency
        rate = 1.0 / rates[from_curr]
      else
        rate = rates[to_curr] * (1.0 / rates[from_curr])
      end
      round(rate, 6)
    end

    def convert(amount, options = {})
      round(amount*exchange_rate(options))
    end

    def latest(reload = false)
      @latest_response = reload ? parse_latest : (@latest_response ||= parse_latest)
      OpenExchangeRates::Response.new(@latest_response)
    end

    def valid_yyyy_mm_dd(date_string)
      matches = date_string =~ /^([0-9]{4})(?:(1[0-2]|0[1-9])|-(1[0-2]|0[1-9])-)(3[0-1]|0[1-9]|[1-2][0-9])/
      raise ArgumentError, 'Not a valid date string (ie. yyyy-mm-dd)' unless matches
      date_string
    end

    def date_string_from(date_representation)
      if date_representation.kind_of? Date
        date_representation.to_s
      elsif date_representation.kind_of? String
        valid_yyyy_mm_dd date_representation
      else
        raise ArgumentError, "'on' must be a Date or 'yyyy-mm-dd' string"
      end
    end

    def on(date_representation)
      date_string = date_string_from(date_representation)
      OpenExchangeRates::Response.new(parse_on(date_string))
    end

    def round(amount, decimals = 2)
      (amount * 10**decimals).round.to_f / 10**decimals
    end

    def parse_latest
      json = URI.open("#{OpenExchangeRates::LATEST_URL}?app_id=#{@app_id}").read
      JSON.parse(json)
    end

    def parse_on(date_string)
      json = URI.open("#{OpenExchangeRates::BASE_URL}/historical/#{date_string}.json?app_id=#{@app_id}").read
      JSON.parse(json)
    end

  end
end