Roteamento Rails de fora para dentro – parte 2
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.

