Upload
pivorak-meetup
View
270
Download
0
Embed Size (px)
Citation preview
RubyMeditation
W A R N I N GTHIS TALK CONTAINSLOTS
OF CODETHIS IS YOUR LAST CHANCE TO LEAVE AUDITORY
Problems with rails
• Low latency
• Dependency hell
• MVC is only suitable for simple CRUD
• ActiveSupport
Other frameworks
• grape
• sinatra
• rum
• nyny
And…
Rack
run ->(env) { [ 200, # <= Response code {'Content-Type' => 'application/json'}, # <= Headers [ '{"a": 1}' ] # <= Body ]}
require 'json'
run ->(env) { [ 200, {'Content-Type' => 'application/json'}, [ JSON.dump({ a: 1 }) ] # <= Almost API ;) ]}
Add some OOP
run ->(env) { [ 200, # <= Response code {'Content-Type' => 'application/json'}, # <= Headers [ '{"a": 1}' ] # <= Body ]}
class Responder def response_code 200 end
def headers {'Content-Type' => 'application/json'} end
def body [ JSON.dump({ a: 1 }) ] endend
class Responder def response_code @code end
def headers @headers end
def body [ JSON.dump(@body) ] endend
class Example < Responder def initialize(env) @code = 200 @headers = {'Content-Type' => 'application/json'} @body = { a: 1 } endend
class ReadUsers < Responder def initialize(env) @code = 200 @headers = {'Content-Type' => 'application/json'} @body = DB[:users].all endend
run ->(env) { result = ReadUsers.new(env) [result.response_code, result.headers, result.body]}
result = ReadUsers.new(env)r = Nginx::Request.new
result.headers.each_pair { |k, v| r.headers_out[k] = v }Nginx.rputs result.body[0]Nginx.return result.response_code
Less generic example
Good API
• Proper status codes
• Compatibility (?suppress_response_code=true)
• Metadata
class Responder
class << self def call(env) req = ::Rack::Request.new(env) instance = new(req) instance.call instance.to_rack_array end end
attr_reader :request, :params, :headers
def initialize(req) @request = req @params = req.params @headers = default_response_headers end
def call; end
def to_rack_array [http_response_code, http_response_headers, http_response_body] end
end
class Responder
def response_code @response_code || default_response_code end
private
def default_response_code 200 end
def http_response_code params['suppress_response_codes'] ? 200 : response_code end
end
class Responder
def default_response_headers { 'Content-Type' => 'application/json' }.dup end
def http_response_headers @headers end
end
class Responder
def body @body end
private
def http_response_body [ JSON.dump(body) ] end
end
class ReadUsers < Responder def call @body = DB[:users].all endend
class Read < Responder def call @body = fetch endend
class ReadUsers < Read def fetch DB[:users].all endend
class Write < Responder def call @body = valid_params? ? success : failure end
private
def success; end def failure; end def valid_params? true endend
class CreateUser < Write def default_response_code 201 end
def valid_params? params['login'] && params['email'] end
def success DB[:users].insert(params) end
def failure @response_code = 400
{ error: 'Invalid params' } endend
class Responder
def body { code: http_response_code, result: @body, meta: meta } end
def meta { server_time: Time.now.to_i } end
end
{ "code": 200, "result": [ { "id": 1, "name": "Andriy Savchenko", "email": "[email protected]", "company": "Aejis", "hiring": true } ], "meta": { "server_time": 1447939835 }}
Awesome!
Routers
• Rack::Builder
• http_router (gh:joshbuddy/http_router)
• lotus-router (gh:lotus/router)
• signpost (gh:Ptico/signpost)
• journey (dead)
Advantages
Faster$ ruby -vruby 2.2.3p173 (2015-08-18 revision 51636) [x86_64-darwin15]
$ puma -e production
$ ab -n 10000 -c 100 http://0.0.0.0:9292/users/
|======================|====Rails-API====|=====Sinatra=====|=====Rack API====||Time taken for tests: | 13.262 seconds | 6.858 seconds | 3.665 seconds ||Complete requests: | 10000 | 10000 | 10000 ||Failed requests: | 0 | 0 | 0 ||Requests per second: | 754.03 [#/sec] | 1458.20 [#/sec] | 2728.28 [#/sec] ||Time per request: | 132.620 [ms] | 68.578 [ms] | 36.653 [ms] ||Time per request (c): | 1.326 [ms] | 0.686 [ms] | 0.367 [ms] ||Transfer rate: | 301.91 [KB/sec] | 262.02 [KB/sec] | 402.31 [KB/sec] ||============================================================================|
Faster
• 4x faster then rails-api & 2x then sinatra
• Ready for further improvements
Magic-less
• Base responder takes ≈ 65LOC
• The only dependency is Rack (optional)
Maintainable
• Stable object interface
• Each responder can have its own file structure
• SOLID
• Test-friendly
Questions?
Credits and attributions:• Title illustration by Max Bohdanowski • Lobster Two font by Pablo Impallari & Igino Marini (OFL) • Font Awesome by Dave Gandy - http://fontawesome.io (OFL) • https://www.flickr.com/photos/mattsh/14194586111/ (CC BY-NC-SA 2.0)
Andriy Savchenko ! " # $/ptico