Reputation: 625
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?
Upvotes: 1
Views: 1295
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