Dedy Puji
Dedy Puji

Reputation: 625

Explain about Route Constraint Rails

I want to constraint rails to have multiple version of api routes. Then i read a tutorial using constraint class.

route.rb

require 'api_constraints'

Rails.application.routes.draw do
  namespace :api, defaults: { format: :json } do
    scope module: :v1, constraints: ApiConstraints.new(version: 1, default: true) do
      resources :users, only: [:create, :show]
    end

    scope module: :v2, constraints: ApiConstraints.new(version: 2) do
      resources :users, only: [:create, :show]
    end
  end
end

api_constraint

class ApiConstraints
  def initialize(options)
    @version = options[:version]
    @default = options[:default]
  end

  def matches?(req)
    req.headers['Accept'].include?("application/vnd.shop.becker.v#{@version}") || @default
  end
end

And i have multiple users_controller on different module folder

users_controller.rb (module v1)

module Api
  module V1
    class UsersController < ApplicationController
      def show
        render json: {hello: 'false'}
      end
    end
  end
end

users_controller.rb (module v2)

module Api
  module V2
    class UsersController < ApplicationController
      def show
        render json: { hello: 'true'}
      end
    end
  end
end

my default version is 1, (because default is true in route file), then i expect if i dont send header 'Accept' then it will call my users_controller module V1 and my result is fine.

but if send using header 'Accept' equal to application/vnd.shop.becker.v2. It still call my users_controller.rb in module v1.

Can somebody help me to explain how constraint and the class ApiConstraints work on routes.rb? and why if i send Accept to equal application/vnd.shop.becker.v2 is still call my module v1?

enter image description here

Upvotes: 1

Views: 1295

Answers (1)

David
David

Reputation: 455

You set up correctly everything fine, just have to change the order of the v1 and v2 blocks.

Rails.application.routes.draw do
  namespace :api, defaults: { format: :json } do
    scope module: :v2, constraints: ApiConstraints.new(version: 2) do
      resources :users, only: [:create, :show]
    end

    scope module: :v1, constraints: ApiConstraints.new(version: 1, default: true) do
      resources :users, only: [:create, :show]
    end
  end
end

The problem is related with the order of the routes and the constraints together. With the default flag true, v1 constraint always matches the request, so it tries to match the routes defined in the v1 block and if there is any then the request is routed there. That's why setting the v2 header landed in the v1 users controller.

Upvotes: 2

Related Questions