Ceará On Rails 2009 - Vou Participar Art Of Community

Roteamento Rails de fora para dentro – parte 2

Primeira parte

Terceira parte

Quarta parte

3 Roteamento RESTful: o Padrão Rails

Roteamento RESTful é o padrão atual de roteamento no Rails, e a única que você deve escolher para novas aplicações. Pode demorar um pouco enquanto você entende como funciona o roteamento RESTful, mas que vale o esforço; seu código será mais fácil de ser lido e você estará trabalho com Rails, ao invés de lutar contra ele, quando você usa este estilo de roteamento.

3.1 O que é REST?

A Fundação do roteamento RESTful é geralmente considerada na tese de doutorado de Roy Fielding, Architectural Styles and the Design of Network-based Software Architectures. Felizmente, você não precisa ler este documento para entender como o REST funciona no Rails. REST é um acrônimo para Representational State Transfer, que resume-se em dois princípios fundamentais para nossos propósitos:

  • Usando identificadores de recurso (na qual, para o propósito da discussão, você pode pensar como as URLs) para representar recursos
  • Transferindo representações de estados entre recursos e componentes do sistema.

Por exemplo, para uma requisição em uma aplicação Rails como esta:

DELETE /photos/17

seria entendido como uma referencia a um recurso photo com o ID 17, e indicaria a ação desejada – deletar este recurso. REST é um estilo natural para a arquitetura de aplicações web, e Rails faz isso de uma forma mais natural usando convenções para proteger você de algumas complexidades do RESTful.

3.2 CRUD, verbos e ações

No Rails, uma rota RESTful fornece o mapeamento entre verbos HTTP, ações de controladores, e (implicitamente) a operações CRUD no banco de dados. Uma entrada única no arquivo de roteamento, como essa

map.resources :photos

cria sete diferentes rotas na sua aplicação:

HTTP verb URL controller action used for
GET /photos Photos index mostra a lista de todas as fotos
GET /photos/new Photos new retorna um formulário HTML para a criação de uma nova foto
POST /photos Photos create cria uma nova foto
GET /photos/1 Photos show mostra uma foto específica
GET /photos/1/edit Photos edit retorna um formulário HTML para edição da foto
PUT /photos/1 Photos update atualiza uma foto específica
DELETE /photos/1 Photos destroy apaga uma foto específica

Para estas rotas específicas (aquelas que fazem referencia a um único recurso), o identificador do recurso deverá estar disponível na ação correspondente de um controlador como um params[:id].

Se você consistentemente usar rotas RESTful na sua aplicação, você deverá desabilitar as rotas padrões em routes.rb de modo que o Rails aplicará o mapeamento entre verbos HTTP e rotas.

3.3 URLs e Caminhos

Criando uma rota RESTful também tornará disponível um monte de helpers dentro da sua aplicação:

  • photos_url e photos_path mapeia do caminho para as ações index e create.
  • new_photo_url e new_photo_path mapeia o caminho para a ação new
  • edit_photo_url e edit_photo_path mapeia o caminho para a ação edit
  • photo_url e photo_path mapeia o caminho para as ações show, update e destroy

Por que o roteamento faz uso de verbos HTTP, bem como o caminho no pedido para expedir requisições, as sete rotas geradas pelo roteamento RESTful só dão origem a quatro pares de helpers.

Em cada caso, o helper _url gera uma string contendo toda a URL que a aplicação irá entender, enquanto o helper _path gera uma string contendo um caminho relativo para a raiz da aplicação. Por exemplo:

photos_url # => "http://www.example.com/photos"
photos_path # => "/photos"

3.4 Definindo Múltiplos Recursos ao Mesmo Tempo

Se você precisa criar rotas para mais de um recurso RESTful, você pode diminuir um pouco de digitar, definindo todas as chamadas para map.resources:

map.resources :photos, :books, :videos

Isto tem exatamente o mesmo efeito de

map.resources :photos map.resources :books map.resources :videos

3.5 Recursos singulares

Você pode também aplicar o roteamento RESTful para um único recurso dentro da sua aplicação. Neste caso, você usa map.resource em vez de map.resources e a geração das rotas é ligeiramente diferente. Por exemplo, uma entrada para o roteamento

map.resource :geocoder

cria seis rotas diferentes na sua aplicação:

HTTP verb URL controller action used for
GET /geocoder/new Geocoders new retorna um formulário HTML para criação de um novo geocoder
POST /geocoder Geocoders create cria um novo geocoder
GET /geocoder Geocoders show mostra um e somente um recurso geocoder
GET /geocoder/edit Geocoders edit retorna um formulário HTML para edição do geocoder
PUT /geocoder Geocoders update atualiza um e somente um recurso do geocoder
DELETE /geocoder Geocoders destroy apaga um recurso geocoder

Mesmo que o nome do recurso seja singular em routes.rb, o controlador correspondente continua no plural.

Uma rota RESTful singular gera um conjunto abreviados de helpers:

  • new_geocoder_url e new_geocoder_path mapeia o caminho para a ação new
  • edit_geocoder_url e edit_geocoder_path mapeia o caminho para a ação edit
  • geocoder_url e geocoder_path mapeia o caminho para as ações create, show, update e destroy.

3.6 Customizando recursos

Apesar das convenções do roteamento RESTful serem suficientes para muitas aplicações, existe inúmeras formas de customizar a forma como uma rota RESTful funciona. Estas opções incluem:

  • :controller
  • :singular
  • :requirements
  • :conditions
  • :as
  • :path_names
  • :path_prefix
  • :name_prefix

Você pode adicionar rotas adicionais pelas opções :member e :collection, na qual serão discutidos mais tarde neste guia.

3.6.1 Usando :controller

A opção :controller permite você usar o nome do controlador diferente do nome do recurso público. Por exemplo, esta entrada no roteamento:

map.resources :photos, :controller => "images"

reconhecerá o recebimento de URLs contendo photo mas a requisição das rotas para o controlador Images :

HTTP verb URL controller action used for
GET /photos Images index mostra a lista de todas as imagens
GET /photos/new Images new retorna um formulário HTML para criação de uma nova imagem
POST /photos Images create cria uma nova imagem
GET /photos/1 Images show mostra uma imagem específica
GET /photos/1/edit Images edit retorna um formulário HTML para edição da imagem
PUT /photos/1 Images update atualiza uma imagem específica
DELETE /photos/1 Images destroy apaga uma imagem específica

Os helpers serão gerados com o nome do recurso, não com o nome do controlador. Portanto neste caso você receberá photos_path, new_photo_path, e assim por diante.

3.7 ‘Namespaces’ de Controladores e Roteamento

Rails permite que você agrupe seus controladores dentro de ‘namespaces’ salvando dentro de pastas debaixo de app/controllers. A opção :controllers fornece uma forma conveniente para usar essas rotas. Por exemplo, você pode ter um recurso cujo controlador é apenas para administração de usuários na pasta admin:

map.resources :adminphotos, :controller => "admin/photos"

Se você usa o namespaces do controlador, você precisa ter cuidado com a sutileza no código de roteamento do Rails: ele tenta preservar o máximo do namespace de uma requisição anterior o quanto possível. Por exemplo, se você estiver em uma visão gerada pelo helper adminphoto_path, e seguida de um link gerado com <%= link_to "show", adminphoto(1) %> você acabará com a visão gerada por admin/photos/show mas você vai acabar no mesmo lugar se você tiver <%= link_to "show", {:controller => "photos", :action => "show"} %> por quê o Rails mostrará a URL relativa a URL atual.

Se você quiser garantir que o link vá para um controlador de nível superior, use uma barra precedendo a âncora para o nome do controlador: <%= link_to "show", {:controller => "/photos", :action => "show"} %>

Você pode especificar o namespace do controlador com a opção :namespace ao invés do caminho:

map.resources :adminphotos, :namespace => "admin", :controller => "photos"

Isso pode ser especialmente útil quando combinada com with_options para mapear múltiplas rotas com namespace:

map.with_options(:namespace => "admin") do |admin|
admin.resources :photos, :videos
end

Isso iria lhe dar o roteamento para os controladores admin/photos e admin/videos.

3.7.1 Usando :singular

Se por alguma razão o Rails não está fazendo o que você deseja, convertendo o nome do recurso do plural para um único nome no membro das rotas, você pode substituir seu julgamento com a opção :singular:

map.resources :teeth, :singular => "tooth"

Dependendo de outros códigos na sua aplicação, você pode optar em adicionar regras adicionais para a classe Inflector.

3.7.2 Usando :requirements

Você pode usar a opção :requirements em uma rota RESTful para impor um formato implícito sobre o parâmetro :id em rotas singulares. Por exemplo:

map.resources :photos, :requirements => {:id => /[A-Z][A-Z][0-9]+/}

Esta declaração obriga o parâmetro combinar com a expressão regular fornecida. Então, neste caso, /photos/1 não irá ser reconhecida por esta rota, mas /photos/RR27 irá.

3.7.3 Usando :conditions

Condições no roteamento Rails são usadas atualmente para ajustar o verbo HTTP para rotas individuais. Apesar de na teoria você poder ajustar isto para as rotas RESTful, na prática não são é uma boa razão para fazê-lo. (Você aprenderá mais sobre condições na discussão de roteamento clássico depois neste guia)

3.7.4 Usando :as

A opção :as permite que você sobrescreva o nomeamento normal para os paths gerados atualmente. Por exemplo:

map.resources :photos, :as => "images"

reconhecerá URLS recebidas contendo image mas a rota requisita o controlador Photos:

HTTP verb URL controller action used for
GET /images Photos index mostra a lista de todas as fotoso
GET /images/new Photos new retorna um formulário HTML para criação de uma novo foto
POST /images Photos create cria uma nova foto
GET /images/1 Photos show mostra uma foto específica
GET /images/1/edit Photos edit retorna um formulário HTML para edição de uma foto
PUT /images/1 Photos update atualiza uma foto específica
DELETE /images/1 Photos destroy apaga uma foto específica

Os helpers irão ser gerados com o mesmo nome do recurso, não o nome path. Então neste caso, você ainda obterá photos_path, new_photo_path, e assim por diante.

3.7.5 Usando :path_names

A opção :path_names permite que você sobrescreva os segmentos “new” e “edit” gerados automaticamente nas URLs:

map.resources :photos, :path_names => { :new => 'make', :edit => 'change' }

Isto causaria o roteamento para URLs reconhecidas como

/photos/make
/photos/1/change

Os nomes das ações atuais não serão alterados por esta opção; as duas URLs que mostram as rotas para as ações new e edit continuam funcionando.

Se você está querendo mudar esta opção de modo uniforme para todas as suas rotas, você pode definir um padrão em seu environment:

config.action_controller.resources_path_names = { :new => 'make', :edit => 'change' }
3.7.6 Usando :path_prefix

A opção :path_prefix permite que você adicione parâmetros adicionais que serão prefixadas para as rotas reconhecidas. Por exemplo, suponha que cada foto na sua aplicação possua a um fotografo em particular. No caso, você deve declarar esta rota:

map.resources :photos, :path_prefix => '/photographers/:photographer_id'

Rotas reconhecidas por esta entrada incluem:

/photographers/1/photos/2
/photographers/1/photos

Na maioria dos casos, é mais simples de reconhecer URLs deste tipo, criando recursos aninhados, como discutido na próxima seção

Você também pode usar :path_prefix com rotas não RESTful.

3.7.7 Usando :name_prefix

Você pode usar a opção :name_prefix para evitar colisões entre rotas. Isto é mais usando quando você tem dois recursos com o mesmo nome que usam :path_prefix para mapear diferentemente. Por exemplo:

map.resources :photos, :path_prefix => '/photographers/:photographer_id', :name_prefix => 'photographer_'
map.resources :photos, :path_prefix => '/agencies/:agency_id', :name_prefix => 'agency_'

Com esta combinação você irá receber helpers tais como photographer_photos_path e agency_edit_photo_path para usar no seu código.

Você pode usar :name_prefix com rotas não RESTful.

Primeira parte

Terceira parte

Quarta parte