Rinaldi Fonseca

Rack para leigos – Ruby Webserver Interface

7 de fevereiro de 2011

O Rack se tornou praticamente um padrão obrigatório para se criar frameworks web em Ruby. Nest post irei apresentar a motivação e um exemplo prático de sua utilização.

Contexto:

Um servidor web tem uma tarefa relativamente fácil de se explicar. Ele obtém uma requisição e devolve uma resposta.
Quando uma requisição chega para o servidor web, esta estará endereçada para “alguem”. Um exemplo seria acessarmos um arquivo www.site.com.br/home.php. Ou seja, o servidor web irá direcionar requisição para o arquivo home.php.

O arquivo home.php será processado(provavelmente por um mod_php do apache por exemplo) e irá devolver uma resposta.
Esta resposta será enviada para o cliente que fez a request.
É importante observar que ao invés de termos um simples arquivo para processar a requisição. poderíamos ter um framework web.

Um outro exemplo seria a requisição ser direcionada para um arquivo estático. Neste caso não há processamento de nenhum script. O servidor web somente devolve o conteúdo do arquivo sem realizar nenhuma mudança.

O Rack surgiu da necessidade de mapear de uma forma simples, a interação entre um framework web(escrito em Ruby) e um servidor web.
Ou seja, o Rack prove uma API minimalista para os frameworks web.

Em termos práticos, podemos considerá-lo como uma camada intermediária entre um Mongrel ou Thin(web servers), e o Rails ou Sinatra(frameworks).

Motivação:

Todo framework web, independente da linguagem, normalmente necessita realizar uma série de tarefas básicas, como por exemplo: realizar parser de HTTP, mapear URL, manipular sessões etc. Com isso, havia muita duplicação de código em nos frameworks. Todos eles estavam resolvendo sempre os mesmos problemas.
Desta forma o Rack unifica todas estas(e muitas outras) funcionalidades.

Prática:

O fluxo canônico de uma resquest HTTP pode ser descrito como algo que recebe um parâmetro(environment), e retorna uma resposta contendo praticamente 3 partes: um status, um set de cabeçalhos e o body da resposta. Ou seja uma resposta http mapeada em três partes.
O environment que seria o parâmetro de entrada, é praticamente um hash de variáveis de ambiente.
Ou seja o environment tem que ser tratado pelo framework web quando o cliente realiza uma request.

Para escrevermos uma aplicação Rack, ou seja, uma aplicação que implemente a especificação do Rack para “conversar” com o servidor web, devemos:

Criar um código que:

1) Responda ao método call
2) Receba um hash como argumento do método call
3) Retorne um hash contendo o código de status, um cabeçalho e uma resposta.

A única condição para a resposta é que ela responda ao método each. Uma simples string atende a esta condição

Podemos escrever um código Ruby que represente exatamente isso:

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

No exemplo acima nosso bloco de código está dentro de um método, mas poderia ser literalmente um bloco(procs, lambdas).

Quando executarmos nosso código com o Rack, ele irá chamar o método call passando o environment como parâmetro. Como foi citado acima, recebemos todas as informações da request de forma tratada(pois a Gem Rack realiza este tratamento para nós). Desta forma, de dentro do método, poderíamos por exemplo recuperar informações do user agent no Hash env com:

env['HTTP_USER_AGENT']

Isso retornaria algo como:

Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.70 Safari/533.4

No caso de usar um simples bloco para representar nossa aplicação, seria algo como:

Proc.new {|env| [200, {"Content-Type" => "text/html"}, "Hello World!"]}

Como um proc responde ao método call, o Rack realizará a chamada passando o env como parâmetro para o bloco.

Para criar e ver em funcionamento uma simples aplicação Rack, siga os passos:

1) Caso ainda não tenha, instale a gem com: gem install rack
2) Crie um arquivo chamado config.ru com o seguinte conteúdo abaixo:

require 'rubygems'
require 'rack'
 
class RackApplication
def call(env)
[200, {"Content-Type" => "text/html"}, "Hello Rack!"]
end
end
 
run RackApplication.new

2) Pelo console, rode e seguinte comando:
rackup config.ru

3) Acesse o endereço: http://localhost:9292/ em seu navegador e deverá ver a string “Hello Rack!” na página.

O que aconteceu por debaixo dos panos?

Ao instalar a Gem Rack, ganhamos o comando rackup que recebendo como parâmetro nosso arquivo de configuração config.ru, irá subir o servidor WEBrick e servir nossa aplicação na porta 9292. No caso, nossa aplicação será uma instância da classe RackApplication.

O método run na última linha irá utilizar o que chamamos de Handlers para poder conectar o web server ao Rack, e com isso poder servir a aplicação.
Seria o mesmo se fizéssemos: Rack::Handler::WEBrick.run. No caso, o Webrick já é o padrão.

O Rack possui diversos Handlers como:Rack::Handler::Mongrel, Rack::Handler::Thin etc. Poderíamos usar qualquer um deles, desde que tenhamos cada web server instalado previamente em nosso ambiente.

Também é possível realizar uma série se configurações, como por exemplo, mudar a porta passando passando o parâmetro :post => 3000 para o método run. Recomendo acessar a documentação em http://rack.rubyforge.org/doc/ para maiores detalhes de funcionamento e configuração.

Em um próximo post, irei mostrar alguns exemplos de Rack middlewares e como utilizá-los com o Rails.

3 Comentários »

  1. Bom artigo!!! Nunca entendi por qual motivo nasceu RACK.

    Comentário por Alther — 22 de fevereiro de 2011 @ 20:58

  2. Parabéns pelo artigo. Objetivo e esclarecedor.

    Comentário por Grangeiro — 2 de junho de 2011 @ 21:20

  3. Bem bacana.

    É algo que todo Railer deve saber.

    Comentário por Marcelo Cajueiro — 17 de agosto de 2011 @ 0:19

Feed RSS dos comentários deste post URL de TrackBack

Deixe um comentário

Spam protection by WP Captcha-Free