Rack in Rails 3 <http://twitter.com/rtomayko> Ryan Tomayko - - PowerPoint PPT Presentation

rack in rails 3
SMART_READER_LITE
LIVE PREVIEW

Rack in Rails 3 <http://twitter.com/rtomayko> Ryan Tomayko - - PowerPoint PPT Presentation

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


slide-1
SLIDE 1

Rack in Rails 3

slide-2
SLIDE 2

Ryan Tomayko

  • Rack (Core Team)
  • Rack::Contrib (Maintainer)
  • Sinatra (Committer)
  • Rack::Cache (Maintainer)
  • Shotgun (Maintainer)

<http://twitter.com/rtomayko>

GitHub

slide-3
SLIDE 3

"Get a better logo, guys." -- Tim Bray

slide-4
SLIDE 4

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.
slide-5
SLIDE 5
slide-6
SLIDE 6

Expressing HTTP in Ruby

slide-7
SLIDE 7

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

slide-8
SLIDE 8

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

{"REQUEST_METHOD"=>"GET", 
"PATH_INFO"=>"/hello", 
"SCRIPT_NAME"=>"", 
"SERVER_NAME"=>"localhost", 
"SERVER_PORT"=>"9292", 
"REMOTE_ADDR"=>"127.0.0.1", 
"QUERY_STRING"=>"", 
"HTTP_HOST"=>"localhost:9292", 
"HTTP_CONNECTION"=>"keep‐alive", 
"HTTP_USER_AGENT"=>"Mozilla/5.0
(...", 
"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}

Rack "env" Hash

slide-9
SLIDE 9

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

slide-10
SLIDE 10

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

[ 

200, 

{'Content‐Type'


=>
'text/plain', 


'Content‐Length'
=>
'11', 


'Cache‐Control'

=>
'max‐age=60'}, 

["Hello
World"] ]

Rack Response

slide-11
SLIDE 11

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

[ 

200, 

{'Content‐Type'


=>
'text/plain', 


'Content‐Length'
=>
'11', 


'Cache‐Control'

=>
'max‐age=60'}, 

["Hello
World"] ]

Rack Response

[ 

200, 

{'Content‐Type '


=>
'text/plain', 


'Content‐Length '
=>
File.size('hello.txt').to_s , 


'Cache‐Control '

=>
'max‐age=60'}, 

File.open('hello.txt',
'rb') ]

OR

slide-12
SLIDE 12

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.

slide-13
SLIDE 13

class
HelloWorldApp 

def
call(env) 



[200, 




{'Content‐Type'
=>
'text/plain'}, 




["Hello
World"]] 

end end 
 app
=
HelloWorldApp.new

slide-14
SLIDE 14

class
HelloWorldApp 

def
call(env) 



[200, 




{'Content‐Type'
=>
'text/plain'}, 




["Hello
World"]] 

end end 
 app
=
HelloWorldApp.new app
=
lambda
{
|env| 

[200,
{'Content‐Type'=>'text/plain'},
["Hello
World"]]
}

OR

slide-15
SLIDE 15

We haven't used any Rack APIs yet.

slide-16
SLIDE 16

Interoperability

WEBrick CGI FastCGI Mongrel Thin Passenger Unicorn Heroku Rails Sinatra Merb Ramaze Camping Coset Rango Just Rack

Servers Frameworks

Rack SPEC

slide-17
SLIDE 17

Interoperability

WEBrick CGI FastCGI Mongrel Thin Passenger Unicorn Heroku Rails Sinatra Merb Ramaze Camping Coset Rango Just Rack

Servers Frameworks

Rack SPEC

slide-18
SLIDE 18

Architecture of Intermediaries

slide-19
SLIDE 19

HTTP Intermediaries

Company Proxy

Alice Bob Carol

HTTP HTTP HTTP HTTP HTTP

Auth + Logging Compression

Regional Proxy

Load Balancing

App 1 App 2

HTTP HTTP HTTP HTTP HTTP HTTP

separate process inside example.com firewall

slide-20
SLIDE 20

HTTP Intermediaries

Company Proxy

Alice Bob Carol

HTTP HTTP HTTP HTTP HTTP

Auth + Logging Compression

Regional Proxy

Load Balancing

App 1 App 2

HTTP HTTP HTTP HTTP HTTP HTTP

separate process inside example.com firewall

slide-21
SLIDE 21

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

slide-22
SLIDE 22

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

app
=
lambda
{
|env| 

[200,
{'Content‐Type'=>'text/plain'},
["hello
world"]]
} stream
=
ShoutMiddleware.new(app) Rack::Handler::Mongrel.run(stream,
:Port
=>
8080)

slide-23
SLIDE 23

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

slide-24
SLIDE 24

Rack Middleware

Rack::Chunked Rack::ContentLength Rack::ConditionalGet Rack::ContentType Rack::Deflater Rack::ETag Rack::Head Rack::MethodOverride Rack::Runtime Rack::Sendfile Rack::ShowStatus Rack::CommonLogger Rack::Lint Rack::Lock Rack::Reloader Rack::Cascade Rack::Recursive Rack::Static Rack::URLMap

Routing Behavioral Content Modifying

<http://github.com/rack/rack/tree/master/lib/rack>

slide-25
SLIDE 25

Rack::Contrib

Rack::AcceptFormat Rack::Access Rack::Backstage Rack::Callbacks Rack::Config Rack::Cookies Rack::CSSHTTPRequest Rack::Deflect Rack::Evil Rack::HostMeta Rack::JSONP Rack::LighttpdScriptNameFix Rack::Locale Rack::MailExceptions Rack::NestedParams Rack::NotFound Rack::ProcTitle Rack::Profiler Rack::ResponseCache Rack::ResponseHeaders Rack::RelativeRedirect Rack::Signals Rack::StaticCache Rack::SimpleEndpoint Rack::TimeZone

<http://github.com/rack/rack‐contrib>

slide-26
SLIDE 26

<http://coderack.org>

slide-27
SLIDE 27

<http://coderack.org>

99 Pieces of Middleware

slide-28
SLIDE 28
slide-29
SLIDE 29

ActionDispatch

  • Middleware extracted from ActionController
  • ActionDispatch::MiddlewareStack
  • Request/Response objects
  • Routing
  • Integration Testing Support

<http://github.com/rails/rails/tree/master/actionpack/lib/action_dispatch >

slide-30
SLIDE 30

$
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

Default Middleware

slide-31
SLIDE 31

AD::MiddlewareStack

require
'rack/cache' require
'rack/contrib' module
Helloworld 

class
Application
<
Rails::Application 



#
adds
the
new
middleware
at
the
bottom
of
the
stack 



config.middleware.use
Rack::Cache,
:verbose
=>
true 
 



#
adds
the
new
middleware
before
an
existing
middleware 



config.middleware.insert_before
Rack::Lock,
Rack::ProcTitle 
 



#
adds
the
new
middleware
after
an
existing
middleware 



config.middleware.insert_after
Rack::Head,
Rack::CSSHTTPRequest 
 



#
swap
one
middleware
for
another 



config.middleware.swap
ActionDispatch::ShowExceptions,
SuperShowExceptions 
 



#
remove
a
middleware
from
the
stack 



config.middleware.delete
Rack::MethodOverride 

end end

config/application.rb

slide-32
SLIDE 32

Middleware Plugins

require
"rack/cache" module
Cache 

class
Railtie
<
Rails::Railtie 



railtie_name
:rails_cache 
 



initializer
"rails_cache.insert_rack_cache"
do
|app| 





app.config.middleware.use
Rack::Cache, 







:metastore


=>
"file:#{Rails.root
+
"cache/rack/meta"}", 







:entitystore
=>
"file:#{Rails.root
+
"cache/rack/body"}", 







:verbose




=>
true 



end 

end end

<http://boldr.net/upgrade‐plugins‐gems‐rails‐3 >

## Bundle the gems you use: gem "rails-cache", :require => "rails/cache"

rails-cache.gem/lib/rails/cache.rb Gemfile

slide-33
SLIDE 33

Rack::Mount

require
'rack/mount' Routes
=
Rack::Mount::RouteSet.new
do
|set| 

#
add_route
takes
a
rack
application
and
conditions
to
match
with 

#
conditions
may
be
strings
or
regexps 

#
See
Rack::Mount::RouteSet#add_route
for
more
options. 

set.add_route
FooApp,
:method
=>
'get',
:path
=>
%{/foo} end 
 #
The
route
set
itself
is
a
simple
rack
app
you
mount run
Routes

<http://github.com/josh/rack‐ mount> config.ru

slide-34
SLIDE 34

Rack::Mount

require
'rack/mount' Routes
=
Rack::Mount::RouteSet.new
do
|set| 

#
add_route
takes
a
rack
application
and
conditions
to
match
with 

#
conditions
may
be
strings
or
regexps 

#
See
Rack::Mount::RouteSet#add_route
for
more
options. 

set.add_route
FooApp,
:method
=>
'get',
:path
=>
%{/foo} end 
 #
The
route
set
itself
is
a
simple
rack
app
you
mount run
Routes

<http://github.com/josh/rack‐ mount> config.ru

slide-35
SLIDE 35

Rack::Mount

require
'rack/mount' Routes
=
Rack::Mount::RouteSet.new
do
|set| 

#
add_route
takes
a
rack
application
and
conditions
to
match
with 

#
conditions
may
be
strings
or
regexps 

#
See
Rack::Mount::RouteSet#add_route
for
more
options. 

set.add_route
FooApp,
:method
=>
'get',
:path
=>
%{/foo} end 
 #
The
route
set
itself
is
a
simple
rack
app
you
mount run
Routes

<http://github.com/josh/rack‐ mount> config.ru

slide-36
SLIDE 36

Actions = Rack Apps

$
rails
console Loading
development
environment
(Rails
3.0.0.beta) >>
action
=
ApplicationController.action(:index) =>
#<ActionController::Metal::ActionEndpoint:0x2290fd4 




@action=:index, 




@controller=ApplicationController, 




@_formats=<stuff>> >>
action.respond_to?(:call) =>
true

slide-37
SLIDE 37

Routing to Rack

class
TwitterApp
<
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

<http://lindsaar.net/2010/2/7/rails_3_routing_with_rack >

require
'twitter_app' 
 HelloWorldApp::Application.routes.draw
do
|map| 

match
'/twitter',
:to
=>
TwitterApp end lib/twitter_app.rb config/routes.rb

slide-38
SLIDE 38

More Rack + Rails

RailsGuides: Rails on Rack (Updated for Rails 3)

<http://guides.rails.info/rails_on_rack.html>

Scaling Rails: Rack & Metal (Screencast)

<http://railslab.newrelic.com/2009/06/05/episode-14-rack-metal>

Rack API Documentation

<http://rack.rubyforge.org/doc/>

Rack SPEC (Seriously)

<http://rack.rubyforge.org/doc/SPEC.html>

Rack Source (On The GitHub)

<http://github.com/rack/rack>