El enrutamiento de recursos le permite declarar rápidamente todas las rutas comunes para un controlador con recursos. En lugar de declarar rutas separadas para sus acciones index
, show
, new
, edit
, create
, update
y destroy
, una ruta de recursos las declara en una sola línea de código.
Los navegadores solicitan páginas desde Rails realizando una solicitud de URL
utilizando un método HTTP
específico, como GET
, POST
, PATCH
, PUT
y DELETE
. Cada método es una solicitud para realizar una operación en el recurso. Una ruta de recursos traza un número de solicitudes relacionadas a acciones en un solo controlador.
Cuando su aplicación Rails recibe una solicitud de entrada para:
DELETE /photos/17
Pide al enrutador que lo mande a una acción del controlador. Si la primera ruta coincidente es:
resources :photos
Rails enviará esa solicitud a la acción destroy
en el controlador de fotos con {id: '17'}
en params.
En Rails, una ruta de recursos proporciona una correlación entre los verbos HTTP
y las URL
a las acciones del controlador. Por convención, cada acción también se correlaciona con una operación CRUD
específica en una base de datos. Una sola entrada en el archivo de enrutamiento, como por ejemplo:
resources :photos
crea siete rutas diferentes en su aplicación, todas mapeando al controlador de fotos:
HTTP Verb | Path | Controller#Action | Used for |
---|---|---|---|
GET | /photos | photos#index | muestra una lista de todas las fotos |
GET | /photos/new | photos#new | devuelve un formulario HTML para crear una nueva foto |
POST | /photos | photos#create | crea una nueva foto |
GET | /photos/:id | photos#show | muestra una foto en especifico |
GET | /photos/:id/edit | photos#edit | retorna un formulario HTML para editar la foto |
PATCH/PUT | /photos/:id | photos#update | actualiza una foto en especifico |
DELETE | /photos/:id | photos#destroy | elimina una foto en especifico |
Debido a que el enrutador usa el verbo
HTTP
y laURL
para coincidir con las solicitudes entrantes, cuatro direcciones URL asignan siete acciones diferentes.Las rutas de Rails se emparejan en el orden en que se especifican, por lo que si tienes
resources :photos
arriba deget 'photos/poll'
la ruta de la acciónshow
para la línea deresources
coincidirá antes de la líneaget
. Para arreglar esto, mueva la líneaget
por encima de la línea deresources
para que coincida primero.
La creación de una ruta resources
también expondrá una serie de ayudantes a los controladores en su aplicación. En el caso de resources :photos
photos_path
retorna/photos
new_photo_path
retorna/photos/new
edit_photo_path(:id)
retorna/photos/:id/edit
(por ejemplo,edit_photo_path(10)
retorna/photos/10/edit
)photo_path(:id)
retorna/photos/:id
(por ejemplo,photo_path(10) retorna
/photos/10
)
Cada uno de estos ayudantes tiene un ayudante _url
correspondiente (como photos_url
) que devuelve la misma ruta prefijada con el prefijo actual de host, puerto y ruta.
Si necesita crear rutas para más de un recurso, puede guardar un poco de escritura definiéndolas todas con una sola llamada a resources
:
resources :photos, :books, :videos
Esto funciona exactamente igual que:
resources :photos
resources :books
resources :videos
A veces, tiene un recurso que los clientes siempre buscan sin hacer referencia a un ID
. Por ejemplo, desea que /profile
muestre siempre el perfil del usuario actualmente conectado. En este caso, puede utilizar un recurso singular para asignar /profile
(en lugar de /profile/:id
) a la acción show
:
get 'profile', to: 'users#show'
Pasar un string a get
esperará un formato controller#action
, y al pasar un símbolo se asignará directamente a una acción, pero también debe especificar el controller:
a utilizar:
get 'profile', to: :show, controller: 'users'
Esta ruta resourceful (en singular):
resource :geocoder
Crea seis rutas diferentes en su aplicación, todas mapeadas al controlador Geocoders
:
HTTP Verb | Path | Controller#Action | Used for |
---|---|---|---|
GET | /geocoder/new | geocoders#new | retorna un formulario HTML para crear el geocoder |
POST | /geocoder | geocoders#create | crea el nuevo geocoder |
GET | /geocoder | geocoders#show | muestra el único recurso de geocoder |
GET | /geocoder/edit | geocoders#edit | devuelve un formulario HTML para editar el geocoder |
PATCH/PUT | /geocoder | geocoders#update | actualiza el único recurso de geocoder |
DELETE | /geocoder | geocoders#destroy | elimina el recurso geocoder |
Como es posible que desee utilizar el mismo controlador para una ruta singular (/account
) y una ruta plural (/accounts/45
), los recursos singulares se asignan a controladores múltiples. Por ejemplo, resource: photo
y resources: photos
crea rutas singulares y plurales que se asignan al mismo controlador (PhotosController
).
Una ruta singular genera estos ayudantes:
new_geocoder_path
retorna/geocoder/new
edit_geocoder_path
retorna/geocoder/edit
geocoder_path
retorna/geocoder
Al igual que con recursos en plural, los mismos helpers que terminan en _url
también incluirán el prefijo host, port y path.
Un error de larga data impide que form_for
trabaje automáticamente con recursos singulares. Como solución, especifique la URL del formulario directamente, de la siguiente manera:
form_for @geocoder, url: geocoder_path do |f|
# snippet for brevity
Es posible que desee organizar grupos de controladores en un espacio de nombres. Más comúnmente, puede agrupar varios controladores administrativos bajo un Admin::
namespace. Usted puede colocar estos controladores en el directorio app/controllers/admin
, y puede agruparlos en su enrutador:
namespace :admin do
resources :articles, :comments
end
Esto creará una serie de rutas para cada uno de los controladores articles
y comments
. Para Admin::ArticlesController
, Rails creará:
HTTP Verb | Path | Controller#Action | Named Helper |
---|---|---|---|
GET | /admin/articles | admin/articles#index | admin_articles_path |
GET | /admin/articles/new | admin/articles#new | new_admin_article_path |
POST | /admin/articles | admin/articles#create | admin_articles_path |
GET | /admin/articles/:id | admin/articles#show | admin_article_path(:id) |
GET | /admin/articles/:id/edit | admin/articles#edit | edit_admin_article_path(:id) |
PATCH/PUT | /admin/articles/:id | admin/articles#update | admin_article_path(:id) |
DELETE | /admin/articles/:id | admin/articles#destroy | admin_article_path(:id) |
Si desea enrutar /articles
(sin el prefijo /admin
) a Admin::ArticlesController
, puede utilizar:
scope module: 'admin' do
resources :articles, :comments
end
O, en un solo caso:
resources :articles, module: 'admin'
Si desea enrutar /admin/artículos
a ArticlesController
(sin el prefijo Admin::module
), puede utilizar:
scope '/admin' do
resources :articles, :comments
end
O, en un solo caso:
resources :articles, path: '/admin/articles'
En cada uno de estos casos, las rutas nombradas siguen siendo las mismas que si no utiliza el ámbito. En el último caso, las siguientes rutas mapean a ArticlesController
:
HTTP Verb | Path | Controller#Action | Named Helper |
---|---|---|---|
GET | /admin/articles | articles#index | articles_path |
GET | /admin/articles/new | articles#new | new_article_path |
POST | /admin/articles | articles#create | articles_path |
GET | /admin/articles/:id | articles#show | article_path(:id) |
GET | /admin/articles/:id/edit | articles#edit | edit_article_path(:id) |
PATCH/PUT | /admin/articles/:id | articles#update | article_path(:id) |
DELETE | /admin/articles/:id | articles#destroy | article_path(:id) |
Si necesita usar un espacio de nombres de controlador diferente dentro de un bloque de espacio de nombres, puede especificar una ruta de controlador absoluta, por ejemplo: get '/foo' => '/foo#index
''.
Es común tener recursos que son lógicamente hijos de otros recursos. Por ejemplo, suponga que su aplicación incluye estos modelos:
class Magazine < ApplicationRecord
has_many :ads
end
class Ad < ApplicationRecord
belongs_to :magazine
end
Las rutas anidadas permiten capturar esta relación en el enrutamiento. En este caso, puede incluir esta declaración de ruta:
resources :magazines do
resources :ads
end
Además de las rutas para magazines, esta declaración también enruta los anuncios a un AdsController.
Las URL de los ads requieren un magazine:
HTTP Verb | Path | Controller#Action | Used for |
---|---|---|---|
GET | /magazines/:magazine_id/ads | ads#index | muestra una lista de todos los anuncios de una revista específica |
GET | /magazines/:magazine_id/ads/new | ads#new | devuelve un formulario HTML para crear un nuevo anuncio perteneciente a una revista específica |
POST | /magazines/:magazine_id/ads | ads#create | crea un nuevo anuncio perteneciente a una revista específica |
GET | /magazines/:magazine_id/ads/:id | ads#show | muestra un anuncio específico perteneciente a una revista específica |
GET | /magazines/:magazine_id/ads/:id/edit | ads#edit | devuelve un formulario HTML para editar un anuncio perteneciente a una revista específica |
PATCH/PUT | /magazines/:magazine_id/ads/:id | ads#update | actualiza un anuncio específico perteneciente a una revista específica |
DELETE | /magazines/:magazine_id/ads/:id | ads#destroy | elimina un anuncio específico perteneciente a una revista específica |
Esto también creará ayudantes de enrutamiento como magazine_ads_url
y edit_magazine_ad_path
. Estos ayudantes toman una instancia de magazine como el primer parámetro (magazine_ads_url(@magazine)
).
Puede anidar recursos dentro de otros recursos anidados si lo desea. Por ejemplo:
resources :publishers do
resources :magazines do
resources :photos
end
end
Los recursos profundamente anidados se vuelven rápidamente engorrosos. En este caso, por ejemplo, la aplicación reconocería rutas tales como:
/publishers/1/magazines/2/photos/3
El auxiliar de ruta correspondiente sería publisher_magazine_photo_url
, lo que requiere que especifique objetos en los tres niveles. De hecho, esta situación es bastante confusa que un artículo popular de Jamis Buck propone una regla de oro para el buen diseño de Rails:
Los recursos nunca deben anidarse más de 1 nivel de profundidad.
Una manera de evitar la anidación profunda (como se recomienda anteriormente) es generar una coleccion de acciones dentro de un ambito del pariente, para obtener un sentido de la jerarquía, pero no anidar las acciones de los miembros. En otras palabras, sólo para construir rutas con la cantidad mínima de información para identificar de forma exclusiva el recurso, como esto:
resources :articles do
resources :comments, only: [:index, :new, :create]
end
resources :comments, only: [:show, :edit, :update, :destroy]
Esta idea establece un equilibrio entre las rutas descriptivas y el anidamiento profundo. Existe sintaxis abreviada para lograr eso, a través de la opción :shallow
:
resources :articles do
resources :comments, shallow: true
end
Esto generará exactamente las mismas rutas que el primer ejemplo. También puede especificar la opción :shallow
en el recurso padre, en cuyo caso todos los recursos anidados serán superficiales (shallow
):
resources :articles, shallow: true do
resources :comments
resources :quotes
resources :drafts
end
El método superficial (shallow
) de la DSL crea un ámbito dentro del cual cada anidado es superficial. Esto genera las mismas rutas que el ejemplo anterior:
shallow do
resources :articles do
resources :comments
resources :quotes
resources :drafts
end
end
Existen dos opciones de ámbito para personalizar rutas poco profundas. prefijos :shallow_path
del path miembro con el parámetro especificado:
scope shallow_path: "sekret" do
resources :articles do
resources :comments, shallow: true
end
end
El recurso de comentarios aquí tendrá las siguientes rutas generadas:
HTTP Verb | Path | Controller#Action | Named Helper |
---|---|---|---|
GET | /articles/:article_id/comments(.:format) | comments#index | article_comments_path |
POST | /articles/:article_id/comments(.:format) | comments#create | article_comments_path |
GET | /articles/:article_id/comments/new(.:format) | comments#new | new_article_comment_path |
GET | /sekret/comments/:id/edit(.:format) | comments#edit | edit_comment_path |
GET | /sekret/comments/:id(.:format) | comments#show | comment_path |
PATCH/PUT | /sekret/comments/:id(.:format) | comments#update | comment_path |
DELETE | /sekret/comments/:id(.:format) | comments#destroy | comment_path |
La opción: shallow_prefix
añade el parámetro especificado a los ayudantes nombrados:
scope shallow_prefix: "sekret" do
resources :articles do
resources :comments, shallow: true
end
end
El recurso comments
aquí tendrá las siguientes rutas generadas para él:
HTTP Verb | Path | Controller#Action | Named Helper |
---|---|---|---|
GET | /articles/:article_id/comments(.:format) | comments#index | article_comments_path |
POST | /articles/:article_id/comments(.:format) | comments#create | article_comments_path |
GET | /articles/:article_id/comments/new(.:format) | comments#new | new_article_comment_path |
GET | /comments/:id/edit(.:format) | comments#edit | edit_sekret_comment_path |
GET | /comments/:id(.:format) | comments#show | sekret_comment_path |
PATCH/PUT | /comments/:id(.:format) | comments#update | sekret_comment_path |
DELETE | /comments/:id(.:format) | comments#destroy | sekret_comment_path |
El Routing concerns le permite declarar rutas comunes que pueden reutilizarse dentro de otros recursos y rutas. Para definir un concerns:
concern :commentable do
resources :comments
end
concern :image_attachable do
resources :images, only: :index
end
Estos concerns pueden utilizarse en recursos para evitar la duplicación de código y compartir el comportamiento entre rutas:
resources :messages, concerns: :commentable
resources :articles, concerns: [:commentable, :image_attachable]
Lo anterior es equivalente a:
resources :messages do
resources :comments
end
resources :articles do
resources :comments
resources :images, only: :index
end
También puedes usarlos en cualquier lugar que quieras dentro de las rutas, por ejemplo en una llamada de ámbito o de espacio de nombres:
namespace :articles do
concerns :commentable
end
Además de utilizar los ayudantes de enrutamiento, Rails también puede crear rutas de acceso y URL desde un array de parámetros. Por ejemplo, supongamos que tiene este conjunto de rutas:
resources :magazines do
resources :ads
end
Cuando se utiliza magazine_ad_path
, puede pasarle instancias de Magazine y Ad en lugar de los ID
numéricos:
<%= link_to 'Ad details', magazine_ad_path(@magazine, @ad) %>
También puede utilizar url_for
con un conjunto de objetos y Rails determinará automáticamente la ruta que desea:
<%= link_to 'Ad details', url_for([@magazine, @ad]) %>
En este caso, Rails verá que @magazine
es una revista y @ad
es un anuncio y por lo tanto utilizará el ayudante de magazine_ad_path
. En helpers como link_to
, puede especificar sólo el objeto en lugar de la llamada completa url_for
:
<%= link_to 'Ad details', [@magazine, @ad] %>
Si quisieras linkearte a una sola revista:
<%= link_to 'Magazine details', @magazine %>
Para otras acciones, sólo tiene que insertar el nombre de la acción como el primer elemento del array:
<%= link_to 'Edit Ad', [:edit, @magazine, @ad] %>
Esto le permite tratar instancias de sus modelos como URLs, y es una ventaja clave para usar el estilo resource.
No está limitado a las siete rutas que el enrutamiento RESTful
crea de forma predeterminada. Si lo desea, puede agregar rutas adicionales que se aplican a la colección o miembros individuales de la colección.
Para agregar una ruta member, simplemente agregue un bloque member en el bloque de recursos:
resources :photos do
member do
get 'preview'
end
end
Esto reconocerá /photos/1/preview
con GET
y enruta a la acción preview
de PhotosController
, con el valor de id
de recurso pasado en params[:id]
. También creará los ayudantes preview_photo_url
y preview_photo_path
.
Dentro del bloque de rutas member, cada nombre de ruta especifica que el verbo HTTP será reconocido. Puedes usar aquí get
, patch
, put
, post
o delete
. Si no tiene múltiples rutas member
, también puede pasar :on
a una ruta, eliminando el bloque:
resources :photos do
get 'preview', on: :member
end
Puede omitir la opción :on
, esto creará la misma ruta member
, excepto que el valor id
del recurso estará disponible en params[:photo_id]
en lugar de params[:id]
.
Para agregar una ruta a la colección:
resources :photos do
collection do
get 'search'
end
end
Esto permitirá a Rails reconocer rutas como /photos/search
con GET
, y enruta a la acción search
de PhotosController
. También creará los helpers de búsqueda search_photos_url
y search_photos_path
.
Al igual que con las rutas member
, puede pasar :on
a una ruta:
resources :photos do
get 'search', on: :collection
end
Para agregar una acción nueva alternativa se hace mediante el acceso directo :on
resources :comments do
get 'preview', on: :new
end
Esto permitirá a Rails reconocer rutas como /comments/new/preview
con GET
, y enruta a la acción preview
de CommentsController
. También creará los auxiliares de ruta preview_new_comment_url
y preview_new_comment_path
.
Si te encuentras agregando muchas acciones adicionales a una ruta de recursos, es hora de parar y pregúntarte si estás disfrazando la presencia de otro recurso.