Se você mora na Terra, deve saber que o Rails Edge adotou o Rack. Mas, o que é esse tal de Rack?

O que é o Rack?

O Rack é uma interface entre servidores web e aplicações Ruby, baseada no padrão WSGI do Python. Ele possui diferentes handlers para os mais diversos webservers, como: Passenger, Mongrel, Thin, Ebb, Webrick, entre outros.

Ele é usado pelos principais frameworks Ruby: MerbSinatraRamazeWaves, e outros. O Rails 2.3 virá com suporte à Rack. Ele também pode ser considerado um framework para construir frameworks. O Nyane é um bom exemplo disto, mas existem muitos outros.

Ele tem suporte à Middlewares que são como mini-funcionalidades que podem ser compartilhadas entre todos os frameworks que suportam Rack. Este artigo possui uma lista de diversos middlewares interessantes.

Através do Rack::Cascade é possível utilizar diferentes frameworks dentro de uma mesma aplicação.

Aplicações Rack

Uma aplicação Rack nada mais é do que um simples objeto Ruby (não precisa ser uma classe) que responde ao método call. Ele precisa de apenas um argumento, o environment e retorna um Array com três valores: O status (200, 404, 500), o header (’Content-Type’ => ‘text/html’, ’Location’ => ‘/’, ) e um objeto que responde ao método each (’uma string’).

Exemplos

O seguinte código é um exemplo de uma aplicação Rack:

class Hello
   def call(env)
      [ 200, {}, 'Hello World!' ]
   end
end

Mas, como eu disse, não precisa ser uma classe, um simples lambda também é uma aplicação Rack válida:

lambda { |env| [200, {}, 'Hello World!' }

Isto é válido, porquê o método lambda retorna uma Proc, que responde pelo método call.

Nos dois exemplos, o retorno do método call é um Array com o status 200, headers em branco em uma string 'Hello World!'. Exatamente o que o Rack precisa para funcionar. :)

E esse environment, o que é?

O env (objeto passado ao método call), é um hash com diversas variáveis de ambientes, por exemplo: PATH_INFO, QUERY_STRING, REQUEST_METHOD, SERVER_PORT, entre outros.

E pra que ele serve?

Você usa o env para construir um objeto Rack::Request, que pode ser usado para obter os parâmetros GET e POST:

request = Rack::Request.new(env)
# returns the data received in the query string
request.GET
# returns the data received in the request body
request.POST

Eu sei receber um request, mas e como eu envio uma resposta?

O Rack::Response é usado para criar uma resposta HTTP. Com ele você pode configurar headers, criar cookies, entre outros:

# uma resposta simples
response = Rack::Response.new
response.status = 200
response.body("Hello World!")
response.finish
 
# um redirect
response = Rack::Response.new
response.status = 302
response.headers["Location"] = "/"
response.finish
 
# criando um cookie
response = Rack::Response.new
response.set_cookie('cookie-name', 'content')
response.finish
 

Rackup

O Rack vem com um binário chamado rackup que pode ser usado para servir aplicações Rack rapidamente. Ele lê arquivos .ru (não pode ser .rb), também chamados de “rack config files”, e usa o handler do Mongrel para servir a aplicação. Ele já adiciona os middlewares para Logging (Rack::CommonLogger) e Exceptions (Rack::ShowExceptions).

Exemplos

Usando uma classe:

class Hello
   def call(env)
      [ 200, {}, 'Hello World!' ]
   end
end
 
run Hello.new

Um lambda:

run lambda {|env| [200, {}, "Hello World!"]}

Isso mesmo, não precisa de nenhum require. :) Para rodar, é simples:

rackup app.ru

Por padrão, ele rodará na porta 9292.

Ou, se você preferir, pode rodá-lo com o Thin:

thin start -R app.ru

Você também pode usar o Handler do Mongrel diretamente:

require "rubygems"
require "rack"
 
app = lambda { |env| [200, {}, 'Hello World!'] }
Rack::Handler::Mongrel.run(app, :Port => 3000)

Neste caso, o require é necessário.

Finalizando

Espero que este post tenha ajudado você a entender melhor o que é o Rack e o porquê dele ser importante no mundo Ruby. Nos próximos posts, irei explicar como construir e usar Middlewares, e falarei de uma das novas features do Rails que usa o Rack, o Metal, muito discutido nessa última semana.