Ruby on Rails: Advanced routing

Shreyansh GuptaShreyansh Gupta
3 min read

In this article, we’ll cover some advance configurations that Ruby on Rails provides us for routing.

Routing concerns

Concerns allow us to create reusable routes that can be used inside other resources and routes.

Rails.application.routes.draw do
  concern :accountable do
    resources :accounts
  end

  concern :profilable do
    resources :profiles
  end

  resources :users, concerns: :accountable
  resources :customers, concerns: [:accountable, :profilable]

  # The above code is equivalent to the following

  resources :users do
    resources :accounts
  end

  resources :customers do
    resources :accounts
    resources :profiles
  end
end

Optional parameters

We can have optional parameters in the URLs. Such URLs can be defined as shown below.

Rails.application.routes.draw do
  # Matches both - /users/1 and /users
  get "/users(/:id)", to: "users#show"
end

Defining defaults

We can specify default values in a route as shown below.

Rails.application.routes.draw do
  # Method 1
  get "/users/:id", to: "users#show", defaults: { format: :json }

  # Method 2
  defaults format: :json do
    get "/users/:id", to: "users#show"
  end
end

Segment Constraints

We can enforce a format constraint for a dynamic segment within a route as shown below. A dynamic segment would be the dynamic part of the URL such as :id and :username in the example shown below.

Rails.application.routes.draw do
  # Only a URL with a 5 digit ID is matched to this route.
  get "/users/:id", to: "users#show", constraints: { id: /\d{5}/ }
  # Only a URL with a 5 alphabet uppercase username is matched to this route.
  get "/users/:username", to: "users#show", constraints: { username: /[A-Z]{5}/ }
end

Request based constraints

We can also add a constraint based on any property on the Request object that returns a string.

Rails.application.routes.draw do
  get "/users/:id", to: "users#show", constraints: { subdomain: "admin" }
  # OR
  get "/users/:id", to: "users#show", constraints: lambda { |req| req.subdomain == "admin" }
end

Note that to apply a constraint on the format of the request (req.format), we must pass in a lambda. A format constraint specified using a Hash will also match requests where no format is specified. This is because format is an optional parameter on every request. So to only match JSON requests, we must pass in the following -

..., constraints: lambda { |req| req.format == :json }

We can also apply more advanced constraints as demonstrated in the Rails guide.

Customizing the routes

We can specify the controller to be used for generating the routes as shown below.

Rails.application.routes.draw do
  # UsersController
  resources :users, controller: "users"
  # Admin::UsersController
  resources :users, controller: "admin/users"
end

We can also update the param name for the generated routes.

Rails.application.routes.draw do
  # /users/:identifier
  resources :users, param: :identifier
end

Breaking up a large routes file

To break up a large routes file, we can use the draw method. It will search for a filename corresponding to the param passed within the /config/routes/* directory and sub-directories.

# /config/routes.rb
Rails.application.routes.draw do
  draw(:admin)
end

# /config/routes/admin.rb
namespace :admin do
  # admin routes ...
end

CLI

Rails also has a powerful CLI utility that allows us to view the routes within the console. Check out the Rails guides for usage.

References

To dig deeper, check out the routing section of Rails guides.

0
Subscribe to my newsletter

Read articles from Shreyansh Gupta directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Shreyansh Gupta
Shreyansh Gupta