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:
Hello World! — 2009-10-05 14:05:58 -0300
Outros Exemplos de Middlewares
Na primeira parte dessa série de artigos, citei o projeto rack-contrib, que consistia em uma coleção de Middlewares criados pela comunidade e disponibilizado em um único projeto. Além de serem de extrema utilidade no desenvolvimento, eles também servem como um ótimo objeto de estudo para quem quer aprender sobre o funcionamento de um Middleware. Se olharmos o código do exemplo garbagecollection.rb, veremos que o funcionamento é extremamente simples, consistindo apenas de uma chamada forçada do Garbage Collector a cada request na aplicação.
module Rack
# Forces garbage collection after each request.
class GarbageCollector
def initialize(app)
@app = app
end
def call(env)
@app.call(env)
ensure
GC.start
end
end
end
Sem entrar no mérito da utilidade do exemplo, é interessante notar a simplicidade do código. Em outro exemplo, podemos ver integração fácil do código do Google Analytics em sua aplicação a com 2 linhas:
require "rack/google_analytics" use Rack::GoogleAnalytics, :web_property_id => "UA-000000-1"
E o código, propriamente dito, é algo simples:
module Rack #:nodoc:
class GoogleAnalytics < Struct.new :app, :options
def call env
status, headers, response = app.call(env)
if headers["Content-Type"] =~ /text\/html|application\/xhtml\+xml/
body = ""
response.each { |part| body << part }
index = body.rindex("
Muito boa serie de artigos. Eh o “Tudo que eu queria saber sobre Rack mas tinha medo de perguntar”.