Introdução a Rotas e Responders no Ruby On Rails

Neste artigo analisaremos o funcionamento das rotas (routes) no Rails. Veremos alguns detalhes sobre como o framework trata as requisições e como funciona o recurso de Responders.

Uma rota é um caminho para uma ação de um controlador qualquer e no Rails o arquivo config/routes.rb contém todas as rotas que serão usadas pela aplicação. Ele é automaticamente criado quando o comando rails new é executado e seu conteúdo é semelhante ao da Listagem 1.

Listagem 1. Conteúdo do routers.rb

Agenda::Application.routes.draw do # The priority is based upon order of creation: first created -> highest priority. # See how all your routes lay out with "rake routes". # You can have the root of your site routed with "root" # root 'welcome#index' # Muitos outros comentários aqui… End

Note que o arquivo contém muitos comentários, que são instruções explicando o funcionamento das rotas e com alguns exemplos de construção. Por exemplo, um desses comentários instrui a como alterar a página inicial da aplicação através do seguinte comando:

root 'home#inicio'

Essa instrução é dividida em três partes: a opção root indica que essa é a rota principal, ou seja, define a página inicial da aplicação. Em seguida existe uma String literal que é separada por um sinal de cerquilha (#). A primeira parte dessa String referencia um controlador chamado Home que está na pasta app/controllers, e a segunda representa um método desse controlador chamado inicio. Assim, a página que está no diretório apps/views/home/inicio.html.erb será a primeira a ser apresentada ao usuário.

Configuração

Para configurar uma rota para um controlador chamado ContaController e uma ação com o nome de meu_perfil, poderíamos escrever o código a seguir dentro do bloco contido no arquivo routes.rb:

get 'conta/meu_perfil'

Assim, o Rails infere o nome do controlador (conta) e que método deve ser executado (meu_perfil). O framework também espera que exista um arquivo meu_perfil.html.erb em um diretório com o mesmo nome do controlador na pasta app/views. Essa página será renderizada para o usuário quando o método for executado. Se essa página não existir, um erro informando que ela está faltando será apresentado (Template is missing), como mostra a Figura 1.

Figura 1. Erro ao tentar acessar um controller e action, que não tem uma página associada.

Diante disso, um controlador com actions que não estão mapeadas no arquivo de rotas estarão inacessíveis ou não escutarão as requisições. Veja na Listagem 2 um exemplo de um controlador que tem um método de boas-vindas, bem como uma página associada a ação (Listagem 3), mas sem a necessária configuração de acesso em config/routes.rb.

Listagem 2. Controller - home_controller.rb

class HomeController < ApplicationController def saudacao @mensagem = “Hello World” end end

Listagem 3. View - saudacao.html.erb

<div> <h1><%= @ mensagem %></h1> </div>

O resultado de tentar acessar o método de boas-vindas por meio do path /home/saudacao (ou por qualquer outro path/caminho), resultaria na saída apresentada na Figura 2.

Figura 2. Erro ao tentar acessar um controller e action, que não estão inclusas no arquivo de rotas.

Como a própria descrição do erro diz, não foram encontradas combinações para a rota 'home/saudacao'. O erro ainda esclarece que não há rotas configuradas no arquivo config/routes.rb. Então esse problema pode ser resolvido adicionando a linha a seguir no referido arquivo:

get 'home/saudacao'

Tipos de rotas

O framework Rails possibilita ao desenvolvedor trabalhar de várias formas com as rotas. Na versão 4 os tipos de rotas que estão disponíveis são:

  1. RESTful, que utilizam o padrão REST para representar os recursos;
  2. Nomeadas, que tem esse nome por que, como veremos, são identificadas por um nome único;
  3. Aninhadas, que permitem criar rotas no padrão REST para entidades que possuem relacionamento muitos para um padrão e que fazem a aplicação responder as rotas com um modelo definido, como por exemplo /nome_do_controlador/nome_da_ação;
  4. Regulares, que são a forma de ligar um endereço URL a uma ação em um controlador manualmente.

O Rails, por padrão, favorece o uso das rotas RESTful.

A seguir vamos analisar com mais detalhes essas categorias de rotas.

Rotas RESTful

REST é um padrão arquitetural para a web que utiliza os métodos HTTP (GET, POST, PUT, DELETE) para gerenciar os recursos da aplicação.

A combinação de um método HTTP com o identificador do recurso (URI) diz a aplicação a operação que deve ser realizada com o recurso informado. Veja na Tabela 1 alguns exemplos de como funcionaria em uma aplicação Rails.

Método HTTP URL Método Executado no Controlador Ação
GET /contatos/ index solicita uma lista de contatos
GET /contatos/1 show solicita o contato cujo id é 1
POST /contatos/ create cria um contato baseado em dados enviados em um formulário, por exemplo
PUT / PATCH /contatos/1 update edita as informações do contato cujo identificador é 1
DELETE /contatos/4 destroy exclui o contato cujo identificador é 4

Tabela 1. Uso do REST

Para que todas essas operações estejam acessíveis é necessário ter uma rota descrita no arquivo responsável por isso para cada umas delas, como mostra o exemplo da Listagem 4.

Listagem 4. Codificação dos exemplos da Tabela 1

Agenda::Application.routes.draw do get 'contatos', controller: 'contatos', action: 'index' → lista os contatos get 'contatos/:id', controller: 'contatos', action: 'show', as: 'contato' → recupera o contato com o identificador passado patch 'contatos/:id', controller: 'contatos', action: 'update' → atualiza os dados do contato com as informações passadas post 'contatos', controller: 'contatos', action: 'create' → cria um contato com os dados passados delete 'contatos/:id', controller: 'contatos', action: 'destroy' → exclui o contato pelo identificador passado end

As linhas do código são formadas por: método HTTP, identificador de recurso (URI), nome do controlador, ação ou método do controlador.

Veja que o código para as rotas de apenas uma entidade não é pequeno, mas sim muito verboso e cheio de detalhes, o que seria muito repetitivo de implementar caso tivéssemos muitas entidades.

Então, favorecendo a filosofia DRY (Don't Repeat Yourself), o framework nos dá uma maneira muito mais simples e enxuta de criarmos todas essas rotas, como mostra a Listagem 5.

Listagem 5. Exemplo da Listagem 4 de acordo com o Rails

Agenda::Application.routes.draw do resources :contatos end

Rotas Nomeadas

Também é possível personalizar a URL para acesso a algum recurso da aplicação por meio das named routes, que recebem um identificador único servindo como um alias ou atalho para acesso a ação. Para isso basta acrescentar a opção 'as', como no exemplo a seguir:

get 'contas/efetuar_logout', controller: 'contas', action: 'efetuar_logout', as: 'sair'

A instrução as: 'sair', diz ao framework Rails que agora é possível acessar a ação efetuar_logout do controlador contas por meio da URL sair.

Quando definimos rotas nomeadas, o Rails cria dois helpers automaticamente. No caso do exemplo anterior criou-se o sair_path e sair_url e ambos ficam disponíveis na camada de visão e na camada de controle.

O primeiro retorna o endereço parcial do recurso (/sair), enquanto o segundo retorna o endereço completo incluindo o domínio da aplicação (por exemplo www.dominiodasuaaplicacao.com.br/sair).

Esses dois helpers criados pelo framework são de bastante ajuda para construirmos hiperlinks. Para isso o Rails disponibiliza o helper link_to, definido em um módulo chamado UrlHelper, que está disponível na camada de visão. Esse helper é transformado, em tempo de execução, em uma tag <a href=”” />do HTML, como mostra o exemplo a seguir:

<%= link_to “Sair”, “/sair” %>

Veja que um hiperlink HTML é criado e aponta para o endereço sair. Primeiro temos a chamada ao método link_to(), seguido do texto que aparecerá como rótulo do link, e em seguida o endereço. Poderíamos, ao invés de passar a string literal, usar o helper sair_path, criado para este fim, como no exemplo a seguir:

<%= link_to “Sair”, sair_path %>

Também é possível fazer usos destes helpers criados com as rotas nomeadas a partir do controlador.

A Listagem 6 redireciona o usuário para a action de logout sem nem mesmo escrever o endereço URL.

Listagem 6. Exemplo de helper a partir do controlador

def metodo # alguma lógica aqui redirect_to sair_path end

Rotas Aninhadas

Esse recurso permite criar rotas para modelos que necessitam de um novo modelo ou que estão aninhados a outro modelo, como mostra a o exemplo da Listagem 7.

Listagem 7. Exemplo de Rota aninhada

class Pedido < ActiveRecord::Base has_many :itens end class Item < ActiveRecord::Base belongs_to :nota_fiscal end

A classe pedido tem muitos itens e isso é definido pelo relacionamento has_many. Cada item pertence a um pedido definido com a instrução belongs_to. Então existe um relacionamento entre as classes, de modo que, para que um item seja criado, por exemplo, ele deve estar aninhado a um pedido. Podemos criar as rotas referentes a este relacionamento com o código da Listagem 8 no arquivo config/routes.rb.

Listagem 8. Rotas de relacionamento entre as classes

Agenda::Application.routes.draw do resources :pedidos do |pedido| pedido.resources :items end end

Também há um atalho para esse código:

Agenda::Application.routes.draw do resources :pedidos, :has_many => :items end

Além de criar todas as rotas necessárias para o recurso Pedido, o código apresentado cria também todas as rotas que o modelo Item necessitará, conforme mostra a Tabela 2.

Método HTTP URL Método Executado no Controlador Ação
GET /pedidos/1/itens/ index requisita os itens do pedido com identificador 1
GET /pedidos/1/itens/1 show solicita o item do pedido 1 cujo identificador é 1
POST /pedidos/1/itens create cria um item baseado em dados enviados em um formulário, e associa o mesmo com o pedido de identificador 1
PUT / PATCH /pedidos/1/itens/1 update atualiza um pedido de identificador 1 pertencente a pedido um pedido específico
DELETE /pedidos/1/itens/1 destroy exclui o item com identificador 1 de um determinado pedido

Tabela 2. Rotas de pedidos

Caso seja necessário adicionar mais rotas do que as do padrões RESTful, que estão listadas, também é possível fazê-lo utilizando a opção :new. O código a seguir demonstra como:

Agenda::Application.routes.draw do resources :pedidos, :new => { :cancelar => :post }, :has_many => :items end

A opção :new adicionada na definição da rota faz com que a aplicação passe a aceitar uma requisição no endereço /pedidos/cancelar, usando o método post para isso.

Além disso, essa opção também pode ser usada para mudar o método HTTP que uma rota aceita. Por padrão, a ação show, por exemplo, aceita apenas requisições que usam o método get. É possível mudar isso fazendo como mostrado a seguir:

Agenda::Application.routes.draw do resources :pedidos, :new => { :show => :any }, :has_many => :items end

A instrução :new => { :show => :any } diz que o método show no controlador de pedidos deve aceitar requisições, independentemente do método HTTP enviado.

Rotas Padrão

As rotas default dão a possibilidade para o aplicativo responder a qualquer requisição que tenha o padrão /nome_do_controlador/nome_da_ação_opcional/parametros_opcionais, sem ter que definir literalmente as rotas no arquivo config/routes.rb, como mostra o exemplo a seguir;

Agenda::Application.routes.draw do match '/:controller(/:action(/:id))', :via => [:get, :post, :path, :put, :delete] end

Isso fará com que, ao ser solicitada pelo navegador uma URL no padrão mencionado no exemplo, o aplicativo responda, aceitando as requisições com os métodos HTTP listados na opção 'via'.

A sintaxe das rotas padrões mudaram a partir da versão 3 do framework, então o código apresentado no exemplo só irá funcionar da versão 3 até a atual. Para versões anteriores a sintaxe era um pouco diferente:

Agenda::Application.routes.draw do |map| map.connect ':controller/:action/:id' end

Rotas Regulares

As rotas regulares são a forma não automática que possibilitam associar uma rota diretamente a um controlador e a sua ação desejada. Ela é tida como não automática porque as rotas precisam ser declaradas uma a uma, ou seja, não existem “atalhos”, como no caso das rotas RESTful.

O exemplo a seguir demostra o uso deste tipo de rotas:

Agenda::Application.routes.draw do match 'contatos', controller: 'contatos', action: 'index', :via => 'get' end

É notado que a instrução defini o nome do controlador e da sua respectiva ação. Esse código poderia ficar um pouco menor com o uso da opção ':to':

Agenda::Application.routes.draw do match 'contatos', :to => 'contatos#index', :via => 'get' end

Nesse código vemos que foi passado para a opção 'to:' o nome do controlador e da ação separados apenas pelo sinal de cerquilha (#).

Estes códigos demonstrados também não funcionaram em versões anteriores a 3, que usavam a seguinte sintaxe:

Agenda::Application.routes.draw do |map| map.connect 'contatos', :controller => 'contatos', :action => 'index' end

Listando rotas existentes

Com o objetivo de verificar e até validar as rotas que estão sendo criadas na fase de desenvolvimento, é possível listar as rotas existentes na aplicação. Existem maneiras diferentes de fazer isso e uma delas é acessando o endereço http://localhost:3000/rails/info/routes no seu navegador com o servidor ativo. Esse recurso estará disponível no ambiente de desenvolvimento. A saída é uma tabela que mostra o nome da rota, o método HTTP usado e a URL. Na Tabela 3 é apresentado um exemplo do formato dessa tabela de uma agenda de contatos.

Helper URL Helper Path Método HTTP Path Controller#Action
contatos_url contatos_path GET /contatos(.:format) contatos#index
POST /contatos(.:format) contatos#create
new_contato_url new_contato_path GET /contatos/new(.:format) contatos#new
edit_contato_url edit_contato_path GET /contatos/:id/edit(.:format) contatos#edit
contatos_url contato_path GET /contatos/:id(.:format) contatos#show
PATCH /contatos/:id(.:format) contatos#update
PUT /contatos/:id(.:format) contatos#update
DELETE /contatos/:id(.:format) contatos#destroy

Tabela 3. Exemplo de retorno de rotas

Também é possível ter acesso a esses dados digitando no console o comando a seguir:

rake routes

O resultado dessa instrução é parecido com a Tabela 3. Existe ainda a opção de filtrar as rotas por controlador, digitando o comando a seguir, novamente no console:

CONTROLLER=contatos rake routes

Responders

O responder é uma característica do Rails que permite a um método de um controlador fornecer um resultado em mais de um formato, dependendo do que for solicitado na requisição. Por exemplo, poderíamos implementar uma ação index em um controlador Pedido para que este retorne uma lista de pedidos em HTML, XML ou JSON.

O framework vai decidir qual será o formato da resposta baseado no cabeçalho HTTP_ACCEPT vindo na requisição. Podemos fazer isso usando o método responto_to, como mostra a Listagem 9.

Listagem 9. Método responto_to

class PedidosController < ApplicationController def index @pedidos = Pedido.find(:all) respond_to do |formato| formato.html formato.xml {render xml: @pedidos.to_xml} formato.json {render json: @pedidos.to_json} end end def show @pedido = Pedido.find(params[:id]) respond_to do |formato| formato.html formato.xml {render xml: @pedido.to_xml} formato.json {render json: @pedido.to_json} end end end

Os dois métodos possuem um bloco de código passado para o método respond_to. A instrução formato.html diz ao controlador para responder a requisição mostrando o template ou página relacionada ao método.

No caso do método index, a página é app/view/pedidos/index.html.erb; já no caso do método show, app/view/pedidos/show.html.erb.

As páginas seguintes usam os métodos to_xml e to_json para transformar os objetos vindos do banco de dados em XML e JSON, respectivamente.

Se for necessário ter mais de um método no controlador que faça esta espécie de tratamento do formato da resposta que deve ser enviada ao cliente, o bloco de código associado ao método respond_to se repetirá. Isso é claramente visto nos métodos declarados anteriormente, onde o código relacionado ao formato da resposta é semelhante em ambos.

Para evitar essa duplicação de código o framework Rails fornece outra maneira de fazer isso, como mostra a Listagem 10.

Listagem 10. Mesmo exemplo da Listagem 9, sem duplicação

class PedidosController < ApplicationController respond_to :html, :xml, :json def index @pedidos = Pedido.find(:all) respond_with(@pedidos) end def show @pedido = Pedido.find(params[:id]) respond_with(@pedido) end end

A instrução respond_to no início da classe diz quais formatos os métodos do controlador poderão responder ao cliente e então nos respectivos métodos que necessitem retornar a resposta nos formatos especificados só precisam passar o objeto como parâmetro ao método respond_with.

Com essa segunda abordagem, a única instrução que se repetirá é a respond_to, no caso de muitos controladores tratarem a resposta. Se este for o caso, essa instrução pode ser inserida no ApplicationController e então será herdado pelos demais controladores, como segue no exemplo da Listagem 11.

Listagem 11. Exemplo de herança

class ApplicationController < ActionController::Base protect_from_forgery with: :exception respond_to :html, :xml, :json end class PedidosController < ApplicationController def index @pedidos = Pedido.find(:all) respond_with(@pedidos) end def show @pedido = Pedido.find(params[:id]) respond_with(@pedido) end end class ComprasController < ApplicationController def index @compras = Compra.find(:all) respond_with(@compras) end def show @compra = Compra.find(params[:id]) respond_with(@compra) end end

Essa foi uma pequena introdução às rotas do framework Rails e também ao recurso Responders, que agilizam o processo de criação da aplicação. Sempre busque a documentação do framework para incrementar a sua codificação.

Artigos relacionados