Your best friend Rack
What is Rack?
     Rack provides a minimal interface

“     between webservers supporting
       Ruby and Ruby frameworks.

Transparently integrates in your existing
           rails, sinatra apps

 Helps you easily create a middleware
     standalone application stack
Yeah! but... what is
         Rack?
 A Rack application is a Ruby object
  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.
A basic Rack app
    diagram
Minimum server
  interface you said?
app = lambda d o |env|
  [200,
    { 'Content-Type' => 'text/html'
   'Hello World']
end
run app
Where does Rack sit?
Rack is a full
middleware stack
Allows a modular design
What is a middleware?
    Rack application that is designed

“     to run in conjunction with
    another Rack application, which
          acts as the endpoint
...
 Think of a Rack middleware as a filter
receiving the Rack environment for the
request from the previous middleware

   Does some work with or on the
      request's environment

Then calls the next middleware in the
                chain
...
      The last Rack application in the

“      chain is the application itself

     Any middleware in the chain can
return the Rack response itself, thus
preventing the rest of the middlewares
     in the chain from executing
Installation
$... $ gem install rack
Successfully installed rack-1.2.1
1 gem installed
Installing ri documentation for rack-1.2.1...
Building YARD (yri) index for rack-1.2.1...
Installing RDoc documentation for rack-1.2.1...
Let's talk more Rack
status, headers, body = object

[200,
  { 'Content-Type' => 'text/html' }
  'Hello World']
Stacking apps
m o d u l e MyModule
   c l a s s Upcase
       d e f initialize app
           @app = app
       end
    d e f call env
       p 'upcase'
       status, headers, body =
       [status, headers, [body.
    end
  end
end
...
m o d u l e MyModule
   c l a s s Reverse
       d e f initialize app
           @app = app
       end
    d e f call env
       p 'reverse'
       status, headers, body =
       [status, headers, [body.
    end
  end
end
...
use MyModule::Upcase
use MyModule::Reverse
use Rack::ContentLength
app = lambda { |env| [200, { 'Content
run app
...
$...$ rackup stacked1.ru &
[1] 2123
...$ curl localhost:9292
"upcase"
"reverse"
127.0.0.1 - - [03/Nov/2010 16:15:34] "GET / HTTP/
DLROW OLLEH
...$
What happened?
A Decorator pattern happens

    In OOP, the decorator pattern is

“      a design pattern that allows
     new/additional behaviour to be
        added to an existing object
              dynamically
Basic API
use(middleware, **args, &block) adds
      a middleware to the stack

 run(app) dispatches to an application

  map(path, &block) constructs a
 Rack::URLMap in a convenient way
Basic API - usage
       example
r e q u i r e 'rack-validate'

use Rack::Validate

map '/hello' d o
  run lambda d o |env|
    [200,
      { 'Content-Type' => 'text/html'
      'Hello World']
  end
end
Rake::Validate from
http://coderack.org/
Basic optional usage -
   Rack::Builder
Provides an optional DSL
app = Rack::Builder.new d o
  map "/hello" d o
    run lambda d o |env|
      [200,
        { 'Content-Type' => 'text/html'
        'Hello World']
    end
  end
end
Rack convenience
 Wanna develop outside of existing
 frameworks, implement your own
   ones, or develop middleware?

Rack provides many helpers to create
Rack applications quickly and without
  doing the same web stuff all over
Rack::Request
req = Rack::Request.new(env)
req.post?
req.params["data"]
Rack::Response
res = Response.new
res.write "<title>Lobstericious!</title>"
res.write "<pre>"
res.write lobster
res.write "</pre>"
res.write "<p><a href='#{href}'>flip!</a>
res.write "<p><a href='?flip=crash'>crash
res.finish
use Rack::CommonLogger
Writes a log statement to STDOUT in the Apache
      common log format for each request
         use Rack::ShowExceptions
   Renders a nice looking errors page for all
            unhandled exceptions
                use Rack::Lint
 Ensures that your Rack application conforms to
   the Rack spec. Rack::Lint will generate an
exception if the response of your application does
              not meet the Rack spec
And many more
     Rack::Auth::Basic

   Rack::Session::Cookie

       Rack::Sendfile

     http://coderack.org/

http://rack.rubyforge.org/doc/
Is this testable?
   Use your favorite framework

It provides Rack::MockRequest and
        Rack::MockResponse
With bacon

      Bacon is a small RSpec clone

“    weighing less than 350 LoC but
    nevertheless providing all essential
                 features.
MockRequest
describe Rack::Static d o
  root = File.expand_path(File.dirname(_ _ F I L E _ _
                                        _
  OPTIONS = {:urls => ["/cgi"], :root => root

  @request =
    Rack::MockRequest.new(
      Rack::Static.new(DummyApp.new, OPTIONS
  it "serves files" d o
    res = @request.get("/cgi/test")
    res.should.be.ok
    res.body.should =~ /ruby/
  end
end
MockResponse
describe Rack::Chunked d o
  before d o
    @env = Rack::MockRequest.
      env_for('/', 'HTTP_VERSION' => '1.1', 'REQUEST_METH
  end

  should 'chunk responses with no Content-Length' d o
    app = lambda { |env| [200, {}, ['Hello',
    response = Rack::MockResponse.new(
      *Rack::Chunked.new(app).call(@env))
    response.headers.should.n o t .i n c l u d e 'Content-Le
                                n     i
    response.headers['Transfer-Encoding'].should
    response.body.should.equal
        "5rnHellorn1rn rn6rnWorld!rn0rnrn"
  end
end
Why do we care?
   Supported web servers
     Mongrel, EventedMongrel,
       SwiftipliedMongrel

WEBrick, FCGI, CGI, SCGI, LiteSpeed

               Thin
These web servers include
handlers in their distributions
       Ebb, Fuzed, Glassfish v3

 Phusion Passenger (which is mod_rack
       for Apache and for nginx)

     Rainbows!, Unicorn, Zbatery
Any valid Rack app will run
the same on all these handlers,
  without changing anything
Supported web
   frameworks
Camping, Coset, Halcyon, Mack

       Maveric, Merb,
 Racktools::SimpleApplication

  Ramaze, Rum, Sinatra, Sin

       Vintage, Waves

   Wee, … and many others.
Of course Ruby on Rails
Rails has adopted the Rack philosophy
       throughout the framework

    A Rails application is actually a
     collection of Rack and Rails
 middleware components that all work
 together to form the completed whole
Listing the rails middleware
            stack
$...(master) $ rake middleware
(in /home/chischaschos/Projects/salary-manager)
use ActionDispatch::Static
use Rack::Lock
use ActiveSupport::Cache::Strategy::LocalCache
use Rack::Runtime
use Rails::Rack::Logger
use ActionDispatch::ShowExceptions
use ActionDispatch::RemoteIp
use Rack::Sendfile
use ActionDispatch::Callbacks
use ActiveRecord::ConnectionAdapters::ConnectionManagement
use ActiveRecord::QueryCache
use ActionDispatch::Cookies
use ActionDispatch::Session::CookieStore
use ActionDispatch::Flash
use ActionDispatch::ParamsParser
use Rack::MethodOverride
use ActionDispatch::Head
use ActionDispatch::BestStandardsSupport
use Warden::Manager
use Sass::Plugin::Rack
run SalaryManager::Application.routes
Things to note
 The Rack application being run with
the run directive at the end of the list of
 middlewares is the Rails application's
                 routes
Rails controllers are
   rack compliant
  A controller declaration
c l a s s HomeController < ApplicationCont
   d e f index
       render :text => "I'm your home cont
   end
end
Rails console testing (had to
    shorten the output)
$ruby-1.8.7-p302 > ...$ rails console
Loading development environment (Rails 3.0.0)
ruby-1.8.7-p302 > app = RailsRackApp::Application
ruby-1.8.7-p302 > app.class
 => ActionDispatch::Routing::RouteSet
ruby-1.8.7-p302 > env = {'REQUEST_METHOD' => 'GET
 => {"PATH_INFO"=>"/home/index", "REQUEST_METHOD"
ruby-1.8.7-p302 > status, headers, body = app.cal
 => [200, {"ETag"=>""d47cb2eec6f22cb9ff6fbb21cd3
ruby-1.8.7-p302 > body.body
 => "I'm yout home controller's body"
Rack app from a controller
       declaration
$ruby-1.8.7-p302 > app = HomeController.action :i
 => #<Proc:0xb6e26664@/home/chischaschos/.rvm/gem
ruby-1.8.7-p302 > app.respond_to? 'call'
 => true
ruby-1.8.7-p302 > status, headers, body = app.cal
=> [200, {"ETag"=>""d47cb2eec6f22cb9ff6fbb21cd34
ruby-1.8.7-p302 > body.body
 => "I'm yout home controller's body"
There are two different ways to
 install Rack components into
     your Rails application
 1 - Either configure your Rack application as part
      of your application's middleware stack

  2 - Or you can route URI paths directly to the
 Rack application from you application's routes
1.1 - Installing a component into your
               application
          lib/average_time.rb
 c l a s s AverageRuntime
    @@requests = 0
    @@total_runtime = 0.0
   d e f initialize(app)
      @app = app
   end

   d e f call(env)
      code, headers, body = @app.call(env

     @@requests += 1
     @@total_runtime += headers['X-Runtime'
     headers['X-AverageRuntime'] =
       (@@total_runtime / @@requests).to_s

     [code, headers, body]
   end
1.2 - Inserting the middleware
          config/application.rb
r e q u i r e File.expand_path('../boot', _ _ F I L E _ _
r e q u i r e 'rails/all'
Bundler.r e q u i r e (:default, Rails.env)
              r
m o d u l e RailsRackApp
   c l a s s Application < Rails::Application

     # starts the important part
     config.autoload_paths += %W(#{config
     config.middleware.insert_before Rack
       "AverageRuntime"
     # end the important part
    config.encoding = "utf-8"
    config.filter_parameters += [:password
  end
end
1.3 - Verifying middleware is in the
                stack
$...$ rake middleware
(in /home/chischaschos/Projects/rack-testing/rails-rack-app)
use ActionDispatch::Static
use Rack::Lock
use AverageRuntime
use ActiveSupport::Cache::Strategy::LocalCache
use Rack::Runtime
use Rails::Rack::Logger
use ActionDispatch::ShowExceptions
use ActionDispatch::RemoteIp
use Rack::Sendfile
use ActionDispatch::Callbacks
use ActiveRecord::ConnectionAdapters::ConnectionManagement
use ActiveRecord::QueryCache
use ActionDispatch::Cookies
use ActionDispatch::Session::CookieStore
use ActionDispatch::Flash
use ActionDispatch::ParamsParser
use Rack::MethodOverride
use ActionDispatch::Head
use ActionDispatch::BestStandardsSupport
run RailsRackApp::Application.routes
1.4 Testing our middleware
Look at X-Averageruntime: header
 $...$ curl -I localhost:3000
 HTTP/1.1 404 Not Found
 Connection: Keep-Alive
 Content-Type: text/html
 Date: Fri, 05 Nov 2010 16:04:43 GMT
 Server: WEBrick/1.3.1 (Ruby/1.8.7/2010-08-16)
 X-Runtime: 0.312526
 Content-Length: 621
 X-Averageruntime: 0.312526
2.1 - Routing to a rack application
         lib/walking_arrow.rb
c l a s s WalkingArrow
  ARROW = '=>'
  @@spaces = 0
  d e f call(env)
     @@spaces += 1
     [200, {'Content-Type' => 'text/plain'},
  end
end
2.2 - Add a route
        lib/walking_arrow.rb
r e q u i r e 'lib/walking_arrow.rb'
RailsRackApp::Application.routes.draw
   get 'home/index'
   get 'walkingarrow' => WalkingArrow.new
end
2.3 Testing our middleware
              Walk!!!
$...$ curl localhost:3000/walkingarrow
 =>
...$ curl localhost:3000/walkingarrow
  =>
...$ curl localhost:3000/walkingarrow
    =>
...$ curl localhost:3000/walkingarrow
     =>
...$ curl localhost:3000/walkingarrow
       =>
...$ curl localhost:3000/walkingarrow
        =>
Before diving into rack
  and rails creation
 Learn and play with some examples

    http://guides.rubyonrails.org
           /railsonrack.html
Do you have ideas on
   how to use it?
Most sincere thanks to
     http://rack.rubyforge.org/doc/

  http://rails-nutshell.labs.oreilly.com
                /ch07.html

http://rubylearning.com/blog/2010/09/21
   /writing-modular-web-applications-
  with-rack/?utmsource=twitterfeed&
           utmmedium=twitter

              And Mendo
References
github repo and examples

  chischaschos twitter

Intro to Rack

  • 1.
  • 2.
    What is Rack? Rack provides a minimal interface “ between webservers supporting Ruby and Ruby frameworks. Transparently integrates in your existing rails, sinatra apps Helps you easily create a middleware standalone application stack
  • 3.
    Yeah! but... whatis Rack? A Rack application is a Ruby object 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.
  • 4.
    A basic Rackapp diagram
  • 5.
    Minimum server interface you said? app = lambda d o |env| [200, { 'Content-Type' => 'text/html' 'Hello World'] end run app
  • 6.
  • 7.
    Rack is afull middleware stack
  • 8.
  • 9.
    What is amiddleware? Rack application that is designed “ to run in conjunction with another Rack application, which acts as the endpoint
  • 10.
    ... Think ofa Rack middleware as a filter receiving the Rack environment for the request from the previous middleware Does some work with or on the request's environment Then calls the next middleware in the chain
  • 11.
    ... The last Rack application in the “ chain is the application itself Any middleware in the chain can return the Rack response itself, thus preventing the rest of the middlewares in the chain from executing
  • 12.
    Installation $... $ geminstall rack Successfully installed rack-1.2.1 1 gem installed Installing ri documentation for rack-1.2.1... Building YARD (yri) index for rack-1.2.1... Installing RDoc documentation for rack-1.2.1...
  • 13.
    Let's talk moreRack status, headers, body = object [200, { 'Content-Type' => 'text/html' } 'Hello World']
  • 14.
    Stacking apps m od u l e MyModule c l a s s Upcase d e f initialize app @app = app end d e f call env p 'upcase' status, headers, body = [status, headers, [body. end end end
  • 15.
    ... m o du l e MyModule c l a s s Reverse d e f initialize app @app = app end d e f call env p 'reverse' status, headers, body = [status, headers, [body. end end end
  • 16.
    ... use MyModule::Upcase use MyModule::Reverse useRack::ContentLength app = lambda { |env| [200, { 'Content run app
  • 17.
    ... $...$ rackup stacked1.ru& [1] 2123 ...$ curl localhost:9292 "upcase" "reverse" 127.0.0.1 - - [03/Nov/2010 16:15:34] "GET / HTTP/ DLROW OLLEH ...$
  • 18.
    What happened? A Decoratorpattern happens In OOP, the decorator pattern is “ a design pattern that allows new/additional behaviour to be added to an existing object dynamically
  • 20.
    Basic API use(middleware, **args,&block) adds a middleware to the stack run(app) dispatches to an application map(path, &block) constructs a Rack::URLMap in a convenient way
  • 21.
    Basic API -usage example r e q u i r e 'rack-validate' use Rack::Validate map '/hello' d o run lambda d o |env| [200, { 'Content-Type' => 'text/html' 'Hello World'] end end
  • 22.
  • 23.
    Basic optional usage- Rack::Builder Provides an optional DSL app = Rack::Builder.new d o map "/hello" d o run lambda d o |env| [200, { 'Content-Type' => 'text/html' 'Hello World'] end end end
  • 24.
    Rack convenience Wannadevelop outside of existing frameworks, implement your own ones, or develop middleware? Rack provides many helpers to create Rack applications quickly and without doing the same web stuff all over
  • 25.
  • 26.
    Rack::Response res = Response.new res.write"<title>Lobstericious!</title>" res.write "<pre>" res.write lobster res.write "</pre>" res.write "<p><a href='#{href}'>flip!</a> res.write "<p><a href='?flip=crash'>crash res.finish
  • 27.
    use Rack::CommonLogger Writes alog statement to STDOUT in the Apache common log format for each request use Rack::ShowExceptions Renders a nice looking errors page for all unhandled exceptions use Rack::Lint Ensures that your Rack application conforms to the Rack spec. Rack::Lint will generate an exception if the response of your application does not meet the Rack spec
  • 28.
    And many more Rack::Auth::Basic Rack::Session::Cookie Rack::Sendfile http://coderack.org/ http://rack.rubyforge.org/doc/
  • 29.
    Is this testable? Use your favorite framework It provides Rack::MockRequest and Rack::MockResponse
  • 30.
    With bacon Bacon is a small RSpec clone “ weighing less than 350 LoC but nevertheless providing all essential features.
  • 31.
    MockRequest describe Rack::Static do root = File.expand_path(File.dirname(_ _ F I L E _ _ _ OPTIONS = {:urls => ["/cgi"], :root => root @request = Rack::MockRequest.new( Rack::Static.new(DummyApp.new, OPTIONS it "serves files" d o res = @request.get("/cgi/test") res.should.be.ok res.body.should =~ /ruby/ end end
  • 32.
    MockResponse describe Rack::Chunked do before d o @env = Rack::MockRequest. env_for('/', 'HTTP_VERSION' => '1.1', 'REQUEST_METH end should 'chunk responses with no Content-Length' d o app = lambda { |env| [200, {}, ['Hello', response = Rack::MockResponse.new( *Rack::Chunked.new(app).call(@env)) response.headers.should.n o t .i n c l u d e 'Content-Le n i response.headers['Transfer-Encoding'].should response.body.should.equal "5rnHellorn1rn rn6rnWorld!rn0rnrn" end end
  • 33.
    Why do wecare? Supported web servers Mongrel, EventedMongrel, SwiftipliedMongrel WEBrick, FCGI, CGI, SCGI, LiteSpeed Thin
  • 34.
    These web serversinclude handlers in their distributions Ebb, Fuzed, Glassfish v3 Phusion Passenger (which is mod_rack for Apache and for nginx) Rainbows!, Unicorn, Zbatery
  • 35.
    Any valid Rackapp will run the same on all these handlers, without changing anything
  • 36.
    Supported web frameworks Camping, Coset, Halcyon, Mack Maveric, Merb, Racktools::SimpleApplication Ramaze, Rum, Sinatra, Sin Vintage, Waves Wee, … and many others.
  • 37.
    Of course Rubyon Rails Rails has adopted the Rack philosophy throughout the framework A Rails application is actually a collection of Rack and Rails middleware components that all work together to form the completed whole
  • 38.
    Listing the railsmiddleware stack $...(master) $ rake middleware (in /home/chischaschos/Projects/salary-manager) use ActionDispatch::Static use Rack::Lock use ActiveSupport::Cache::Strategy::LocalCache use Rack::Runtime use Rails::Rack::Logger use ActionDispatch::ShowExceptions use ActionDispatch::RemoteIp use Rack::Sendfile use ActionDispatch::Callbacks use ActiveRecord::ConnectionAdapters::ConnectionManagement use ActiveRecord::QueryCache use ActionDispatch::Cookies use ActionDispatch::Session::CookieStore use ActionDispatch::Flash use ActionDispatch::ParamsParser use Rack::MethodOverride use ActionDispatch::Head use ActionDispatch::BestStandardsSupport use Warden::Manager use Sass::Plugin::Rack run SalaryManager::Application.routes
  • 39.
    Things to note The Rack application being run with the run directive at the end of the list of middlewares is the Rails application's routes
  • 40.
    Rails controllers are rack compliant A controller declaration c l a s s HomeController < ApplicationCont d e f index render :text => "I'm your home cont end end
  • 41.
    Rails console testing(had to shorten the output) $ruby-1.8.7-p302 > ...$ rails console Loading development environment (Rails 3.0.0) ruby-1.8.7-p302 > app = RailsRackApp::Application ruby-1.8.7-p302 > app.class => ActionDispatch::Routing::RouteSet ruby-1.8.7-p302 > env = {'REQUEST_METHOD' => 'GET => {"PATH_INFO"=>"/home/index", "REQUEST_METHOD" ruby-1.8.7-p302 > status, headers, body = app.cal => [200, {"ETag"=>""d47cb2eec6f22cb9ff6fbb21cd3 ruby-1.8.7-p302 > body.body => "I'm yout home controller's body"
  • 42.
    Rack app froma controller declaration $ruby-1.8.7-p302 > app = HomeController.action :i => #<Proc:0xb6e26664@/home/chischaschos/.rvm/gem ruby-1.8.7-p302 > app.respond_to? 'call' => true ruby-1.8.7-p302 > status, headers, body = app.cal => [200, {"ETag"=>""d47cb2eec6f22cb9ff6fbb21cd34 ruby-1.8.7-p302 > body.body => "I'm yout home controller's body"
  • 43.
    There are twodifferent ways to install Rack components into your Rails application 1 - Either configure your Rack application as part of your application's middleware stack 2 - Or you can route URI paths directly to the Rack application from you application's routes
  • 44.
    1.1 - Installinga component into your application lib/average_time.rb c l a s s AverageRuntime @@requests = 0 @@total_runtime = 0.0 d e f initialize(app) @app = app end d e f call(env) code, headers, body = @app.call(env @@requests += 1 @@total_runtime += headers['X-Runtime' headers['X-AverageRuntime'] = (@@total_runtime / @@requests).to_s [code, headers, body] end
  • 45.
    1.2 - Insertingthe middleware config/application.rb r e q u i r e File.expand_path('../boot', _ _ F I L E _ _ r e q u i r e 'rails/all' Bundler.r e q u i r e (:default, Rails.env) r m o d u l e RailsRackApp c l a s s Application < Rails::Application # starts the important part config.autoload_paths += %W(#{config config.middleware.insert_before Rack "AverageRuntime" # end the important part config.encoding = "utf-8" config.filter_parameters += [:password end end
  • 46.
    1.3 - Verifyingmiddleware is in the stack $...$ rake middleware (in /home/chischaschos/Projects/rack-testing/rails-rack-app) use ActionDispatch::Static use Rack::Lock use AverageRuntime use ActiveSupport::Cache::Strategy::LocalCache use Rack::Runtime use Rails::Rack::Logger use ActionDispatch::ShowExceptions use ActionDispatch::RemoteIp use Rack::Sendfile use ActionDispatch::Callbacks use ActiveRecord::ConnectionAdapters::ConnectionManagement use ActiveRecord::QueryCache use ActionDispatch::Cookies use ActionDispatch::Session::CookieStore use ActionDispatch::Flash use ActionDispatch::ParamsParser use Rack::MethodOverride use ActionDispatch::Head use ActionDispatch::BestStandardsSupport run RailsRackApp::Application.routes
  • 47.
    1.4 Testing ourmiddleware Look at X-Averageruntime: header $...$ curl -I localhost:3000 HTTP/1.1 404 Not Found Connection: Keep-Alive Content-Type: text/html Date: Fri, 05 Nov 2010 16:04:43 GMT Server: WEBrick/1.3.1 (Ruby/1.8.7/2010-08-16) X-Runtime: 0.312526 Content-Length: 621 X-Averageruntime: 0.312526
  • 48.
    2.1 - Routingto a rack application lib/walking_arrow.rb c l a s s WalkingArrow ARROW = '=>' @@spaces = 0 d e f call(env) @@spaces += 1 [200, {'Content-Type' => 'text/plain'}, end end
  • 49.
    2.2 - Adda route lib/walking_arrow.rb r e q u i r e 'lib/walking_arrow.rb' RailsRackApp::Application.routes.draw get 'home/index' get 'walkingarrow' => WalkingArrow.new end
  • 50.
    2.3 Testing ourmiddleware Walk!!! $...$ curl localhost:3000/walkingarrow => ...$ curl localhost:3000/walkingarrow => ...$ curl localhost:3000/walkingarrow => ...$ curl localhost:3000/walkingarrow => ...$ curl localhost:3000/walkingarrow => ...$ curl localhost:3000/walkingarrow =>
  • 51.
    Before diving intorack and rails creation Learn and play with some examples http://guides.rubyonrails.org /railsonrack.html
  • 52.
    Do you haveideas on how to use it?
  • 53.
    Most sincere thanksto http://rack.rubyforge.org/doc/ http://rails-nutshell.labs.oreilly.com /ch07.html http://rubylearning.com/blog/2010/09/21 /writing-modular-web-applications- with-rack/?utmsource=twitterfeed& utmmedium=twitter And Mendo
  • 54.
    References github repo andexamples chischaschos twitter