1 The Purpose of the Rails Router
El enrutador Rails reconoce las URL y las envía a la acción de un controlador o a una aplicación de Rack. También puede generar rutas y URL, evitando la necesidad de codificar cadenas en sus vistas.
1.1 Connecting URLs to Code
Cuando su aplicación Rails recibe una solicitud entrante para:
GET /patients/17
le pide al enrutador que lo haga coincidir con una acción del controlador. Si la primera ruta coincidente es:
get '/patients/:id', to: 'patients#show'
la solicitud se envía a la acción show
del controlador de patients
con {id: '17'}
en params
.
Rails usa snake_case para los nombres de los controladores aquí, si tiene un controlador de varias palabras como MonsterTrucksController
, desea usar monster_trucks#show
, por ejemplo.
1.2 Generating Paths and URLs from Code
También puede generar rutas y URL. Si la ruta anterior se modifica para ser:
get '/patients/:id', to: 'patients#show', as: 'patient'
y su aplicación contiene este código en el controlador:
@patient = Patient.find(params[:id])
y esto en la vista correspondiente:
<%= link_to 'Patient Record', patient_path(@patient) %>
entonces el enrutador generará la ruta /pacientes/17
. Esto reduce la fragilidad de su vista y hace que su código sea más fácil de entender. Tenga en cuenta que no es necesario especificar la identificación en el asistente de ruta.
1.3 Configuring the Rails Router
Las rutas para su aplicación o motor se encuentran en el archivo config/routes.rb
y normalmente se ven así:
Rails.application.routes.draw do
resources :brands, only: [:index, :show] do
resources :products, only: [:index, :show]
end
resource :basket, only: [:show, :update, :destroy]
resolve("Basket") { route_for(:basket) }
end
Dado que este es un archivo fuente normal de Ruby, puede usar todas sus características para ayudarlo a definir sus rutas, pero tenga cuidado con los nombres de las variables, ya que pueden entrar en conflicto con los métodos DSL del enrutador.
El bloque Rails.application.routes.draw do ... end
que envuelve las definiciones de ruta es necesario para establecer el alcance del enrutador DSL y no debe eliminarse.
2 Resource Routing: the Rails Default
El enrutamiento de recursos le permite declarar rápidamente todas las rutas comunes para un controlador con recursos dado. En lugar de declarar rutas separadas para sus acciones de index
,show
, new
,edit
, create
,update
y destroy
, una ruta ingeniosa las declara en una sola línea de código.
2.1 Resources on the Web
Los navegadores solicitan páginas de Rails solicitando una URL mediante 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 mapea una serie de solicitudes relacionadas con acciones en un solo controlador.
Cuando su aplicación Rails recibe una solicitud entrante para:
DELETE /photos/17
le pide al enrutador que lo asigne a una acción de controlador. Si la primera ruta coincidente es:
resources :photos
Rails enviaría esa solicitud a la acción destroy
en el controladorphotos
con {id: '17'}
en params
.
2.2 CRUD, Verbs, and Actions
En Rails, una ruta ingeniosa proporciona un mapeo entre verbos HTTP y URL para acciones del controlador. Por convención, cada acción también se asigna a un CRUD específico operación en una base de datos. Una sola entrada en el archivo de enrutamiento, como:
resources :photos
crea siete rutas diferentes en su aplicación, todas asignadas al controlador Photos
:
HTTP Verb | Path | Controller#Action | Used for |
---|---|---|---|
GET | /photos | photos#index | display a list of all photos |
GET | /photos/new | photos#new | return an HTML form for creating a new photo |
POST | /photos | photos#create | create a new photo |
GET | /photos/:id | photos#show | display a specific photo |
GET | /photos/:id/edit | photos#edit | return an HTML form for editing a photo |
PATCH/PUT | /photos/:id | photos#update | update a specific photo |
DELETE | /photos/:id | photos#destroy | delete a specific photo |
Debido a que el enrutador usa el verbo HTTP y la URL para hacer coincidir las solicitudes entrantes, cuatro URL se asignan a siete acciones diferentes.
Las rutas de los rieles coinciden en el orden en que se especifican, por lo que si tiene un resources: photos
encima de unget 'photos/poll'
, la ruta de la acción show
para la línearesources
coincidirá antes que la línea get
. Para solucionar este problema, mueva la línea get
** encima de ** la línea resources
para que coincida primero.
2.3 Path and URL Helpers
La creación de una ruta ingeniosa también expondrá una serie de ayudantes a los controladores de su aplicación. En el caso de `resources :photos:
-
photos_path
devuelve/photos
-
new_photo_path
devuelve/photos/new
-
edit_photo_path(:id)
devuelve/photos/:id/edit
(por ejemplo,edit_photo_path(10)
por ejemplo/photos/10/edit
) -
photo_path(:id)
devuelve/photos/:id
(por ejemplo,photo_path(10)
por ejemplo/photos/10
)
Cada uno de estos ayudantes tiene un ayudante _url
correspondiente (comophotos_url
) que devuelve la misma ruta con el prefijo actual de host, puerto y ruta.
2.4 Defining Multiple Resources at the Same Time
Si necesita crear rutas para más de un recurso, puede ahorrar 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
2.5 Singular Resources
A veces, tiene un recurso que los clientes siempre buscan sin hacer referencia a una identificación. Por ejemplo, le gustaría que /profile
muestre siempre el perfil del usuario actualmente conectado. En este caso, puede usar un recurso singular para mapear /profile
(en lugar de/profile/:id
) a la acción show
:
get 'profile', to: 'users#show'
Pasar una String
a to:
esperará un formato de controller#action
. Cuando se usa un Symbol
, la opciónto:
debe reemplazarse poracción:
. Cuando se usa un String
sin un #
, la opciónto:
debe reemplazarse por controller:
:
get 'profile', action: :show, controller: 'users'
Esta ruta ingeniosa:
resource :geocoder
resolve('Geocoder') { [:geocoder] }
crea seis rutas diferentes en su aplicación, todas asignadas al controlador Geocoders
:
HTTP Verb | Path | Controller#Action | Used for |
---|---|---|---|
GET | /geocoder/new | geocoders#new | return an HTML form for creating the geocoder |
POST | /geocoder | geocoders#create | create the new geocoder |
GET | /geocoder | geocoders#show | display the one and only geocoder resource |
GET | /geocoder/edit | geocoders#edit | return an HTML form for editing the geocoder |
PATCH/PUT | /geocoder | geocoders#update | update the one and only geocoder resource |
DELETE | /geocoder | geocoders#destroy | delete the geocoder resource |
Debido a que 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 plurales. De modo que, por ejemplo, resource: photo
yresources: photos
crea rutas tanto en singular como en plural que se asignan al mismo controlador (PhotosController
).
Una ruta de recursos singular genera estas ayudantes:
-
new_geocoder_path
regresa/geocoder/new
-
edit_geocoder_path
regresa/geocoder/edit
-
geocoder_path
regresa/geocoder
Al igual que con los recursos plurales, los mismos ayudantes que terminan en _url
también incluirán el prefijo de host, puerto y ruta.
2.6 Controller Namespaces and Routing
Es posible que desee organizar grupos de controladores en un espacio de nombres. Lo más común es agrupar varios controladores administrativos en un espacio de nombres Admin::
. Colocaría 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 artcicles
y comments
. Para Admin::ArticlesController
, Rails creará:
HTTP Verb | Path | Controller#Action | Named Route 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 usar:
scope module: 'admin' do
resources :articles, :comments
end
o, para un solo caso:
resources :articles, module: 'admin'
Si desea enrutar /admin/articles
a ArticlesController
(sin el prefijo del módulo Admin::
), puede usar:
scope '/admin' do
resources :articles, :comments
end
o, para un solo caso:
resources :articles, path: '/admin/articles'
En cada uno de estos casos, las rutas nombradas siguen siendo las mismas que si no usara scope
. En el último caso, las siguientes rutas se asignan a ArticlesController
:
HTTP Verb | Path | Controller#Action | Named Route 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 namespace
, puede especificar una ruta de controlador absoluta, por ejemplo: get '/foo', to: '/foo#index'
.
2.7 Nested Resources
Es común tener recursos que lógicamente son 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 le permiten capturar esta relación en su ruta. En este caso, podría incluir esta declaración de ruta:
resources :magazines do
resources :ads
end
Además de las rutas para revistas, esta declaración también enrutará anuncios a un AdsController
. Las URL de los anuncios requieren una revista:
HTTP Verb | Path | Controller#Action | Used for |
---|---|---|---|
GET | /magazines/:magazine_id/ads | ads#index | display a list of all ads for a specific magazine |
GET | /magazines/:magazine_id/ads/new | ads#new | return an HTML form for creating a new ad belonging to a specific magazine |
POST | /magazines/:magazine_id/ads | ads#create | create a new ad belonging to a specific magazine |
GET | /magazines/:magazine_id/ads/:id | ads#show | display a specific ad belonging to a specific magazine |
GET | /magazines/:magazine_id/ads/:id/edit | ads#edit | return an HTML form for editing an ad belonging to a specific magazine |
PATCH/PUT | /magazines/:magazine_id/ads/:id | ads#update | update a specific ad belonging to a specific magazine |
DELETE | /magazines/:magazine_id/ads/:id | ads#destroy | delete a specific ad belonging to a specific magazine |
Esto también creará ayudantes de enrutamiento como magazine_ads_url
y edit_magazine_ad_path
. Estos ayudantes toman una instancia de Magazine como primer parámetro (magazine_ads_url(@magazine)
).
2.7.1 Limits to Nesting
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 como:
/publishers/1/magazines/2/photos/3
The corresponding route helper would be publisher_magazine_photo_url
, requiring you to specify objects at all three levels. Indeed, this situation is confusing enough that a popular article by Jamis Buck proposes a rule of thumb for good Rails design:
_Los recursos nunca deben anidarse a más de 1 nivel de profundidad.
2.7.2 Shallow Nesting
Una forma de evitar la anidación profunda (como se recomendó anteriormente) es generar las acciones de colección dentro del ámbito del padre, para tener una idea de la jerarquía, pero no anidar las acciones de los miembros. En otras palabras, para construir solo rutas con la mínima cantidad de información para identificar de forma única el recurso, así:
resources :articles do
resources :comments, only: [:index, :new, :create]
end
resources :comments, only: [:show, :edit, :update, :destroy]
Esta idea logra un equilibrio entre rutas descriptivas y anidación profunda. Existe una sintaxis abreviada para lograr precisamente 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 principal, en cuyo caso todos los recursos anidados serán poco profundos:
resources :articles, shallow: true do
resources :comments
resources :quotes
resources :drafts
end
El método shallow
del DSL crea un alcance dentro del cual cada anidamiento 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 para scope
para personalizar rutas poco profundas. :shallow_path
antepone las rutas de miembros 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 para él:
HTTP Verb | Path | Controller#Action | Named Route 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
agrega el parámetro especificado a los ayudantes de ruta nombrados:
scope shallow_prefix: "sekret" do
resources :articles do
resources :comments, shallow: true
end
end
El recurso de comentarios aquí tendrá las siguientes rutas generadas para él:
HTTP Verb | Path | Controller#Action | Named Route 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 |
2.8 Routing Concerns
Las preocupaciones sobre el enrutamiento le permiten declarar rutas comunes que se pueden reutilizar dentro de otros recursos y rutas. Para definir una preocupación:
concern :commentable do
resources :comments
end
concern :image_attachable do
resources :images, only: :index
end
Estas preocupaciones se pueden utilizar en los 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 puede usarlos en cualquier lugar que desee dentro de las rutas, por ejemplo, en una llamada a scope
o namespace
:
namespace :articles do
concerns :commentable
end
2.9 Creating Paths and URLs from Objects
Además de utilizar los ayudantes de enrutamiento, Rails también puede crear rutas y URL a partir de una serie de parámetros. Por ejemplo, suponga que tiene este conjunto de rutas:
resources :magazines do
resources :ads
end
Al usar magazine_ad_path
, puede pasar instancias de Magazine
y Ad
en lugar de los ID numéricos:
<%= link_to 'Ad details', magazine_ad_path(@magazine, @ad) %>
También puede usar url_for
con un conjunto de objetos, y Rails determinará automáticamente qué ruta desea:
<%= link_to 'Ad details', url_for([@magazine, @ad]) %>
En este caso, Rails verá que @magazine
es unaMagazine
y @ad
es un Ad
y, por lo tanto, usará el ayudante magazine_ad_path
. En ayudantes como link_to
, puedes especificar solo el objeto en lugar de la llamada completa a url_for
:
<%= link_to 'Ad details', [@magazine, @ad] %>
Si desea vincular solo a una revista:
<%= link_to 'Magazine details', @magazine %>
Para otras acciones, solo necesita insertar el nombre de la acción como el primer elemento de la matriz:
<%= link_to 'Edit Ad', [:edit, @magazine, @ad] %>
Esto le permite tratar las instancias de sus modelos como URL y es una ventaja clave para usar el estilo ingenioso.
2.10 Adding More RESTful Actions
No está limitado a las siete rutas que el enrutamiento RESTful crea de forma predeterminada. Si lo desea, puede agregar rutas adicionales que se apliquen a la colección o miembros individuales de la colección.
2.10.1 Adding Member Routes
Para agregar una ruta miembro, 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 se dirigirá 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 miembro, cada nombre de ruta especifica el verbo HTTP que
será reconocido. Puede usar get
,patch
, put
,post
o delete
aquí
. Si no tiene varias rutas member
, también puede pasar :on
a un
ruta, eliminando el bloque:
resources :photos do
get 'preview', on: :member
end
Puede omitir la opción :on
, esto creará la misma ruta de miembro excepto que el valor de id del recurso estará disponible en params[:photo_id]
en lugar deparams [: id]
. Los asistentes de ruta también cambiarán de nombre de preview_photo_url
y preview_photo_path
a photo_preview_url
y photo_preview_path
.
2.10.2 Adding Collection Routes
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 dirigirse a la acción de search
de PhotosController
. También creará los ayudantes de ruta search_photos_url
y search_photos_path
.
Al igual que con las rutas de miembros, puede pasar :on
a una ruta:
resources :photos do
get 'search', on: :collection
end
Si está definiendo rutas de recursos adicionales con un símbolo como primer argumento posicional, tenga en cuenta que no es equivalente a usar una cadena. Los símbolos infieren acciones del controlador mientras que las cadenas infieren rutas.
2.10.3 Adding Routes for Additional New Actions
Para agregar una nueva acción alternativa usando el atajo :on
:
resources :comments do
get 'preview', on: :new
end
Esto permitirá a Rails reconocer rutas como /comments/new/preview
con GET, y dirigirse a la acción de preview
de CommentsController
. También creará los ayudantes de ruta preview_new_comment_url
y preview_new_comment_path
.
si se encuentra agregando muchas acciones adicionales a una ruta ingeniosa, es hora de detenerse y preguntarse si está ocultando la presencia de otro recurso.
3 Non-Resourceful Routes
Además del enrutamiento de recursos, Rails tiene un poderoso soporte para enrutar URL arbitrarias a acciones. Aquí, no obtiene grupos de rutas generados automáticamente por enrutamiento ingenioso. En su lugar, configura cada ruta por separado dentro de su aplicación.
Si bien generalmente debe utilizar un enrutamiento ingenioso, todavía hay muchos lugares donde el enrutamiento más simple es más apropiado. No es necesario tratar de calzar hasta la última pieza de su aplicación en un marco ingenioso si no es una buena opción.
En particular, el enrutamiento simple hace que sea muy fácil asignar URL heredadas a nuevas acciones de Rails.
3.1 Bound Parameters
Cuando configuras una ruta regular, proporcionas una serie de símbolos que Rails asigna a partes de una solicitud HTTP entrante. Por ejemplo, considere esta ruta:
get 'photos(/:id)', to: 'photos#display'
Si una solicitud entrante de /photos/1
es procesada por esta ruta (porque no ha coincidido con ninguna ruta anterior en el archivo), el resultado será invocar la acción display
del PhotosController
, y para que el parámetro final "1"
esté disponible como params[:id]
. Esta ruta también enrutará la solicitud entrante de /photos
aPhotosController#display
, ya que :id
es un parámetro opcional, indicado entre paréntesis.
3.2 Dynamic Segments
Puede configurar tantos segmentos dinámicos dentro de una ruta regular como desee. Cualquier segmento estará disponible para la acción como parte de "params". Si configura esta ruta:
get 'photos/:id/:user_id', to: 'photos#show'
Se enviará una ruta entrante de /photos/1/2
a la acción show
del PhotosController
. params[:id]
será "1"
, y params[:user_id]
será "2"
.
SUGERENCIA: De forma predeterminada, los segmentos dinámicos no aceptan puntos; esto se debe a que el punto se usa como separador para rutas formateadas. Si necesita usar un punto dentro de un segmento dinámico, agregue una restricción que anule esto, por ejemplo, id: /[^\/]+/
permite cualquier cosa excepto una barra.
3.3 Static Segments
Puede especificar segmentos estáticos al crear una ruta sin anteponer dos puntos a un fragmento:
get 'photos/:id/with_user/:user_id', to: 'photos#show'
Esta ruta respondería a rutas como /photos/1/with_user/2
. En este caso, params
sería { controller: 'photos', action: 'show', id: '1', user_id: '2' }
.
3.4 The Query String
Los params
también incluirán cualquier parámetro de la cadena de consulta. Por ejemplo, con esta ruta:
get 'photos/:id', to: 'photos#show'
Una ruta entrante de /photos/1? User_id=2
será enviada a la acción show
del controlador Photos
. params
será{ controller: 'photos', action: 'show', id: '1', user_id: '2' }
.
3.5 Defining Defaults
Puede definir valores predeterminados en una ruta proporcionando un hash para la opción :defaults
. Esto incluso se aplica a los parámetros que no especifica como segmentos dinámicos. Por ejemplo:
get 'photos/:id', to: 'photos#show', defaults: { format: 'jpg' }
Rails haría coincidir photos/12
con la acciónshow
de PhotosController
, y establecería params[:format]
en "jpg"
.
También puede utilizar defaults
en un formato de bloque para definir los valores predeterminados para varios elementos:
defaults format: :json do
resources :photos
end
No puede anular los valores predeterminados mediante los parámetros de consulta, esto es por razones de seguridad. Los únicos valores predeterminados que se pueden anular son los segmentos dinámicos mediante sustitución en la ruta de la URL.
3.6 Naming Routes
Puede especificar un nombre para cualquier ruta usando la opción :as
:
get 'exit', to: 'sessions#destroy', as: :logout
Esto creará logout_path
y logout_url
como ayudantes de ruta con nombre en su aplicación. Llamar a logout_path
devolverá /exit
También puede usar esto para anular los métodos de enrutamiento definidos por los recursos, como este:
get ':username', to: 'users#show', as: :user
Esto definirá un método user_path
que estará disponible en controladores, ayudantes y vistas que irán a una ruta como /bob
. Dentro de la acción show
de UsersController
, params [:username]
contendrá el nombre de usuario del usuario. Cambie :username
en la definición de ruta si no desea que el nombre de su parámetro sea :username
.
3.7 HTTP Verb Constraints
En general, debes usar los métodos get
,post
, put
, patch
y delete
para restringir una ruta a un verbo en particular. Puede usar el método match
con la opción :via
para hacer coincidir varios verbos a la vez:
match 'photos', to: 'photos#show', via: [:get, :post]
Puede hacer coincidir todos los verbos con una ruta en particular usando via: :all
:
match 'photos', to: 'photos#show', via: :all
Enrutar las solicitudes GET
y POST
a una sola acción tiene implicaciones de seguridad. En general, debe evitar enrutar todos los verbos a una acción a menos que tenga una buena razón para hacerlo.
GET
en Rails no buscará el token CSRF. Nunca debe escribir en la base de datos desde solicitudes GET
. Para obtener más información, consulte la security guide sobre las contramedidas CSRF.
3.8 Segment Constraints
Puede utilizar la opción :constraints
para imponer un formato para un segmento dinámico:
get 'photos/:id', to: 'photos#show', constraints: { id: /[A-Z]\d{5}/ }
Esta ruta coincidiría con rutas como /photos/A12345
, pero no con /photos/893
. Puede expresar más sucintamente la misma ruta de esta manera:
get 'photos/:id', to: 'photos#show', id: /[A-Z]\d{5}/
:constraints
toma expresiones regulares con la restricción de que los anclajes de expresiones regulares no se pueden usar. Por ejemplo, la siguiente ruta no funcionará:
get '/:id', to: 'articles#show', constraints: { id: /^\d/ }
Sin embargo, tenga en cuenta que no es necesario utilizar anclajes porque todas las rutas están ancladas al principio.
Por ejemplo, las siguientes rutas permitirían articles
con valores to_param
como 1-hello-world
que siempre comienzan con un número y users
con valores to_param
como david
que nunca comienzan con un número para compartir el espacio de nombres raíz:
get '/:id', to: 'articles#show', constraints: { id: /\d.+/ }
get '/:username', to: 'users#show'
3.9 Request-Based Constraints
También puede restringir una ruta en función de cualquier método en el Request object que devuelve una String
.
Especifica una restricción basada en solicitud de la misma manera que especifica una restricción de segmento:
get 'photos', to: 'photos#index', constraints: { subdomain: 'admin' }
También puede especificar restricciones en forma de bloque:
namespace :admin do
constraints subdomain: 'admin' do
resources :photos
end
end
Las restricciones de solicitud funcionan llamando a un método en el Request object con el mismo nombre que la clave hash y luego compare el valor de retorno con el valor hash. Por lo tanto, los valores de restricción deben coincidir con el tipo de retorno del método de objeto Request correspondiente. Por ejemplo: constraints: {subdomain: 'api'}
coincidirá con un subdominio api
como se esperaba, sin embargo, usando un símboloconstraints: {subdominio: :api}
no lo hará, porque request.subdomain
devuelve 'api'
como una cadena.
Hay una excepción para la restricción format
: si bien es un método en el objeto Request, también es un parámetro opcional implícito en cada ruta. Las restricciones de segmento tienen prioridad y la restricción de "formato" solo se aplica como tal cuando se aplica mediante un hash. Por ejemplo, get 'foo', las restricciones: {formato: 'json'}
coincidirá con GET/foo
porque el formato es opcional por defecto. Sin embargo, puede use a lambda como en get 'foo', constraints: lambda { |req| req.format == :json }
y la ruta solo coincidirá con solicitudes JSON explícitas.
3.10 Advanced Constraints
Si tiene una restricción más avanzada, puede proporcionar un objeto que responda a "¿coincidencias?" Que Rails debería usar. Supongamos que desea enrutar a todos los usuarios de una lista restringida al RestrictedListController
. Podrías hacerlo:
class RestrictedListConstraint
def initialize
@ips = RestrictedList.retrieve_ips
end
def matches?(request)
@ips.include?(request.remote_ip)
end
end
Rails.application.routes.draw do
get '*path', to: 'restricted_list#index',
constraints: RestrictedListConstraint.new
end
También puede especificar restricciones como lambda:
Rails.application.routes.draw do
get '*path', to: 'restricted_list#index',
constraints: lambda { |request| RestrictedList.retrieve_ips.include?(request.remote_ip) }
end
Tanto el método matches?
Como el lambda obtienen el objeto request
como argumento
3.11 Route Globbing and Wildcard Segments
El globbing de ruta es una forma de especificar que un parámetro particular debe coincidir con todas las partes restantes de una ruta. Por ejemplo:
get 'photos/*other', to: 'photos#unknown'
Esta ruta coincidiría con photos/12
o /photos/long/path/to/12
, estableciendo params[:other]
en "12"
o "long/path/to/12"
. Los fragmentos precedidos de una estrella se denominan "segmentos comodín".
Los segmentos comodín pueden aparecer en cualquier lugar de una ruta. Por ejemplo:
get 'books/*section/:title', to: 'books#show'
coincidiría books/some/section/last-words-a-memoir
con params[:section]
es igual a 'some/section'
, y params[:title]
es igual a 'last-words-a-memoir'
.
Técnicamente, una ruta puede tener incluso más de un segmento comodín. El comparador asigna segmentos a parámetros de forma intuitiva. Por ejemplo:
get '*a/foo/*b', to: 'test#index'
coincidiría zoo/woo/foo/bar/baz
con params[:a]
es igual a 'zoo/woo'
, y params[:b]
es igual a 'bar/baz'
.
Al solicitar '/foo/bar.json'
, sus params[:pages]
serán iguales a 'foo/bar'
con el formato de solicitud JSON. Si desea recuperar el antiguo comportamiento de 3.0.x, puede proporcionar format: false
como este:
get '*pages', to: 'pages#show', format: false
Si desea que el segmento de formato sea obligatorio, por lo que no se puede omitir, puede proporcionar format: true
así:
get '*pages', to: 'pages#show', format: true
3.12 Redirection
Puede redirigir cualquier ruta a otra ruta usando el ayudante redirect
en su enrutador:
get '/stories', to: redirect('/articles')
También puede reutilizar segmentos dinámicos de la coincidencia en la ruta para redirigir a:
get '/stories/:name', to: redirect('/articles/%{name}')
También puede proporcionar un bloque para redirigir, que recibe los parámetros de ruta simbolizados y el objeto de solicitud:
get '/stories/:name', to: redirect { |path_params, req| "/articles/#{path_params[:name].pluralize}" }
get '/stories', to: redirect { |path_params, req| "/articles/#{req.subdomain}" }
Tenga en cuenta que la redirección predeterminada es una redirección 301 "Movido permanentemente". Tenga en cuenta que algunos navegadores web o servidores proxy almacenarán en caché este tipo de redireccionamiento, haciendo que la página anterior sea inaccesible. Puede utilizar la opción :status
para cambiar el estado de la respuesta:
get '/stories/:name', to: redirect('/articles/%{name}', status: 302)
En todos estos casos, si no proporciona el host principal (http://www.example.com
), Rails tomará esos detalles de la solicitud actual.
3.13 Routing to Rack Applications
En lugar de una cadena como 'articles#index'
, que corresponde a la acción index
en el ArticlesController
, puedes especificar cualquier Rack application como el punto final para un comparador:
match '/application.js', to: MyRackApp, via: :all
Mientras MyRackApp
responda a call
y devuelva un [status, headers, body]
, el enrutador no reconocerá la diferencia entre la aplicación Rack y una acción. Este es un uso apropiado de via: :all
, ya que querrá permitir que su aplicación Rack maneje todos los verbos como lo considere apropiado.
Para los curiosos, 'articles#index'
en realidad se expande a ArticlesController.action(:index)
, que devuelve una aplicación Rack válida.
Si especifica una aplicación Rack como el punto final para un comparador, recuerde que
la ruta no se modificará en la aplicación receptora. Con lo siguiente
route su aplicación Rack debe esperar que la ruta sea /admin
:
match '/admin', to: AdminApp, via: :all
Si prefiere que su aplicación Rack reciba solicitudes en la raíz
ruta en su lugar, use mount
:
mount AdminApp, at: '/admin'
3.14 Using root
Puede especificar a qué Rails debe enrutar '/'
con el método root
:
root to: 'pages#main'
root 'pages#main' # shortcut for the above
Debe colocar la ruta root
en la parte superior del archivo, porque es la ruta más popular y debe coincidir primero.
La ruta root
solo enruta las solicitudes GET
a la acción.
También puede utilizar la raíz dentro de espacios de nombres y ámbitos. Por ejemplo:
namespace :admin do
root to: "admin#index"
end
root to: "home#index"
3.15 Unicode Character Routes
Puede especificar rutas de caracteres Unicode directamente. Por ejemplo:
get 'こんにちは', to: 'welcome#index'
3.16 Direct Routes
Puede crear ayudantes de URL personalizados directamente. Por ejemplo:
direct :homepage do
"http://www.rubyonrails.org"
end
# >> homepage_url
# => "http://www.rubyonrails.org"
El valor de retorno del bloque debe ser un argumento válido para el método url_for
. Por lo tanto, puede pasar una URL de cadena válida, Hash, Array, una instancia de modelo activo o una clase de modelo activo.
direct :commentable do |model|
[ model, anchor: model.dom_id ]
end
direct :main do
{ controller: 'pages', action: 'index', subdomain: 'www' }
end
3.17 Using resolve
El método resolve
permite personalizar el mapeo polimórfico de modelos. Por ejemplo:
resource :basket
resolve("Basket") { [:basket] }
<%= form_with model: @basket do |form| %>
<!-- basket form -->
<% end %>
Esto generará la URL singular /basket
en lugar de la habitual /baskets/:id
.
4 Customizing Resourceful Routes
Si bien las rutas predeterminadas y los ayudantes generados por resources :articles
generalmente le servirán bien, es posible que desee personalizarlos de alguna manera. Rails le permite personalizar prácticamente cualquier parte genérica de los ayudantes ingeniosos.
4.1 Specifying a Controller to Use
La opción :controller
le permite especificar explícitamente un controlador para usar para el recurso. Por ejemplo:
resources :photos, controller: 'images'
reconocerá las rutas entrantes que comienzan con /photos
pero se dirigirán al controladorImages
:
HTTP Verb | Path | Controller#Action | Named Route Helper |
---|---|---|---|
GET | /photos | images#index | photos_path |
GET | /photos/new | images#new | new_photo_path |
POST | /photos | images#create | photos_path |
GET | /photos/:id | images#show | photo_path(:id) |
GET | /photos/:id/edit | images#edit | edit_photo_path(:id) |
PATCH/PUT | /photos/:id | images#update | photo_path(:id) |
DELETE | /photos/:id | images#destroy | photo_path(:id) |
Utilice photos_path
, new_photo_path
, etc. para generar rutas para este recurso.
Para los controladores de espacio de nombres, puede utilizar la notación de directorio. Por ejemplo:
resources :user_permissions, controller: 'admin/user_permissions'
Esto se enrutará al controlador Admin::UserPermissions
.
Solo se admite la notación de directorio. Especificando el
controlador con notación constante de Ruby (por ejemplo, controller: 'Admin::UserPermissions'
)
puede conducir a problemas de enrutamiento y resulta en
una advertencia.
4.2 Specifying Constraints
Puede usar la opción : constraints
para especificar un formato requerido en elid
implícito. Por ejemplo:
resources :photos, constraints: { id: /[A-Z][A-Z][0-9]+/ }
Esta declaración restringe el parámetro :id
para que coincida con la expresión regular proporcionada. Entonces, en este caso, el enrutador ya no coincidirá con / photos / 1
con esta ruta. En cambio, / photos / RR27
coincidiría.
Puede especificar una única restricción para aplicar a varias rutas utilizando el formulario de bloque:
constraints(id: /[A-Z][A-Z][0-9]+/) do
resources :photos
resources :accounts
end
Por supuesto, puede utilizar las restricciones más avanzadas disponibles en rutas sin recursos en este contexto.
De forma predeterminada, el parámetro :id
no acepta puntos; esto se debe a que el punto se usa como separador para rutas formateadas. Si necesita usar un punto dentro de un :id
agregue una restricción que anule esto - por ejemplo, id: /[^\/]+/
permite cualquier cosa excepto una barra.
4.3 Overriding the Named Route Helpers
La opción :as
le permite anular el nombre normal de los ayudantes de ruta nombrados. Por ejemplo:
resources :photos, as: 'images'
reconocerá las rutas entrantes que comienzan con /photos
y enrutará las solicitudes a PhotosController
, pero usará el valor de la opción :as
para nombrar los ayudantes.
HTTP Verb | Path | Controller#Action | Named Route Helper |
---|---|---|---|
GET | /photos | photos#index | images_path |
GET | /photos/new | photos#new | new_image_path |
POST | /photos | photos#create | images_path |
GET | /photos/:id | photos#show | image_path(:id) |
GET | /photos/:id/edit | photos#edit | edit_image_path(:id) |
PATCH/PUT | /photos/:id | photos#update | image_path(:id) |
DELETE | /photos/:id | photos#destroy | image_path(:id) |
4.4 Overriding the new
and edit
Segments
La opción :path_names
te permite anular los segmentos new
y edit
generados automáticamente en las rutas:
resources :photos, path_names: { new: 'make', edit: 'change' }
Esto haría que el enrutamiento reconociera rutas como:
/photos/make
/photos/1/change
Esta opción no cambia los nombres reales de las acciones. Las dos rutas mostradas aún se enrutarían a las acciones nuevo
y edit
.
Si desea cambiar esta opción de manera uniforme para todas sus rutas, puede usar un visor.
scope path_names: { new: 'make' } do
# rest of your routes
end
4.5 Prefixing the Named Route Helpers
Puede usar la opción :as
para prefijar los ayudantes de ruta con nombre que Rails genera para una ruta. Utilice esta opción para evitar colisiones de nombres entre rutas utilizando un alcance de ruta. Por ejemplo:
scope 'admin' do
resources :photos, as: 'admin_photos'
end
resources :photos
Esto proporcionará ayudantes de ruta como admin_photos_path
, new_admin_photo_path
, etc.
Para prefijar un grupo de ayudantes de ruta, use :as
con scope
:
scope 'admin', as: 'admin' do
resources :photos, :accounts
end
resources :photos, :accounts
Esto generará rutas como admin_photos_path
y admin_accounts_path
que se asignan a /admin/photos
y /admin/accounts
respectivamente.
El alcance del namespace
agregará automáticamente los prefijos : as
así como :module
y :path
.
También puede prefijar rutas con un parámetro con nombre:
scope ':username' do
resources :articles
end
Esto le proporcionará URL como /bob/articles/1
y le permitirá hacer referencia a la parte del username
de la ruta como params[:username]
en controladores, ayudantes y vistas.
4.6 Restricting the Routes Created
De forma predeterminada, Rails crea rutas para las siete acciones predeterminadas (index
, show
, new
, create
, edit
, update
, and destroy
) para cada ruta RESTful en su aplicación. Puede utilizar las opciones :only
y :except
para ajustar este comportamiento. La opción :only
le dice a Rails que cree solo las rutas especificadas:
resources :photos, only: [:index, :show]
Ahora, una solicitud GET
a /photos
tendría éxito, pero una solicitud POST
a /photos
(que normalmente se enrutaría a la acción create
) fallará.
La opción : except
especifica una ruta o lista de rutas que Rails no debería crear:
resources :photos, except: :destroy
En este caso, Rails creará todas las rutas normales excepto la ruta para destroy
(una solicitud DELETE
a /photos/:id
).
Si su aplicación tiene muchas rutas RESTful, usar :only
y :except
para generar solo las rutas que realmente necesita puede reducir el uso de memoria y acelerar el proceso de enrutamiento.
4.7 Translated Paths
Usando scope
, podemos alterar los nombres de ruta generados por resources
:
scope(path_names: { new: 'neu', edit: 'bearbeiten' }) do
resources :categories, path: 'kategorien'
end
Rails ahora crea rutas al CategoriesController
.
HTTP Verb | Path | Controller#Action | Named Route Helper |
---|---|---|---|
GET | /kategorien | categories#index | categories_path |
GET | /kategorien/neu | categories#new | new_category_path |
POST | /kategorien | categories#create | categories_path |
GET | /kategorien/:id | categories#show | category_path(:id) |
GET | /kategorien/:id/bearbeiten | categories#edit | edit_category_path(:id) |
PATCH/PUT | /kategorien/:id | categories#update | category_path(:id) |
DELETE | /kategorien/:id | categories#destroy | category_path(:id) |
4.8 Overriding the Singular Form
Si desea definir la forma singular de un recurso, debe agregar reglas adicionales al Inflector
:
ActiveSupport::Inflector.inflections do |inflect|
inflect.irregular 'tooth', 'teeth'
end
4.9 Using :as
in Nested Resources
La opción :as
anula el nombre generado automáticamente para el recurso en los ayudantes de ruta anidados. Por ejemplo:
resources :magazines do
resources :ads, as: 'periodical_ads'
end
Esto creará ayudantes de enrutamiento como magazine_periodical_ads_url
y edit_magazine_periodical_ad_path
.
4.10 Overriding Named Route Parameter
La opción :param
anula el identificador de recurso predeterminado :id
(nombre de
el dynamic segment que se utiliza para generar el
rutas). Puede acceder a ese segmento desde su controlador usando
params[<:param>]
.
resources :videos, param: :identifier
videos GET /videos(.:format) videos#index
POST /videos(.:format) videos#create
new_video GET /videos/new(.:format) videos#new
edit_video GET /videos/:identifier/edit(.:format) videos#edit
Video.find_by(identifier: params[:identifier])
Puede anular ActiveRecord::Base#to_param
de un modelo relacionado para construir
una URL:
class Video < ApplicationRecord
def to_param
identifier
end
end
video = Video.find_by(identifier: "Roman-Holiday")
edit_video_path(video) # => "/videos/Roman-Holiday/edit"
5 Breaking up very large route file into multiple small ones:
Si trabaja en una gran aplicación con miles de rutas,
Un solo archivo config/routes.rb
puede resultar engorroso y difícil de leer.
Rails ofrece una manera de dividir un archivo gigantesco único routes.rb
en varios pequeños usando la macro draw
.
# config/routes.rb
Rails.application.routes.draw do
get 'foo', to: 'foo#bar'
draw(:admin) # Will load another route file located in `config/routes/admin.rb`
end
# config/routes/admin.rb
namespace :admin do
resources :comments
end
Llamar a draw(:admin)
dentro del bloque Rails.application.routes.draw
intentará cargar una ruta
archivo que tiene el mismo nombre que el argumento dado (admin.rb
en este caso).
El archivo debe estar ubicado dentro del directorio config/routes
o cualquier subdirectorio (es decir, config/routes/admin.rb
, config/routes/external/admin.rb
).
Puede usar el DSL de enrutamiento normal dentro del archivo de enrutamiento admin.rb
, sin embargo no debe rodearlo con el bloque Rails.application.routes.draw
como lo hizo en el archivo config/routes.rb
.
5.1 When to use and not use this feature
Dibujar rutas a partir de archivos externos puede resultar muy útil para organizar un gran conjunto de rutas en varias rutas organizadas. Podría tener una ruta admin.rb
que contenga todas las rutas para el área de administración, otro archivo api.rb
para enrutar los recursos relacionados con la API, etc.
Sin embargo, no debe abusar de esta función, ya que tener demasiados archivos de ruta dificulta la detección y la comprensión. Dependiendo de la aplicación, puede ser más fácil para los desarrolladores tener un solo archivo de enrutamiento incluso si tiene algunos cientos de rutas. No debe intentar crear un nuevo archivo de enrutamiento para cada categoría (admin, api ...) a toda costa; El DSL de enrutamiento de Rails ya ofrece una forma de romper rutas de manera organizada con namespaces
y scopes
.
6 Inspecting and Testing Routes
Rails ofrece instalaciones para inspeccionar y probar sus rutas.
6.1 Listing Existing Routes
Para obtener una lista completa de las rutas disponibles en su aplicación, visite http://localhost:3000/rails/info/routes
in your browser while your server is running in the development environment. You can also execute the bin/rails routes
command in your terminal to produce the same output.
Ambos métodos enumerarán todas sus rutas, en el mismo orden en que aparecen en config/routes.rb
. Para cada ruta, verá:
- El nombre de la ruta (si corresponde)
- El verbo HTTP utilizado (si la ruta no responde a todos los verbos)
- El patrón de URL para que coincida
- Los parámetros de enrutamiento para la ruta
Por ejemplo, aquí hay una pequeña sección de la salida de bin/rails route
para una ruta RESTful:
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
También puede usar la opción --expanded
para activar el modo de formato de tabla expandido.
$ bin/rails routes --expanded
--[ Route 1 ]----------------------------------------------------
Prefix | users
Verb | GET
URI | /users(.:format)
Controller#Action | users#index
--[ Route 2 ]----------------------------------------------------
Prefix |
Verb | POST
URI | /users(.:format)
Controller#Action | users#create
--[ Route 3 ]----------------------------------------------------
Prefix | new_user
Verb | GET
URI | /users/new(.:format)
Controller#Action | users#new
--[ Route 4 ]----------------------------------------------------
Prefix | edit_user
Verb | GET
URI | /users/:id/edit(.:format)
Controller#Action | users#edit
Puede buscar en sus rutas con la opción grep: -g. Esto genera cualquier ruta que coincida parcialmente con el nombre del método auxiliar de URL, el verbo HTTP o la ruta URL.
$ bin/rails routes -g new_comment
$ bin/rails routes -g POST
$ bin/rails routes -g admin
Si solo desea ver las rutas que se asignan a un controlador específico, existe la opción -c.
$ bin/rails routes -c users
$ bin/rails routes -c admin/users
$ bin/rails routes -c Comments
$ bin/rails routes -c Articles::CommentsController
Encontrará que la salida de bin/rails route
es mucho más legible si amplía la ventana de su terminal hasta que las líneas de salida no se ajustan.
6.2 Testing Routes
Las rutas deben incluirse en su estrategia de prueba (al igual que el resto de su aplicación). Rails ofrece tres built-in assertions diseñadas para simplificar las rutas de prueba:
assert_generates
assert_recognizes
assert_routing
6.2.1 The assert_generates
Assertion
assert_generates
afirma que un conjunto particular de opciones genera una ruta particular y se puede usar con rutas predeterminadas o rutas personalizadas. Por ejemplo:
assert_generates '/photos/1', { controller: 'photos', action: 'show', id: '1' }
assert_generates '/about', controller: 'pages', action: 'about'
6.2.2 The assert_recognizes
Assertion
assert_recognizes
es el inverso de assert_generates
. Afirma que se reconoce una ruta determinada y la enruta a un lugar particular en su aplicación. Por ejemplo:
assert_recognizes({ controller: 'photos', action: 'show', id: '1' }, '/photos/1')
You can supply a :method
argument to specify the HTTP verb:
assert_recognizes({ controller: 'photos', action: 'create' }, { path: 'photos', method: :post })
6.2.3 The assert_routing
Assertion
La aserción assert_routing
verifica la ruta en ambos sentidos: prueba que la ruta genera las opciones y que las opciones generan la ruta. Por lo tanto, combina las funciones de assert_generates
y assert_recognizes
:
assert_routing({ path: 'photos', method: :post }, { controller: 'photos', action: 'create' })
Comentarios Sobre el Contenido
Las guías de rieles se administran y publican en latinadeveloper/railsguides.es en GitHub.
Si lee esta guía y encuentra algún texto o código incorrecto que le interese, no dude en enviar una solicitud de extracción en el repositorio anterior. Consulte el archivo README en GitHub para saber cómo enviar una solicitud de extracción. Please contribute if you see any typos or factual errors.