Ruby on Rack #1 - Hello Rack!
Published almost 6 years ago

Ruby community is coming up with new frameworks almost every week, but in midst of that, Rack isn’t getting enough attention. Attention that it deserves. And also, the next stable release of Rails after 2.2 will have a better public facing interface for taking full advantage of Rack.

Rack was initially inspired from pythons’s wsgi and it quickly became the de-facto web application/server interface in the ruby community, thanks to it’s simplicity and preciseness. You might want to read Introducing Rack from the creator of rack – Christian Neukirchen before reading this post.

What is Rack ?

Rack provides a minimal, modular and adaptable interface for developing web applications in Ruby. By wrapping HTTP requests and responses in the simplest way possible, it unifies and distills the API for web servers, web frameworks, and software in between (the so-called middleware) into a single method call.

- Rack API Docs

Practically speaking, you can divide “Rack” in two parts :

Rack Specification

Rack specification specifies how exactly a Rack application and the web server should communicate :

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.

- Rack Specification

That’s the specification in a nutshell. You can check out the full details here.

Strictly speaking, you don’t need the rack gem in order to write Rack ready applications. Just stick to the specification and that’s it.

Rack Gem

Rack gem is a collection of utilities and facilitating classes, to make life easier for anyone developing Rack applications. It includes basic implementations of request, response, cookies & sessions. And a good number of usefult middlewares. In short, install the rack gem. You’re gonna need it :

$ sudo gem install rack

To summarize

  • Rack is a framework to roll your own ruby framework.
  • Rack provides an interface between different web servers and your framework/application. Making it very simple for your framework/application to be compatible with any webserver that supports Rack – Phusion Passenger, Litespeed, Mongrel, Thin, Ebb, Webrick to name a few.
  • Rack cuts your chase. You get request, response, cookies, params & sessions for free.
  • Makes it possible to use multiple frameworks for the same application, provided there is no class collision. Rails and sinatra integration is a good example of this.
  • Middlewares ! Think of middlewares as Rails’s before_filter/after_filter that are reusable across different rack supported frameworks/applications. For example, you can use the same Anti-spamming rack middleware for your Rails app, Sinatra app and your custom Rack application too!

Examples

Let’s start with a smallest possible example of a rack application, using mongrel.

require 'rubygems'
require 'rack'

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

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

The above code passes an object of HelloWorld to the mongrel rack handler, and starts the server on port 9292.

The HelloWorld object here respects the rack specifications. That is :

  1. Responds to call(), which takes one argument – environment
  2. call() returns an Array of [http_status_code, response_headers_hash, body]

That’s all ! If you run this script and browse to http://localhost:9292, you’ll see the shiny “Hello Rack!” message.

But hey, even a ruby proc responds to call(). So why not use a proc instead ? Well, no reason not to :

require 'rubygems'
require 'rack'

Rack::Handler::Mongrel.run proc {|env| [200, {"Content-Type" => "text/html"}, "Hello Rack!"]}, :Port => 9292

Another common seen pattern is to use method(:something), which returns an object of Method class :

require 'rubygems'
require 'rack'

def application(env)
  [200, {"Content-Type" => "text/html"}, "Hello Rack!"]
end

Rack::Handler::Mongrel.run method(:application), :Port => 9292

Take that you “Hello World” performance retards. You’re not gonna be able to write a faster ‘Hello World’ ruby application than this.

Rack it up’

As I said earlier, rack gem comes with a bunch of useful stuff to make life easier of a rack application developer. rackup is one of them. In the previous examples, I had used the mongrel handler Rack::Handler::Mongrel directly, and even hard coded the port number. With rackup, these things become configurable ! But to use rackup, you’ll need to supply it with a rackup config file. For our above example, the config file will look somewhat like :

# config.ru
run Proc.new {|env| [200, {"Content-Type" => "text/html"}, "Hello Rack!"]}

Just a line. By convention, you should use .ru extension for a rackup config file. Supply it a run RackObject and you’re ready to go :

$ rackup config.ru

By default, rackup will start a server on port 9292. But you can override that with a -p option to rackup. For more help, RTFM:

$ rackup --help