Rails routing conditions for a DRY routes.rb file

Stefan Natchev | 15 April 2013

Rails routing gives you the ability to set any number of scopes and constraints on your routes. These conditions must all match for the route to match. Sometimes, we want to have any one of the conditions to be true in order to match a route.

For example, we want both of these urls to route to the same actions:

  • With a path prefix http://www.example.com/api/register
  • With a subdomain http://api.example.com/register

Unfortunately, the following mapping produces a condition on both having the subdomain and path prefix.

constraints subdomain: "api" do
  scope "/api" do
    controller :api do
      post :register
    end
  end
end

That route will only match http://api.example.com/api/register. Not quite what we are looking for.

First Implementation

Initially, we would have to duplicate all of our endpoints wrapped in a subdomain constraint and a path scope:

Rails::Application.routes.draw do

  constraints subdomain: "api" do
    controller :api do
      resources :widget
      post :register
      get :help
    end
  end

  scope "/api" do
    controller :api do
      resources :widget
      post :register
      get :help
    end
  end
end

This is not a great solution since any new controller actions would require changes in two places.

A Better Way

A neat trick is to define a method inside the routes block that emits the routes. This can then be wrapped by the scope and constraints conditional.

Rails::Application.routes.draw do

  def api_endpoints
    controller :api do
      resources :widget
      post :register
      get :help
    end
  end

  constraints subdomain: "api" do
    api_endpoints
  end

  scope "/api" do
    api_endpoints
  end
end

This is a great way to group your endpoints into logical chunks that can be reused in different contexts.