mirror of
https://github.com/hakanensari/frankfurter.git
synced 2024-11-21 18:42:29 +01:00
Fold conversion into quote
This commit is contained in:
parent
1754efd0cb
commit
60f980a6ee
12
lib/api.rb
12
lib/api.rb
@ -4,7 +4,6 @@ require 'oj'
|
||||
require 'sinatra'
|
||||
require 'rack/cors'
|
||||
require 'quote'
|
||||
require 'converter'
|
||||
|
||||
configure :development do
|
||||
set :show_exceptions, :after_handler
|
||||
@ -32,12 +31,8 @@ helpers do
|
||||
end
|
||||
end
|
||||
|
||||
def converter
|
||||
@converter ||= Converter.new(params)
|
||||
end
|
||||
|
||||
def symbols
|
||||
@symbols ||= params.values_at('symbols', 'currencies').first
|
||||
@symbols ||= params.values_at('symbols', 'to').compact.first
|
||||
end
|
||||
|
||||
def jsonp(data)
|
||||
@ -83,11 +78,6 @@ get(/(?<date>\d{4}-\d{2}-\d{2})/) do
|
||||
jsonp quote_attributes
|
||||
end
|
||||
|
||||
get '/converter' do
|
||||
params[:base] = params[:from]
|
||||
jsonp Hash(amount: converter.convert(quote))
|
||||
end
|
||||
|
||||
not_found do
|
||||
halt 404, encode_json(error: 'Not found')
|
||||
end
|
||||
|
@ -1,32 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'bigdecimal'
|
||||
require 'quote'
|
||||
|
||||
class Converter
|
||||
attr_reader :to, :from
|
||||
|
||||
def initialize(params = {})
|
||||
@amount = params[:amount]
|
||||
@from = params[:from]
|
||||
@to = params[:to]
|
||||
end
|
||||
|
||||
# Converts an amount into a new currency
|
||||
# @param quote [Quote] the quote object
|
||||
# @return amount [BigDecimal] the converted amount
|
||||
def convert(quote = latest_quote)
|
||||
return amount if quote.base == to
|
||||
amount * quote.rates.fetch(to) { raise Quote::Invalid, 'Invalid to' }
|
||||
end
|
||||
|
||||
def amount
|
||||
BigDecimal(@amount.to_s)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def latest_quote
|
||||
@latest_quote ||= Quote.new(base: from)
|
||||
end
|
||||
end
|
28
lib/quote.rb
28
lib/quote.rb
@ -5,12 +5,14 @@ require 'currency'
|
||||
class Quote
|
||||
Invalid = Class.new(StandardError)
|
||||
|
||||
DEFAULT_AMOUNT = 1
|
||||
DEFAULT_BASE = 'EUR'
|
||||
|
||||
attr_reader :base, :date
|
||||
attr_reader :amount, :base, :date
|
||||
|
||||
def initialize(params = {})
|
||||
self.base = params[:base]
|
||||
self.amount = params['amount']
|
||||
self.base = params.values_at(:base, :from).compact.first
|
||||
self.date = params[:date]
|
||||
end
|
||||
|
||||
@ -26,14 +28,18 @@ class Quote
|
||||
|
||||
private
|
||||
|
||||
def base=(base)
|
||||
@base = base&.upcase || DEFAULT_BASE
|
||||
def amount=(value)
|
||||
@amount = (value || DEFAULT_AMOUNT).to_f
|
||||
raise Invalid, 'Invalid amount' if @amount.zero?
|
||||
end
|
||||
|
||||
def date=(date)
|
||||
current_date = date ? Currency.current_date_before(date) : Currency.current_date
|
||||
raise Invalid, 'Date too old' unless current_date
|
||||
@date = current_date
|
||||
def base=(value)
|
||||
@base = value&.upcase || DEFAULT_BASE
|
||||
end
|
||||
|
||||
def date=(value)
|
||||
@date = value ? Currency.current_date_before(value) : Currency.current_date
|
||||
raise Invalid, 'Date too old' unless @date
|
||||
rescue Sequel::DatabaseError => ex
|
||||
raise Invalid, 'Invalid date' if ex.wrapped_exception.is_a?(PG::DataException)
|
||||
raise
|
||||
@ -49,16 +55,16 @@ class Quote
|
||||
|
||||
def find_default_rates
|
||||
Currency.where(date: date).reduce({}) do |rates, currency|
|
||||
rates.update(currency.to_h)
|
||||
rates.update(Hash[currency.to_h.map { |k, v| [k, round_rate(v * amount)] }])
|
||||
end
|
||||
end
|
||||
|
||||
def find_rebased_rates
|
||||
rates = find_default_rates
|
||||
denominator = rates.update(DEFAULT_BASE => 1.0).delete(base)
|
||||
denominator = rates.update(DEFAULT_BASE => amount).delete(base)
|
||||
raise Invalid, 'Invalid base' unless denominator
|
||||
rates.each do |iso_code, rate|
|
||||
rates[iso_code] = round_rate(rate / denominator)
|
||||
rates[iso_code] = round_rate(amount * rate / denominator)
|
||||
end
|
||||
|
||||
rates
|
||||
|
@ -26,11 +26,26 @@ describe 'the API' do
|
||||
json['base'].must_equal 'USD'
|
||||
end
|
||||
|
||||
it 'sets base amount' do
|
||||
get '/latest?amount=10'
|
||||
json['rates']['USD'].must_be :>, 10
|
||||
end
|
||||
|
||||
it 'filters symbols' do
|
||||
get '/latest?symbols=USD'
|
||||
json['rates'].keys.must_equal %w(USD)
|
||||
end
|
||||
|
||||
it 'aliases base as from' do
|
||||
get '/latest?from=USD'
|
||||
json['base'].must_equal 'USD'
|
||||
end
|
||||
|
||||
it 'aliases symbols as to' do
|
||||
get '/latest?to=USD'
|
||||
json['rates'].keys.must_equal %w(USD)
|
||||
end
|
||||
|
||||
it 'returns historical quotes' do
|
||||
get '/2012-11-20'
|
||||
json['rates'].wont_be :empty?
|
||||
@ -67,8 +82,8 @@ describe 'the API' do
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns converted amount' do
|
||||
get '/converter?to=USD&amount=100'
|
||||
json['amount'].wont_be :nil?
|
||||
it 'converts an amount' do
|
||||
get '/latest?from=GBP&to=USD&amount=100'
|
||||
json['rates']['USD'].must_be :>, 100
|
||||
end
|
||||
end
|
||||
|
@ -1,47 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'helper'
|
||||
require 'converter'
|
||||
|
||||
describe Converter do
|
||||
let(:to) { 'USD' }
|
||||
let(:from) { 'EUR' }
|
||||
let(:amount) { '100.0' }
|
||||
|
||||
let(:converter) { Converter.new to: to, from: from, amount: amount }
|
||||
let(:quote) { Quote.new }
|
||||
|
||||
describe '#amount' do
|
||||
it 'casts to BigDecimal' do
|
||||
converter.amount.must_be_kind_of BigDecimal
|
||||
end
|
||||
end
|
||||
|
||||
describe '#convert' do
|
||||
it 'should return the amount' do
|
||||
quote.stub :base, to do
|
||||
converter.convert(quote).must_equal BigDecimal(amount)
|
||||
end
|
||||
end
|
||||
|
||||
it 'should use the rates to convert' do
|
||||
quote.stub :rates, Hash(to => 1.2) do
|
||||
converter.convert(quote).must_equal BigDecimal('120.0')
|
||||
end
|
||||
end
|
||||
|
||||
it 'should raise with invalid currency' do
|
||||
quote.stub :rates, {} do
|
||||
-> { converter.convert(quote) }.must_raise Quote::Invalid, 'Invalid to'
|
||||
end
|
||||
end
|
||||
|
||||
it 'should use latest quote as default' do
|
||||
quote.stub :rates, Hash(to => 1.3) do
|
||||
converter.stub :latest_quote, quote do
|
||||
converter.convert.must_equal 130.0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -34,6 +34,12 @@ describe 'the API' do
|
||||
json.wont_be_empty
|
||||
end
|
||||
|
||||
it 'will not process an invalid amount' do
|
||||
get '/latest?amount=foo'
|
||||
last_response.must_be :unprocessable?
|
||||
json.wont_be_empty
|
||||
end
|
||||
|
||||
it 'handles malformed queries' do
|
||||
get '/latest?base=USD?callback=?'
|
||||
last_response.must_be :unprocessable?
|
||||
|
Loading…
Reference in New Issue
Block a user