Rack in Rails 3
<http://twitter.com/rtomayko> Ryan Tomayko GitHub • Rack (Core Team) • Rack::Contrib (Maintainer) • Sinatra (Committer) • Rack::Cache (Maintainer) • Shotgun (Maintainer)
"Get a better logo, guys." -- Tim Bray
The Rack SPEC <http://rack.rubyforge.org/doc/SPEC.html> • "Just" a document. • Conventions for expressing HTTP in Ruby. • Interface, Contract, Agreement, Protocol. • Based on Python's WSGI. • Fits on a single slide.
Expressing HTTP in Ruby
HTTP Request GET /hello HTTP/1.1 Host: localhost:9292 Connection: keep‐alive User‐Agent: Mozilla/5.0 (Macinto... Accept: application/xml,applicati... Accept‐Encoding: gzip,deflate,sdch Accept‐Language: en‐US,en;q=0.8 Accept‐Charset: UTF‐8,*;q=0.5 Cache‐Control: max‐age=0 Cookie: foo=bar
HTTP Request Rack "env" Hash { " REQUEST_METHOD "=>"GET", GET /hello HTTP/1.1 " PATH_INFO "=>"/hello", Host: localhost:9292 " SCRIPT_NAME "=>"", Connection: keep‐alive " SERVER_NAME "=>"localhost", User‐Agent: Mozilla/5.0 (Macinto... " SERVER_PORT "=>"9292", Accept: application/xml,applicati... " REMOTE_ADDR "=>"127.0.0.1", Accept‐Encoding: gzip,deflate,sdch " QUERY_STRING "=>"", Accept‐Language: en‐US,en;q=0.8 " HTTP_HOST "=>"localhost:9292", Accept‐Charset: UTF‐8,*;q=0.5 " HTTP_CONNECTION "=>"keep‐alive", Cache‐Control: max‐age=0 " HTTP_USER_AGENT "=>"Mozilla/5.0 (...", Cookie: foo=bar " HTTP_ACCEPT "=>"application/xml,a...", " HTTP_ACCEPT_ENCODING "=>"gzip,def...", " HTTP_ACCEPT_LANGUAGE "=>"en‐US,en...", " HTTP_ACCEPT_CHARSET "=>"UTF‐8,*;q=0.5", " HTTP_CACHE_CONTROL "=>"max‐age=0", " HTTP_COOKIE "=>"foo=bar", " rack.url_scheme "=>"http", " rack.input "=>#<StringIO:0x10841ec>, " rack.errors "=>#<IO:0x3a8d4c>, " rack.version "=>[1, 1], " rack.run_once "=>false, " rack.multithread "=>true, " rack.multiprocess "=>false }
HTTP Response HTTP/1.1 200 OK Connection: close Date: Tue, 16 Feb 2010 23:49:18 GMT Content‐Type: text/plain Content‐Length: 11 Cache‐Control: max‐age=60 Hello World
HTTP Response Rack Response HTTP/1.1 200 OK [ Connection: close 200 , Date: Tue, 16 Feb 2010 23:49:18 GMT {' Content‐Type ' => 'text/plain', Content‐Type: text/plain ' Content‐Length ' => '11', Content‐Length: 11 ' Cache‐Control ' => 'max‐age=60'}, Cache‐Control: max‐age=60 [" Hello World "] ] Hello World
HTTP Response Rack Response HTTP/1.1 200 OK [ Connection: close 200 , Date: Tue, 16 Feb 2010 23:49:18 GMT {' Content‐Type ' => 'text/plain', Content‐Type: text/plain ' Content‐Length ' => '11', Content‐Length: 11 ' Cache‐Control ' => 'max‐age=60'}, Cache‐Control: max‐age=60 [" Hello World "] ] Hello World OR [ 200 , {' Content‐Type ' => 'text/plain', ' Content‐Length ' => File.size('hello.txt').to_s , ' Cache‐Control ' => 'max‐age=60'}, File.open('hello.txt', 'rb') ]
A Rack application is an Ruby object (not a class) that responds to call . It takes exactly one argument, the environment and returns an Array of exactly three values: the status , the headers , and the body .
class HelloWorldApp def call (env) [ 200, {'Content‐Type' => 'text/plain'}, [ "Hello World" ]] end end app = HelloWorldApp . new
class HelloWorldApp def call (env) [ 200, {'Content‐Type' => 'text/plain'}, [ "Hello World" ]] end end app = HelloWorldApp . new OR app = lambda { | env | [ 200, {'Content‐Type' => 'text/plain'}, [ "Hello World" ]] }
We haven't used any Rack APIs yet.
Interoperability Servers Frameworks WEBrick Rails CGI Sinatra FastCGI Merb Mongrel Ramaze Rack Thin SPEC Camping Passenger Coset Unicorn Rango Heroku Just Rack
Interoperability Servers Frameworks WEBrick Rails CGI Sinatra FastCGI Merb Mongrel Ramaze Rack Thin SPEC Camping Passenger Coset Unicorn Rango Heroku Just Rack
Architecture of Intermediaries
HTTP Intermediaries HTTP Alice HTTP Company HTTP HTTP Regional Proxy App 1 Proxy Auth + Logging Load Balancing HTTP HTTP HTTP Compression Bob HTTP HTTP App 2 HTTP HTTP Carol separate process inside example.com firewall
HTTP Intermediaries HTTP Alice HTTP Company HTTP HTTP Regional Proxy App 1 Proxy Auth + Logging Load Balancing HTTP HTTP HTTP Compression Bob HTTP HTTP App 2 HTTP HTTP Carol separate process inside example.com firewall
Middleware class ShoutMiddleware def initialize (app) @app = app end def call (env) status, headers, body = @app . call(env) parts = [] body . each { | part | parts << part . upcase } [ status, headers, parts ] end end
Middleware class ShoutMiddleware def initialize (app) @app = app end def call (env) status, headers, body = @app . call(env) parts = [] body . each { | part | parts << part . upcase } [ status, headers, parts ] end end app = lambda { | env | [ 200, {'Content‐Type' => 'text/plain'}, [ "hello world" ]] } stream = ShoutMiddleware . new(app) Rack :: Handler :: Mongrel . run(stream, :Port => 8080)
Rack::ETag require 'digest/md5' module Rack class ETag def initialize (app) @app = app end def call (env) status, headers, body = @app . call(env) if ! headers . has_key?('ETag') parts = [] body . each { | part | parts << part . to_s } headers [ 'ETag' ] = %("#{Digest :: MD5 . hexdigest(parts . join(""))}") [ status, headers, parts ] else [ status, headers, body ] end end end end
Rack Middleware <http://github.com/rack/rack/tree/master/lib/rack> Content Modifying Behavioral Rack::Chunked Rack::CommonLogger Rack::ContentLength Rack::Lint Rack::ConditionalGet Rack::Lock Rack::ContentType Rack::Reloader Rack::Deflater Rack::ETag Routing Rack::Head Rack::MethodOverride Rack::Cascade Rack::Runtime Rack::Recursive Rack::Sendfile Rack::Static Rack::ShowStatus Rack::URLMap
Rack::Contrib <http://github.com/rack/rack‐contrib> Rack::AcceptFormat Rack::MailExceptions Rack::Access Rack::NestedParams Rack::Backstage Rack::NotFound Rack::Callbacks Rack::ProcTitle Rack::Config Rack::Profiler Rack::Cookies Rack::ResponseCache Rack::CSSHTTPRequest Rack::ResponseHeaders Rack::Deflect Rack::RelativeRedirect Rack::Evil Rack::Signals Rack::HostMeta Rack::StaticCache Rack::JSONP Rack::SimpleEndpoint Rack::LighttpdScriptNameFix Rack::TimeZone Rack::Locale
<http://coderack.org>
<http://coderack.org> 99 Pieces of Middleware
ActionDispatch <http://github.com/rails/rails/tree/master/actionpack/lib/action_dispatch > • Middleware extracted from ActionController • ActionDispatch::MiddlewareStack • Request/Response objects • Routing • Integration Testing Support
Default Middleware $ rake middleware use ActionDispatch::Static use Rack::Lock use Rack::Runtime use Rails::Rack::Logger use ActionDispatch::ShowExceptions use ActionDispatch::Callbacks use ActionDispatch::Cookies use ActionDispatch::Session::CookieStore use ActionDispatch::Flash use ActionDispatch::ParamsParser use Rack::MethodOverride use ActionDispatch::Head use ActiveRecord::ConnectionAdapters::ConnectionManagement use ActiveRecord::QueryCache run Helloworld::Application.routes
Recommend
More recommend