To understand the Rails Router API, it's essential to learn the match
method, which forms the core of the Router DSL. All helper methods like get
and post
use match
under the hood. Additionally, most of the shorthand syntax such as scopes
and constraints
use the options provided by match
behind the scenes.
In its essence, the match
method matches a URL pattern to one or more routes. Here's the basic API of the match
method, which is defined inside the Mapper
class.
# actionpack/lib/action_dispatch/routing/mapper.rb
def match(path, options = nil)
end
The first parameter path
tells the router what URL pattern you wish to match. It can be either a String or a Hash.
- The string version specifies the URL pattern, e.g.
/posts/publish/:id
- The hash matches the URL pattern to a
controller#action
pair, like"/posts/1" => "posts#show"
.
The second parameter options
is a hash containing additional data required by the router to decide where it should redirect this request. You can also pass any custom data, which gets passed to the params
hash accessible in the controllers.
Here are some usage examples of the match
method.
match 'photos/:id' => 'photos#show', via: :get
match 'photos/:id', to: 'photos#show', via: :get
match 'photos/:id', controller: 'photos', action: 'show', via: :get
All the three routes above do the same thing, dispatch the HTTP GET request at /photos/:id
to the show
action method on the PhotosController
class.
In its simplest and most explicit form, you supply a string URL pattern along with the name of the controller and the action via options.
match "photos/:id", controller: "photos", action: "show", via: :get
If you don't want to pass them separately, a string in the form of controller#action
is also allowed with the :to
option.
match "photos/:id", to: "photos#show", via: :get
Finally, the hash version simplifies this even further:
match "photos/:id" => "photos#show", via: :get
Matching Multiple HTTP Verbs
As we'll learn later, you'll often use shortcuts like get
and post
instead of directly using the match
method. However, match
comes in handy when you want to match a route for multiple HTTP verbs.
match "photos/:id", to: "photos#handle", via: [:get, :post]
Note: the :via
option is mandatory for security-related reasons. If you don't pass it, the router raises an ArgumentError
.
Options Available to Match Method
The options
hash helps the router identify the action to which it should pass the incoming request. Here are some of the important options that you can provide to the match
method.
params
hash, which you can access in the controllers, using the params
hash.:controller
and :action
The controller
and action
keys are used together. They specify the name of the controller and the action you want to route this request to.
match "home", controller: "application", action: "home", via: :get
:to
If you don't want to pass the controller and action separately, the :to
option lets you pass the controller#action
as a string.
match "home", to: "application#home", via: :get
Note: You're not restricted to using only controller actions for incoming requests. The :to
option also allows you to point to a Rack endpoint, i.e. any object that responds to a call
method. This is really useful for quickly testing routes without creating new controllers, actions, and views.
match "path", to: -> (env) { [200, {}, ["Success!"]] }, via: :get
match "path", to: RackApp, via: :get
To learn more about forwarding a request to a Rack application, check out the following post:
via
Lists the HTTP verb for this route. You have to pass at least one HTTP verb, for security-related reasons.
match 'path', to: 'controller#action', via: :get
match 'path', to: 'controller#action', via: [:get, :post]
match 'path', to: 'controller#action', via: :all
Alternatively, you can use the shorthand helper methods such as get
, post
, and so on which implicitly provide the HTTP verb.
module
Use this if you want namespaced controllers. The following route directs the request with /posts
URL to Blog::PostsController#index
action.
match '/posts', module: 'blog', controller: 'posts', action: 'index', via: :get
as
This option provides the name for the generated URL helpers. The following route creates the helpers named blog_path
and blog_url
.
match 'posts', to: 'posts#index', as: 'blog', via: :get
To learn more about named routes, check out the following post:
constraints
If you want to put more restrictions on what URL patterns a route should match, provide this option with a hash of regular expressions. It can also take an object responding to matches?
.
match 'path/:id', constraints: { id: /[A-Z]\d{5}/ }, via: :get
defaults
Sets the default value for a parameter. The following route will set the value of params[:author]
to 'Akshay'.
match '/courses/publish(/:author)', to: 'courses#publish', defaults: { author: 'Akshay' }, via: :get
For a complete list of options, check out the API docs for the match
method. Most of the routing methods you'll use are just shorthands using various combinations of the match
method's options.
param
In the resourceful routes, :id
is the name of the dynamic segment used to generate the routes. The param
option overrides this default resource identifier. You can access the value of this dynamic segment using param[<:param>]
.
resources :posts, param: :slug
The posts
resource will contain :slug
as the dynamic segment, e.g. posts/:slug
.
To construct the URL, override the ActiveRecord::Base#to_param
method.
class Post < ApplicationRecord
def to_param
title.parameterize
end
end
post = Post.find_by(title: "Ruby on Rails")
post_path(post) # "/posts/ruby-on-rails"
That's a wrap. I hope you found this article helpful and you learned something new.
As always, if you have any questions or feedback, didn't understand something, or found a mistake, please leave a comment below or send me an email. I reply to all emails I get from developers, and I look forward to hearing from you.
If you'd like to receive future articles directly in your email, please subscribe to my blog. Your email is respected, never shared, rented, sold or spammed. If you're already a subscriber, thank you.