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: Merb, Sinatra, Ramaze, Waves, 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.