Introduction to Rack
  Kerry Buckley, 26 October 2010
Introduction to Rack

Why was it needed?
How does it work?
Building a stack with middleware
Writing a simple rack application
Web frameworks




  and many more…
Web servers
Mongrel

                        CGI




              Webrick
Before Rack
With Rack


Rack-compliant interface
Introduction to Rack

Why was it needed?
How does it work?
Building a stack with middleware
Writing a simple rack application
Rack is just an
interface specification
A Rack app is…
  • An object (not a class)…
  • …which responds to call(env)…
  • …and returns an array containing:
     • response code
     • a hash of headers
     • the body*
*this is a slight simplification
The environment hash

• REQUEST_METHOD
• SERVER_NAME, SERVER_PORT
• SCRIPT_NAME, PATH_INFO, QUERY_STRING
• HTTP_ variables from request headers
• Some rack-specific variables
A simple Rack app

require 'rubygems'
require 'rack'

class HelloWorld
  def call(env)
    [200, {"Content-Type" => "text/html"}, "Hello World!"]
  end
end

Rack::Handler::Mongrel.run HelloWorld.new, :Port => 9292
Even simpler Rack app

require 'rubygems'
require 'rack'

Rack::Handler::Mongrel.run proc { |env|
  [200, {"Content-Type" => "text/html"}, "Hello World!"]
}, :Port => 9292
Revisiting the body
  • An object (not a class)…
  • …which responds to call(env)…
  • …and returns an array containing:
     • response code
     • a hash of headers
     • the body*
*this is a slight simplification
Revisiting the body

• Must respond to each, yielding strings
  • Strings work in 1.8, but not 1.9
• close will be called if present

• to_path can provide a file location
Body as array

require 'rubygems'
require 'rack'

class HelloWorld
  def call(env)
    [200, {"Content-Type" => "text/html"}, ["Hello ", "World!"]]
  end
end

Rack::Handler::Mongrel.run HelloWorld.new, :Port => 9292
Body as IO object

require 'rubygems'
require 'rack'

class HelloWorld
  def call(env)
    [200, {"Content-Type" => "text/html"},
      StringIO.new("Hello World!")]
  end
end

Rack::Handler::Mongrel.run HelloWorld.new, :Port => 9292
Body as self
require 'rubygems'
require 'rack'

class HelloWorld
  def call(env)
    [200, {"Content-Type" => "text/html"}, self]
  end

  def each
    yield "Hello "
    yield "World!"
  end
end

Rack::Handler::Mongrel.run HelloWorld.new, :Port => 9292
The rackup file

• Configuration DSL for a Rack app
• Server-independent
• Allows stacking of middleware
• Provides simple route mapping
The rackup file
Your config.ru file:
class HelloWorld
  def call(env)
    [200, {"Content-Type" => "text/html"}, "Hello World!"]
  end
end

run HelloWorld.new



Rack loads it like this:
config_file = File.read(config)
rack_application = eval("Rack::Builder.new { #{config_file} }")
server.run rack_application, options
The rack gem
Provides a bunch of helper classes
• Request/response wrappers
• Logging
• Authentication (basic and digest)
• Cookies and sessions
• Mock requests and responses
Introduction to Rack

Why was it needed?
How does it work?
Building a stack with middleware
Writing a simple rack application
Middleware
Request                   Response

           Middleware A
Request                   Response

           Middleware B
Request                   Response

            Application
Middleware
• A middleware is just a rack application
• Constructor takes next app down
• Can modify request or response
• Can call layer below, or just return
• Configured with ‘use’ in rackup file
Middleware in Rails

• Used internally for cookies, parameter
  parsing etc
• Add your own in environment.rb:
Rails::Initializer.run do |config|  
  config.middleware.use "MyMiddlewareClass"  
end 
Introduction to Rack

Why was it needed?
How does it work?
Building a stack with middleware
Writing a simple rack application
Demo!

Rack

  • 1.
    Introduction to Rack Kerry Buckley, 26 October 2010
  • 2.
    Introduction to Rack Whywas it needed? How does it work? Building a stack with middleware Writing a simple rack application
  • 3.
    Web frameworks and many more…
  • 4.
  • 5.
  • 6.
  • 7.
    Introduction to Rack Whywas it needed? How does it work? Building a stack with middleware Writing a simple rack application
  • 8.
    Rack is justan interface specification
  • 9.
    A Rack appis… • An object (not a class)… • …which responds to call(env)… • …and returns an array containing: • response code • a hash of headers • the body* *this is a slight simplification
  • 10.
    The environment hash •REQUEST_METHOD • SERVER_NAME, SERVER_PORT • SCRIPT_NAME, PATH_INFO, QUERY_STRING • HTTP_ variables from request headers • Some rack-specific variables
  • 11.
    A simple Rackapp require 'rubygems' require 'rack' class HelloWorld   def call(env)     [200, {"Content-Type" => "text/html"}, "Hello World!"]   end end Rack::Handler::Mongrel.run HelloWorld.new, :Port => 9292
  • 12.
    Even simpler Rackapp require 'rubygems' require 'rack' Rack::Handler::Mongrel.run proc { |env|   [200, {"Content-Type" => "text/html"}, "Hello World!"] }, :Port => 9292
  • 13.
    Revisiting the body • An object (not a class)… • …which responds to call(env)… • …and returns an array containing: • response code • a hash of headers • the body* *this is a slight simplification
  • 14.
    Revisiting the body •Must respond to each, yielding strings • Strings work in 1.8, but not 1.9 • close will be called if present • to_path can provide a file location
  • 15.
    Body as array require 'rubygems' require 'rack' class HelloWorld   def call(env)     [200,{"Content-Type" => "text/html"}, ["Hello ", "World!"]]   end end Rack::Handler::Mongrel.run HelloWorld.new, :Port => 9292
  • 16.
    Body as IOobject require 'rubygems' require 'rack' class HelloWorld   def call(env)     [200, {"Content-Type" => "text/html"},       StringIO.new("Hello World!")]   end end Rack::Handler::Mongrel.run HelloWorld.new, :Port => 9292
  • 17.
    Body as self require 'rubygems' require 'rack' class HelloWorld   def call(env)     [200,{"Content-Type" => "text/html"}, self]   end   def each     yield "Hello "     yield "World!"   end end Rack::Handler::Mongrel.run HelloWorld.new, :Port => 9292
  • 18.
    The rackup file •Configuration DSL for a Rack app • Server-independent • Allows stacking of middleware • Provides simple route mapping
  • 19.
    The rackup file Yourconfig.ru file: class HelloWorld   def call(env)     [200, {"Content-Type" => "text/html"}, "Hello World!"]   end end run HelloWorld.new Rack loads it like this: config_file = File.read(config) rack_application = eval("Rack::Builder.new { #{config_file} }") server.run rack_application, options
  • 20.
    The rack gem Providesa bunch of helper classes • Request/response wrappers • Logging • Authentication (basic and digest) • Cookies and sessions • Mock requests and responses
  • 21.
    Introduction to Rack Whywas it needed? How does it work? Building a stack with middleware Writing a simple rack application
  • 22.
    Middleware Request Response Middleware A Request Response Middleware B Request Response Application
  • 23.
    Middleware • A middlewareis just a rack application • Constructor takes next app down • Can modify request or response • Can call layer below, or just return • Configured with ‘use’ in rackup file
  • 24.
    Middleware in Rails •Used internally for cookies, parameter parsing etc • Add your own in environment.rb: Rails::Initializer.run do |config|     config.middleware.use "MyMiddlewareClass"   end 
  • 25.
    Introduction to Rack Whywas it needed? How does it work? Building a stack with middleware Writing a simple rack application
  • 26.