Switch to Roda

A first stab at replacing Sinatra with Roda
This commit is contained in:
Hakan Ensari 2020-05-08 14:50:09 +01:00
parent 535471509b
commit 82666af2d6
7 changed files with 81 additions and 96 deletions

View File

@ -1,7 +1,7 @@
Metrics/AbcSize:
Max: 21.79
Metrics/BlockLength:
ExcludedMethods: ['describe', 'helpers']
ExcludedMethods: ['describe', 'route']
Metrics/MethodLength:
Max: 13
Minitest:

View File

@ -7,11 +7,12 @@ ruby '2.7.1'
gem 'money'
gem 'oj'
gem 'ox'
gem 'rack-contrib'
gem 'rack-cors'
gem 'rake'
gem 'roda'
gem 'rufus-scheduler'
gem 'sequel_pg'
gem 'sinatra'
gem 'unicorn'
group :test do

View File

@ -25,8 +25,6 @@ GEM
minitest (>= 4, < 6)
money (6.13.7)
i18n (>= 0.6.4, <= 2)
mustermann (1.1.1)
ruby2_keywords (~> 0.0.1)
oj (3.10.6)
ox (2.13.2)
parallel (1.19.1)
@ -36,16 +34,18 @@ GEM
public_suffix (4.0.4)
raabro (1.1.6)
rack (2.2.2)
rack-contrib (2.2.0)
rack (~> 2.0)
rack-cors (1.1.1)
rack (>= 2.0.0)
rack-protection (2.0.8.1)
rack
rack-test (1.1.0)
rack (>= 1.0, < 3)
rainbow (3.0.0)
raindrops (0.19.1)
rake (13.0.1)
rexml (3.2.4)
roda (3.31.0)
rack
rubocop (0.82.0)
jaro_winkler (~> 1.5.1)
parallel (~> 1.10)
@ -61,7 +61,6 @@ GEM
rubocop-sequel (0.0.6)
rubocop (~> 0.55, >= 0.55)
ruby-progressbar (1.10.1)
ruby2_keywords (0.0.2)
rufus-scheduler (3.6.0)
fugit (~> 1.1, >= 1.1.6)
safe_yaml (1.0.5)
@ -73,12 +72,6 @@ GEM
docile (~> 1.1)
simplecov-html (~> 0.11)
simplecov-html (0.12.2)
sinatra (2.0.8.1)
mustermann (~> 1.0)
rack (~> 2.0)
rack-protection (= 2.0.8.1)
tilt (~> 2.0)
tilt (2.0.10)
tzinfo (2.0.2)
concurrent-ruby (~> 1.0)
unicode-display_width (1.7.0)
@ -101,16 +94,17 @@ DEPENDENCIES
money
oj
ox
rack-contrib
rack-cors
rack-test
rake
roda
rubocop-minitest
rubocop-performance
rubocop-sequel
rufus-scheduler
sequel_pg
simplecov
sinatra
unicorn
vcr
webmock

View File

@ -3,4 +3,4 @@
require './config/environment'
require 'web/server'
run Sinatra::Application
run Web::Server.freeze.app

View File

@ -1,103 +1,93 @@
# frozen_string_literal: true
require 'oj'
require 'rack/contrib/jsonp'
require 'rack/cors'
require 'sinatra'
require 'roda'
require 'currency_names'
require 'query'
require 'quote'
module Web
class Server < Roda
use Rack::Cors do
allow do
origins '*'
resource '*', headers: :any, methods: :get
resource '*', headers: :any, methods: %i[get options]
end
end
use Rack::JSONP
plugin :caching
plugin :error_handler do |_error|
request.halt [422, {}, nil]
end
plugin :indifferent_params
plugin :json, content_type: 'application/json; charset=utf-8',
serializer: ->(o) { Oj.dump(o, mode: :compat) }
plugin :params_capturing
route do |r|
r.root do
{ docs: 'https://www.frankfurter.app/docs' }
end
r.is(/latest|current/) do
r.params['date'] = Date.today.to_s
quote = quote_end_of_day(r)
r.etag quote.cache_key
quote.formatted
end
r.is(/(\d{4}-\d{2}-\d{2})/) do
r.params['date'] = r.params['captures'].first
quote = quote_end_of_day(r)
r.etag quote.cache_key
quote.formatted
end
r.is(/(\d{4}-\d{2}-\d{2})\.\.(\d{4}-\d{2}-\d{2})?/) do
r.params['start_date'] = r.params['captures'].first
r.params['end_date'] = r.params['captures'][1] || Date.today.to_s
quote = quote_interval(r)
r.etag quote.cache_key
quote.formatted
end
r.is 'currencies' do
currency_names = CurrencyNames.new
r.etag currency_names.cache_key
currency_names.formatted
end
end
configure :development do
set :show_exceptions, :after_handler
end
private
configure :production do
disable :dump_errors
end
configure :test do
set :raise_errors, false
end
set :static_cache_control, [:public, max_age: 300]
helpers do
def end_of_day_quote
@end_of_day_quote ||= begin
query = Query.build(params)
def quote_end_of_day(request)
query = Query.build(request.params)
quote = Quote::EndOfDay.new(**query)
quote.perform
halt 404 if quote.not_found?
request.halt [404, {}, nil] if quote.not_found?
quote
end
end
def interval_quote
@interval_quote ||= begin
query = Query.build(params)
def quote_interval(request)
query = Query.build(request.params)
quote = Quote::Interval.new(**query)
quote.perform
halt 404 if quote.not_found?
request.halt [404, {}, nil] if quote.not_found?
quote
end
end
def json(data)
json = Oj.dump(data, mode: :compat)
callback = params['callback']
if callback
content_type :js, charset: Encoding::UTF_8
"#{callback}(#{json})"
else
content_type :json, charset: Encoding::UTF_8
json
end
end
end
get '/' do
json({ docs: 'https://www.frankfurter.app/docs' })
end
get '/(?:latest|current)', mustermann_opts: { type: :regexp } do
params[:date] = Date.today.to_s
etag end_of_day_quote.cache_key
json end_of_day_quote.formatted
end
get '/(?<date>\d{4}-\d{2}-\d{2})', mustermann_opts: { type: :regexp } do
etag end_of_day_quote.cache_key
json end_of_day_quote.formatted
end
get '/(?<start_date>\d{4}-\d{2}-\d{2})\.\.(?<end_date>\d{4}-\d{2}-\d{2})?',
mustermann_opts: { type: :regexp } do
@params[:end_date] ||= Date.today.to_s
etag interval_quote.cache_key
json interval_quote.formatted
end
get '/currencies' do
currency_names = CurrencyNames.new
etag currency_names.cache_key
json currency_names.formatted
end
not_found do
halt 404
end
error do
halt 422
end

View File

@ -7,7 +7,7 @@ require 'web/server'
describe 'the server' do
include Rack::Test::Methods
let(:app) { Sinatra::Application }
let(:app) { Web::Server.freeze }
def json
Oj.load(last_response.body)

View File

@ -7,7 +7,7 @@ require 'web/server'
describe 'the server' do
include Rack::Test::Methods
let(:app) { Sinatra::Application }
let(:app) { Web::Server.freeze }
let(:json) { Oj.load(last_response.body) }
let(:headers) { last_response.headers }
@ -100,11 +100,11 @@ describe 'the server' do
it 'handles JSONP' do
get '/latest?callback=foo'
_(last_response.body).must_be :start_with?, 'foo'
_(last_response.body).must_be :start_with?, '/**/foo'
end
it 'sets charset to UTF-8' do
it 'sets charset to utf-8' do
get '/currencies'
_(last_response.headers['content-type']).must_be :end_with?, 'charset=UTF-8'
_(last_response.headers['content-type']).must_be :end_with?, 'charset=utf-8'
end
end