Professional Documents
Culture Documents
<http://twitter.com/rtomayko>
Ryan Tomayko
GitHub
Rack (Core Team) Rack::Contrib (Maintainer) Sinatra (Committer) Rack::Cache (Maintainer) Shotgun (Maintainer)
"Just" a document. Conventions for expressing HTTP in Ruby. Interface, Contract, Agreement, Protocol. Based on Python's WSGI. Fits on a single slide.
HTTP Request
GET/helloHTTP/1.1 Host:localhost:9292 Connection:keepalive UserAgent:Mozilla/5.0(Macinto... Accept:application/xml,applicati... AcceptEncoding:gzip,deflate,sdch AcceptLanguage:enUS,en;q=0.8 AcceptCharset:UTF8,*;q=0.5 CacheControl:maxage=0 Cookie:foo=bar
HTTP Request
GET/helloHTTP/1.1 Host:localhost:9292 Connection:keepalive UserAgent:Mozilla/5.0(Macinto... Accept:application/xml,applicati... AcceptEncoding:gzip,deflate,sdch AcceptLanguage:enUS,en;q=0.8 AcceptCharset:UTF8,*;q=0.5 CacheControl:maxage=0 Cookie:foo=bar
HTTP Response
HTTP/1.1200OK Connection:close Date:Tue,16Feb201023:49:18GMT ContentType:text/plain ContentLength:11 CacheControl:maxage=60 HelloWorld
HTTP Response
HTTP/1.1200OK Connection:close Date:Tue,16Feb201023:49:18GMT ContentType:text/plain ContentLength:11 CacheControl:maxage=60 HelloWorld
Rack Response
[ 200, {'ContentType'=>'text/plain', 'ContentLength'=>'11', 'CacheControl'=>'maxage=60'}, ["HelloWorld"] ]
HTTP Response
HTTP/1.1200OK Connection:close Date:Tue,16Feb201023:49:18GMT ContentType:text/plain ContentLength:11 CacheControl:maxage=60 HelloWorld
Rack Response
[ 200, {'ContentType'=>'text/plain', 'ContentLength'=>'11', 'CacheControl'=>'maxage=60'}, ["HelloWorld"] ]
OR
[ 200, {'ContentType'=>'text/plain', 'ContentLength'=>File.size('hello.txt').to_s, 'CacheControl'=>'maxage=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.
OR
app=lambda{|env| [200,{'ContentType'=>'text/plain'},["HelloWorld"]]}
Interoperability
Servers
WEBrick CGI FastCGI Mongrel Thin Passenger Unicorn Heroku
Frameworks
Rails Sinatra Merb Ramaze Camping Coset Rango Just Rack
Rack SPEC
Interoperability
Servers
WEBrick CGI FastCGI Mongrel Thin Passenger Unicorn Heroku
Frameworks
Rails Sinatra Merb Ramaze Camping Coset Rango Just Rack
Rack SPEC
Architecture of Intermediaries
HTTP Intermediaries
Alice
HTTP
Regional Proxy
Auth + Logging
Bob
Compression
HTTP
HTTP
HTTP
Load Balancing
Company Proxy
HT T
HTTP HTTP
App 1
HTTP
HTTP
HTT
HTTP
App 2
Carol
HTTP Intermediaries
Alice
HTTP
Regional Proxy
Auth + Logging
Bob
Compression
HTTP
HTTP
HTTP
Load Balancing
Company Proxy
HT T
HTTP HTTP
App 1
HTTP
HTTP
HTT
HTTP
App 2
Carol
Middleware
classShoutMiddleware definitialize(app) @app=app end defcall(env) status,headers,body=@app.call(env) parts=[] body.each{|part|parts<<part.upcase} [status,headers,parts] end end
Middleware
classShoutMiddleware definitialize(app) @app=app end defcall(env) status,headers,body=@app.call(env) parts=[] body.each{|part|parts<<part.upcase} [status,headers,parts] end end app=lambda{|env| [200,{'ContentType'=>'text/plain'},["helloworld"]]} stream=ShoutMiddleware.new(app) Rack::Handler::Mongrel.run(stream,:Port=>8080)
Rack::ETag
require'digest/md5' moduleRack classETag definitialize(app) @app=app end defcall(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
Rack::Chunked Rack::ContentLength Rack::ConditionalGet Rack::ContentType Rack::Deater Rack::ETag Rack::Head Rack::MethodOverride Rack::Runtime Rack::Sendle Rack::ShowStatus
Behavioral
Rack::CommonLogger Rack::Lint Rack::Lock Rack::Reloader
Routing
Rack::Cascade Rack::Recursive Rack::Static Rack::URLMap
Rack::Contrib
<http://github.com/rack/rackcontrib>
Rack::AcceptFormat Rack::Access Rack::Backstage Rack::Callbacks Rack::Cong Rack::Cookies Rack::CSSHTTPRequest Rack::Deect Rack::Evil Rack::HostMeta Rack::JSONP Rack::LighttpdScriptNameFix Rack::Locale
Rack::MailExceptions Rack::NestedParams Rack::NotFound Rack::ProcTitle Rack::Proler Rack::ResponseCache Rack::ResponseHeaders Rack::RelativeRedirect Rack::Signals Rack::StaticCache Rack::SimpleEndpoint Rack::TimeZone
<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
$rakemiddleware useActionDispatch::Static useRack::Lock useRack::Runtime useRails::Rack::Logger useActionDispatch::ShowExceptions useActionDispatch::Callbacks useActionDispatch::Cookies useActionDispatch::Session::CookieStore useActionDispatch::Flash useActionDispatch::ParamsParser useRack::MethodOverride useActionDispatch::Head useActiveRecord::ConnectionAdapters::ConnectionManagement useActiveRecord::QueryCache runHelloworld::Application.routes
AD::MiddlewareStack
require'rack/cache' require'rack/contrib'
cong/application.rb
moduleHelloworld classApplication<Rails::Application #addsthenewmiddlewareatthebottomofthestack config.middleware.useRack::Cache,:verbose=>true #addsthenewmiddlewarebeforeanexistingmiddleware config.middleware.insert_beforeRack::Lock,Rack::ProcTitle #addsthenewmiddlewareafteranexistingmiddleware config.middleware.insert_afterRack::Head,Rack::CSSHTTPRequest #swaponemiddlewareforanother config.middleware.swapActionDispatch::ShowExceptions,SuperShowExceptions #removeamiddlewarefromthestack config.middleware.deleteRack::MethodOverride end end
Middleware Plugins
<http://boldr.net/upgradepluginsgemsrails3>
require"rack/cache"
rails-cache.gem/lib/rails/cache.rb
moduleCache classRailtie<Rails::Railtie railtie_name:rails_cache initializer"rails_cache.insert_rack_cache"do|app| app.config.middleware.useRack::Cache, :metastore=>"file:#{Rails.root+"cache/rack/meta"}", :entitystore=>"file:#{Rails.root+"cache/rack/body"}", :verbose=>true end end end
## Bundle the gems you use: gem "rails-cache", :require => "rails/cache"
Gemle
Rack::Mount
<http://github.com/josh/rackmount> cong.ru require'rack/mount' Routes=Rack::Mount::RouteSet.newdo|set| #add_routetakesarackapplicationandconditionstomatchwith #conditionsmaybestringsorregexps #SeeRack::Mount::RouteSet#add_routeformoreoptions. set.add_routeFooApp,:method=>'get',:path=>%{/foo} end #Theroutesetitselfisasimplerackappyoumount runRoutes
Rack::Mount
<http://github.com/josh/rackmount> cong.ru require'rack/mount' Routes=Rack::Mount::RouteSet.newdo|set| #add_routetakesarackapplicationandconditionstomatchwith #conditionsmaybestringsorregexps #SeeRack::Mount::RouteSet#add_routeformoreoptions. set.add_routeFooApp,:method=>'get',:path=>%{/foo} end #Theroutesetitselfisasimplerackappyoumount runRoutes
Rack::Mount
<http://github.com/josh/rackmount> cong.ru require'rack/mount' Routes=Rack::Mount::RouteSet.newdo|set| #add_routetakesarackapplicationandconditionstomatchwith #conditionsmaybestringsorregexps #SeeRack::Mount::RouteSet#add_routeformoreoptions. set.add_routeFooApp,:method=>'get',:path=>%{/foo} end #Theroutesetitselfisasimplerackappyoumount runRoutes
Routing to Rack
<http://lindsaar.net/2010/2/7/rails_3_routing_with_rack>
classTwitterApp<Sinatra::Base set:root,File.dirname(__FILE__) get'/twitter'do @user='raasdnil' t=Twitter::Search.new(@user).fetch @tweets=t.results erb:twitter end end require'twitter_app' HelloWorldApp::Application.routes.drawdo|map| match'/twitter',:to=>TwitterApp end
lib/twitter_app.rb
cong/routes.rb