diff --git a/Gemfile b/Gemfile index b9de014..7ec4df3 100644 --- a/Gemfile +++ b/Gemfile @@ -8,9 +8,8 @@ gem 'fixer' gem 'newrelic_rpm' gem 'rake' gem 'sequel_pg' -gem 'sinatra-jsonp' +gem 'sinatra' gem 'unicorn' -gem 'yajl-ruby' group :development do gem 'minitest' diff --git a/Gemfile.lock b/Gemfile.lock index 61074d7..3604a52 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -11,7 +11,6 @@ GEM minitest (5.8.4) minitest-around (0.3.2) minitest (~> 5.0) - multi_json (1.11.2) newrelic_rpm (3.15.1.316) oga (2.2) ast @@ -41,15 +40,11 @@ GEM rack (~> 1.5) rack-protection (~> 1.4) tilt (>= 1.3, < 3) - sinatra-jsonp (0.4.4) - multi_json (~> 1.8) - sinatra (~> 1.0) slop (3.6.0) tilt (2.0.2) unicorn (5.1.0) kgio (~> 2.6) raindrops (~> 0.7) - yajl-ruby (1.2.1) PLATFORMS ruby @@ -64,9 +59,8 @@ DEPENDENCIES rake sequel_pg shotgun - sinatra-jsonp + sinatra unicorn - yajl-ruby BUNDLED WITH 1.11.2 diff --git a/config/app.rb b/config/app.rb index 41193a0..a0ec39a 100644 --- a/config/app.rb +++ b/config/app.rb @@ -6,7 +6,7 @@ require 'pathname' # Encapsulates app configuration module App class << self - attr_reader :logger + attr_reader :logger, :version, :released_at def env ENV['RACK_ENV'] || 'development' @@ -15,11 +15,9 @@ module App def root Pathname.pwd end - - def version - `git rev-parse --short HEAD 2>/dev/null`.strip! - end end @logger = Logger.new(STDOUT) + @version = `git rev-parse --short HEAD 2>/dev/null`.strip! + @released_at = `git show -s --format=%ci HEAD` end diff --git a/lib/api.rb b/lib/api.rb index b3ac943..39a465f 100644 --- a/lib/api.rb +++ b/lib/api.rb @@ -1,8 +1,7 @@ # frozen_string_literal: true +require 'json' require 'sinatra' -require 'sinatra/jsonp' -require 'yajl' require 'quote' configure do @@ -30,26 +29,31 @@ end helpers do def quote @quote ||= begin - ret = Quote.new(params).attributes - ret[:rates].keep_if { |k, _| symbols.include?(k) } if symbols - - ret + Quote.new(params).attributes.tap do |quote| + quote[:rates].keep_if { |k, _| symbols.include?(k) } if symbols + end end rescue Quote::Invalid => ex - halt_with_message 422, ex.message + halt 422, JSON.generate(error: ex.message) end def symbols - return @symbols if defined?(@symbols) - - @symbols = begin - ret = params.delete('symbols') || params.delete('currencies') - ret.split(',') if ret + @symbols ||= begin + params.values_at('symbols', 'currencies').first.tap do |symbols| + symbols.split(',') if symbols + end end end - def halt_with_message(status, message) - halt status, Yajl::Encoder.encode(error: message) + def jsonp(data) + callback = params.delete('callback') + if callback + content_type :js + "#{callback}(#{JSON.generate(data)})" + else + content_type :json + JSON.generate(data) + end end def enable_cross_origin @@ -65,6 +69,7 @@ end get '/' do enable_cross_origin + last_modified App.released_at jsonp details: 'http://fixer.io', version: App.version end @@ -81,5 +86,5 @@ get(/(?\d{4}-\d{2}-\d{2})/) do end not_found do - halt_with_message 404, 'Not found' + halt 404, JSON.generate(error: 'Not found') end diff --git a/spec/api_spec.rb b/spec/api_spec.rb index fea9d0a..cdfa7a2 100644 --- a/spec/api_spec.rb +++ b/spec/api_spec.rb @@ -8,7 +8,7 @@ describe 'the API' do include Rack::Test::Methods let(:app) { Sinatra::Application } - let(:json) { Yajl::Parser.new.parse last_response.body } + let(:json) { JSON.parse last_response.body } let(:headers) { last_response.headers } it 'describes itself' do @@ -42,14 +42,11 @@ describe 'the API' do json['rates'].wont_be :empty? end - it 'returns a last modified header for latest quote' do - get '/latest' - headers['Last-Modified'].wont_be_nil - end - - it 'returns a last modified header for historical quote' do - get '/2012-11-20' - headers['Last-Modified'].wont_be_nil + it 'returns a last modified header' do + %w(/ /latest /2012-11-20).each do |path| + get path + headers['Last-Modified'].wont_be_nil + end end it 'allows cross-origin requests' do diff --git a/spec/edge_cases_spec.rb b/spec/edge_cases_spec.rb index 08a5459..7e19789 100644 --- a/spec/edge_cases_spec.rb +++ b/spec/edge_cases_spec.rb @@ -6,31 +6,36 @@ describe 'the API' do include Rack::Test::Methods let(:app) { Sinatra::Application } - let(:json) { Yajl::Parser.new.parse last_response.body } + let(:json) { JSON.parse last_response.body } it 'handles unfound pages' do get '/foo' last_response.status.must_equal 404 + json.wont_be_empty end it 'will not process an invalid date' do get '/2010-31-01' last_response.must_be :unprocessable? + json.wont_be_empty end it 'will not process a date before 2000' do get '/1999-01-01' last_response.must_be :unprocessable? + json.wont_be_empty end it 'will not process an invalid base' do get '/latest?base=UAH' 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? + json.wont_be_empty end it 'returns fresh dates' do