Mais em rubyonrails.pro.br: Geral | Download | Deploy | Código | Apresentações | Documentação | Ecossistema | Comunidade | Podcasts | Blogs

Rails on Rack

Esse guia cobre a integração do Rails com o Rack e interface com outros componentes Rack. Ao completar este guia, você será capaz de:

Esse guia pressupõe um conhecimento funcional do protocolo Rack e conceitos acerca do Rack, como middlewares, mapas de urls e Rack::Builder

1 Introdução ao Rack

O Rack fornece uma interface mínima, modular e adaptável para desenvolver aplicações web em Ruby. Por encapsular requisições e respostas HTTP da forma mais simples possível, ele unifica e destila uma API para servidores web, frameworks web, e softwares intermediários (os chamados middlewares) em uma única chamada de método.

- Documentação da API do Rack

Explicar Rack não é o verdadeiro escopo deste guia. Caso você não seja familiar com o básico do Rack, você deve verificar os seguintes links:

2 Rails on Rack

2.1 Objeto Rack em aplicações Rails

ActionController::Dispatcher.new é o principal objeto Rack em uma aplicação Rails. Qualquer servidor web que siga o protocolo Rack deveria estar usando o objeto ActionController::Dispatcher.new para servir uma aplicação Rails.

2.2 script/server

script/server executa o trabalho básico de criar um objeto Rack::Builder e iniciar o servidor web. Esse é o equivalente Rails ao script Rack rackup.

A seguir é mostrado como script/server cria uma instância do Rack::Builder

app = Rack::Builder.new { use Rails::Rack::LogTailer unless options[:detach] use Rails::Rack::Debugger if options[:debugger] map "/" do use Rails::Rack::Static run ActionController::Dispatcher.new end }.to_app

Os middlewares usados no código acima são úteis primariamente apenas em ambiente de desenvolvimento. A tabela a seguir explica suas finalidades:

Middleware Propósito
Rails::Rack::LogTailer Anexar a saída do arquivo de log ao console
Rails::Rack::Static Servir arquivos estáticos do diretório RAILS_ROOT/public
Rails::Rack::Debugger Iniciar debugger

2.3 rackup

Para usar o rackup em vez do script/server do Rails, você pode colocar o seguinte conteúdo no arquivo config.ru, na raiz da sua aplicação Rails:

# RAILS_ROOT/config.ru require "config/environment" use Rails::Rack::LogTailer use Rails::Rack::Static run ActionController::Dispatcher.new

E inicie o servidor:

[lifo@null application]$ rackup

Para saber mais sobre diferentes opções do rackup:

[lifo@null application]$ rackup --help

3 Pilha de middlewares do Action Controller

Muitos dos componentes internos do Action Controller são implementados como middlewares do Rack. ActionController::Dispatcher utiliza o ActionController::MiddlewareStack para combinar vários middlewares internos e externos para formar uma aplicação Rack Rails completa.

ActionController::MiddlewareStack é o equivalente no Rails ao Rack::Builder, mas construído para melhor flexibilidade e com mais características para adequar-se aos requisitos do Rails.

3.1 Inspecionando a pilha de Middlewares

O Rails possui uma tarefa rake muito conveniente para inspecionar a pilha de middlewares em uso:

$ rake middleware

Para uma aplicação Rails recém gerada, isso deve produzir algo como:

use Rack::Lock use ActionController::Failsafe use ActionController::Session::CookieStore, , {:secret=>"<secret>", :session_key=>"_<app>_session"} use Rails::Rack::Metal use ActionController::RewindableInput use ActionController::ParamsParser use Rack::MethodOverride use Rack::Head use ActiveRecord::QueryCache run ActionController::Dispatcher.new

O propósito de cada um desses middlewares é explicado na seção Middlewares internos.

3.2 Configurando a pilha de Middlewares

O Rails fornece uma interface simples de configuração config.middleware para adicionar, remover e modificar os middlewares na pilha de middlewares por meio do environment.rb ou do arquivo de configuração específico do ambiente em environments/<ambiente>.rb.

3.2.1 Adicionando um Middleware

Você pode adicionar um novo middleware à pilha de middlewares usando qualquer um dos métodos a seguir:

  • config.middleware.use(new_middleware, args) – Adiciona o novo middleware no fim da pilha de middlewares.
  • config.middleware.insert_before(existing_middleware, new_middleware, args) – Adiciona o novo middleware antes do middleware especificado na pilha de middlewares.
  • config.middleware.insert_after(existing_middleware, new_middleware, args) – Adiciona o novo middleware depois do middleware existente especificado na pilha de middlewares.

Exemplo:

# environment.rb # Coloca Rack::BounceFavicon no fim config.middleware.use Rack::BounceFavicon # Adiciona Lifo::Cache após ActiveRecord::QueryCache. # Passa { :page_cache => false } como argumento ao Lifo::Cache. config.middleware.insert_after ActiveRecord::QueryCache, Lifo::Cache, :page_cache => false
3.2.2 Trocando um Middleware

Você pode trocar um middleware existente na pilha de middlewares usando config.middleware.swap.

Exemplo:

# environment.rb # Substitui ActionController::Failsafe por Lifo::Failsafe config.middleware.swap ActionController::Failsafe, Lifo::Failsafe
3.2.3 A Pilha de Middlewares é um Array

A pilha de middlewares se comporta como um Array normal. Você pode usar quaisquer métodos de Array para inserir, reordenar, ou remover itens da pilha. Os métodos descritos anteriormente são apenas métodos de conveniência.

Por exemplo, a linha a seguir remove o middleware que possua o nome da classe igual ao informado:

config.middleware.delete(middleware)

3.3 Pilha Interna de Middlwares

Muitas das funcionalidades do Action Controller são implementadas como Middlewares. A tabela a seguir explica o propósito de cada um deles:

Middleware Propósito
Rack::Lock Sets env["rack.multithread"] sinaliza para true e encapsula a aplicação dentro de um Mutex.
ActionController::Failsafe Retorna o Status HTTP 500 ao cliente se uma exceção for lançada durante o processamento.
ActiveRecord::QueryCache Habilita o cache de consultas do ActiveRecord.
ActionController::Session::CookieStore Utiliza sessão armazenada em cookies.
ActionController::Session::MemCacheStore Utiliza sessão armazenada no memcached.
ActiveRecord::SessionStore Utiliza sessão armazenada em banco de dados.
Rack::MethodOverride Estabelece um método HTTP baseado no parâmetro _method ou env["HTTP_X_HTTP_METHOD_OVERRIDE"].
Rack::Head Discarta o corpo da resposta se o cliente enviar uma requisição HEAD.

É possível usar qualquer um dos middlewares acima na sua pilha personalizada do Rack.

3.4 Customizando a pilha interna de middlewares

É possível substituir toda a pilha de middlewares por uma pilha personalizada usando ActionController::Dispatcher.middleware=.

Exemplo:

Coloque o seguinte em um initializer:

# config/initializers/stack.rb ActionController::Dispatcher.middleware = ActionController::MiddlewareStack.new do |m| m.use ActionController::Failsafe m.use ActiveRecord::QueryCache m.use Rack::Head end

Agora, inspecionando a pilha de middlewares:

$ rake middleware (in /Users/lifo/Rails/blog) use ActionController::Failsafe use ActiveRecord::QueryCache use Rack::Head run ActionController::Dispatcher.new

3.5 Usando o Rack Builder

A seguir é mostrado como usar o Rack::Builder em vez do MiddlewareStack fornecido pelo Rails.

Limpe a pilha existente de middlewares do Rails

# environment.rb config.middleware.clear


Adicione o arquivo config.ru à raiz do seu projeto RAILS_ROOT

# config.ru use MyOwnStackFromStratch run ActionController::Dispatcher.new

4 Aplicações Rails Metal

Aplicações Rails Metal são aplicações Rack mínimas especificamente projetadas para integração com uma aplicação Rails típica. Como aplicações Rails Metal ignoram toda a pilha do Action Controller, é possível servir uma requisição sem a sobrecarga do framework Rails. Isso é especialmente útil nos raros casos onde o problema é o desempenho de toda a pilha do framework Rails.

O railscast de Ryan Bates Rails Metal fornece um bom passo a passo sobre a utilização do Rails Metal.

4.1 Gerando uma aplicação Metal

O Rails fornece um gerador chamado metal para criar uma nova aplicação Metal:

$ script/generate metal poller

Esse comando gerará o arquivo poller.rb no diretório app/metal:

# Permite que o fragmento metal execute de forma isolada require(File.dirname(__FILE__) + "/../../config/environment") unless defined?(Rails) class Poller def self.call(env) if env["PATH_INFO"] =~ /^\/poller/ [200, {"Content-Type" => "text/html"}, ["Hello, World!"]] else [404, {"Content-Type" => "text/html"}, ["Not Found"]] end end end

Aplicações Metal dentro do diretório app/metal de plugins também serão descobertos e adicionados à lista.

Aplicações Metal são uma otimização. Você deve certificar-se de entender as implicações relacionadas ao desempenho antes de usá-las.

4.2 Ordem de execução

Todas aplicações Metal são executadas pelo middleware Rails::Rack::Metal, que é parte da cadeia ActionController::MiddlewareStack.

Abaixo o método primário, responsável por executar as aplicações Metal:

def call(env) @metals.keys.each do |app| result = app.call(env) return result unless result[0].to_i == 404 end @app.call(env) end

No código acima, @metals é um hash ordenado de aplicações Metal. Devido à ordenação padrão por ordem alfabética, aaa.rb virá antes de bbb.rb na cadeia Metal.

Todavia, é possível alterar a ordem padrão no seu ambiente. Simplesmente adicione uma linha como a seguinte ao arquivo config/environment.rb

config.metals = ["Bbb", "Aaa"]

Cada string no array deve ser o nome da sua classe metal. Se utilizar a configuração anterior, será avisado de que aplicações metal não listadas não serão carregadas.

Aplicações Metal não podem retornar o Status HTTP 404 para um cliente, pois ele é usado para continuar a execução da cadeia Metal. Por favor, use controllers Rails normais ou um middleware personalizado se retornar 404 for um requisito.

5 Recursos

5.1 Aprendendo Rack

5.2 Entendendo os Middlewares

6 Changelog

Lighthouse ticket

  • 28 de Março de 2009: Revisado por Daniel Lopes
  • 28 de Março de 2009: Traduzido por Fernando Luizão
  • 7 de Fevereiro de 2009: Segunda versão por Pratik
  • 11 de Janeiro 2009: Primeira versão por Pratik