Rack

RS on Rails 2010

O RS on Rails desse ano foi um sucesso absoluto. Auditório com ótima ocupação, palestras inspiradas e lightning talks interessantes marcaram esta segunda edição do evento.

Deixo aqui meus agradecimentos aos organizadores e ao público presente. Abaixo, segue minha apresentação sobre aplicações Rack.

Superguia Rack – Parte 3 – Middlewares

Se você quer entender rapidamente o que é um Middleware, pense em um filtro. Algo que permite que todas as requisições e respostas passem por ele, permitindo assim, que ele altere-as como bem entender. Middlewares devem possuir o mesmo método call de qualquer outro objeto Rack, porém em seu construtor, eles devem receber outra aplicação como argumento. Esta é uma maneira simples de empilhar diversas aplicações. Se você vem de outras linguagens como Java ou C# e já leu sobre Design Patterns, deve estar reconhecendo o padrão Decorator. A idéia é semelhante, mas com Ruby as coisas se tornam muito mais simples.

Nosso primeiro middleware

Se pegarmos o exemplo simples do HelloWorld apresentado no início desta sequência de posts sobre Rack, podemos acomplar um middleware a ele, adicionando funcionalidades extras à aplicação original.

Primeiros, vamos criar nosso Middleware. O exemplo é muito simples. Após imprimirmos um “Hello World!”, vamos informar a hora.

class Middleware
  def initialize(app)
    @app = app
  end

  def call(env)
    status, headers, response = @app.call(env)
    response_body = ""
    response.each { |part| response_body += part }
    response_body += " --- #{Time.now}"
    headers["Content-Length"] = response_body.length.to_s
    [status, headers, [response_body]]
  end
end

Não se assuste se você não entender tudo de cara. A parte mais complicada é cocatenar a informação de tempo na resposta, e isso é feito usando um each, devido ao fato de o response poder ser qualquer objeto que responda a esse método. Além disso, para que nossa resposta seja válida, é interessante colocar no cabeçalho HTTP o comprimento do nosso body.

Mas além do middleware, devemos dizer que nossa aplicação original quer usar ele. E isso deve ser feito em um arquivo .ru:

#config.ru
require 'rack'
require 'middleware'
require 'hello_world'      

use Middleware
run HelloWorld.new

Agora rodando “rackup config.ru” na linha de comando, teremos o seguinte no nosso servidor:

Read more…

Superguia Rack – Parte 2 – Dissecando o Rack

Continuando nossa saga iniciada no post anterior, voltaremos ao exemplo do HelloWorld para entendermos mais profundamente a estrutura de um objeto Rack. Como mencionado anteriormente, um este objeto deve possuir (na verdade responder a) um método call(env), retornando em sua resposta um array com 3 elementos. Veremos mais profundamente cada um desses elementos.

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

O método def call(env)

Env

O parâmetro recebido pelo método call é uma hash com as propriedades do ambiente do visitante da aplicação. Se alterarmos o nosso HelloWorld para imprimir o objeto env ao invés da string, teremos algo parecido com isso:

{
"REQUEST_METHOD"=>"GET",
"REQUEST_PATH"=>"/",
"REQUEST_URI"=>"/",
"HTTP_VERSION"=>"HTTP/1.1",
"HTTP_HOST"=>"localhost:9292",
"HTTP_USER_AGENT"=>"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_7; en-us) AppleWebKit/530.18 (KHTML, like Gecko) Version/4.0.1 Safari/530.18",
"HTTP_ACCEPT"=>"application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5", "HTTP_ACCEPT_LANGUAGE"=>"en-us", "HTTP_ACCEPT_ENCODING"=>"gzip,deflate",
"HTTP_CONNECTION"=>"keep-alive", "GATEWAY_INTERFACE"=>"CGI/1.2",
"SERVER_NAME"=>"localhost",
"SERVER_PORT"=>"9292",
"SERVER_PROTOCOL"=>"HTTP/1.1",
"SERVER_SOFTWARE"=>"Mongrel 1.1.5",
 "PATH_INFO"=>"/",
 "SCRIPT_NAME"=>"",
 "REMOTE_ADDR"=>"127.0.0.1", "rack.version"=>[0, 1], "rack.input"=>#, "rack.errors"=>#>, "rack.multithread"=>true, "rack.multiprocess"=>false, "rack.run_once"=>false, "rack.url_scheme"=>"http",
 "QUERY_STRING"=>""}
 

Dentre as informações impressas, algumas são bem importantes, como:

  • REQUEST_METHOD: Informa qual o verbo HTTP (GET, PUT, DELETE…) usado pelo usuário ao acessar a página.
  • HTTP_USER_AGENT: Informa o browser do usuário, facilitando o tratamento para browsers diferente em sua aplicação.
  • QUERY_STRING: Informa os argumentos passados após o “?” da url, útil para fazer parsing dos parâmetros.

Status

O primeiro objeto retornado no array de resposta é um inteiro, representando o status da resposta que sua aplicação retornará no método. O 200 do nosso exemplo, representa “OK” segundo a especificação do HTTP. É aqui seu controle de exceção jogará aquele lindo “Erro 404″ quando o usuário digitar incorretamente um sub-caminho de sua aplicação. ;-)

HTTP Headers

Cabeçalhos HTTP informam o tipo de retorno do pacote. O segundo objeto do nosso retorno deve OBRIGATORIAMENTE responder a um método “each” e devolver chaves e valores de seus elementos, tipicamente um Hash, como no nosso exemplo. A chave Content-Type, por exemplo, deve retornar o tipo de retorno a ser esperado, podendo ser HTML(“Content-Type” => “text/html”), XML, MP3 (‘Content-Type’ => ‘audio/mp3′), entre outros.

Response Body

O retorno “Body” deve responder ao método each retornando um array de strings. Este será o conteúdo que será apresentado ao usuário no navegador.

ATENÇÃO: Segundo a documentação atual do Rack, o Body não deve mais ser uma simples string(como nos exemplos do post anterior). Aparentemente isto quebrará no Ruby 1.9.

Rack:Builder

É uma ferramenta que nos permite construir aplicações Rack utilizando uma DSL para “costurar” diferentes middlewares e aplicações. Os Builders são os parafusos das estantes, ou a cola que une as estruturas separadas criando uma inteiramente nova. Na verdade, o Builder é o cara que torna o a idéia do Rack tão genial. A facilidade que ele proporciona na criação de Middlewares ficará bem claro no próximo post.

Os métodos fornecidos pela DSL do Buider são:

Read more…

Superguia Rack – Parte 1

Este é o primeiro de uma série de posts dedicado ao Rack. No início, Rack me parecia muito obscuro e estes posts serão criados com o intuito de clarear as idéias de quem ainda possui dificuldade para entender este pequeno framework.

Introdução da Introdução

Em fevereiro de 2007, Christian Neukirchen postou em seu blog um artigo chamado “Introduzindo Rack” no qual falava sobre a quantidade de código duplicado que ele achava em diferentes Frameworks Web escritos em Ruby. Ao anunciar a sua criação, o autor tocava em um ponto importante aonde escrevia, com razão, sobre um assunto que na verdade não era culpa dos desenvolvedores.

Sendo todos frameworks web, era de se esperar que muitas coisas fossem parecidas. A grande sacada do criador de Rack, foi o fato de ter percebido que web pode, em grande parte, ser resumida como uma série de requisições e respostas HTTP. O resto é framework que vai por cima.

HTTP pode ser resumido de maneira muito simples, como uma série de REQUESTS e RESPONSES. Rack utiliza-se muito bem dessa “simplicidade” para encapsular praticamente todo o controle dessa troca de mensagens em um objeto Ruby. Ao encapsular as requisições HTTP, Rack é capaz de prover interfaces para aplicações entre web servers, web frameworks e middlewares.

O que é Rack?

Podemos considerar o Rack uma especificação para a criação de aplicações web desenvolvidas com ruby. Segundo Dan Webb em sua apresentação “8 minutes on Rack“:

“Se você tem um objeto Ruby que possui um método call cujo o único argumento é env e retorna um array com 3 elementos ([200, {"Content-Type" => "text/html"}, "Hello World!"] ), então ele pode ser conectado a qualquer web server que suporte Rack”

Mas a definição mais simples possível é a que Rack provê uma interface mínima entre web servers e web frameworks escritos em Ruby.
Read more…