Understanding the Match Method in Rails Router

In this post, we will learn about the match method, which forms the core of the Rails router. We'll also explore how the match method works behind the scenes. Once you really understand the match method and its options, the rest of the routing methods and shorthands become very easy to understand.

5 min read

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.

🤔
Keep in mind: Any custom data that doesn't match the existing option gets passed as-is to the 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:

How to Route an Incoming URL to a Rack Application in Rails
The Rails router can dispatch an HTTP request to a Rack endpoint, either in your application or within a gem. This is useful when you want to provide a well-isolated web UI or front-end to the users of your gem. In this post, we’ll learn why you may want to do this, how it works, and how to do it.

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:

Understanding Named Routes in Ruby on Rails
This post explains named routes in Rails. We’ll learn how you can name a route, and how you can use the named helper methods to generate URLs corresponding to the route.

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.