Nested Routes Files Rails - ruby-on-rails

I have crafted some nested routes files using the below in my config/initializers:
class ActionDispatch::Routing::Mapper
def draw(routes_name, sub_path=nil)
if sub_path.present?
instance_eval(File.read(Rails.root.join("config/routes/#{sub_path}/#{routes_name}.rb")))
else
instance_eval(File.read(Rails.root.join("config/routes/#{routes_name}.rb")))
end
end
end
However, when used like this in the routes file:
Rails.application.routes.draw do
scope :api do
["v1"].map { |version| draw :base, "api/" + version }
end
end
The routes there do not appear nested as api/users etc. Not sure why the scope is getting ignored there.
EDIT: More Detailed Example
routes.rb
namespace :api, defaults: { format: :json } do
Rails.application.routes.draw do
["v1"].map { |version| draw :base, "api/" + version }
end
end
base.rb
namespace :v1 do
Rails.application.routes.draw do
[:identity].map { |path| draw path, "api/v1"}
end
end
identity.rb
Rails.application.routes.draw do
# Ensures proper namespace when fetching controllers, but does not add to path for routes
scope module: :identity do
namespace :users do
put '/', action: :update
patch '/', action: :update
get 'user_from_token', action: :update
end
end
end

Rails.application.routes.draw was redundant
# config/routes.rb
Rails.application.routes.draw do
scope :api do
["v1"].map { |version| draw :base, "api/" + version }
end
end
# config/routes/api/v1/base.rb
namespace :v1 do
# Rails.application.routes.draw do
[:identity].map { |path| draw path, "api/v1"}
# end
end
# config/routes/api/v1/identity.rb
# Rails.application.routes.draw do
# Ensures proper namespace when fetching controllers, but does not add to path for routes
scope module: :identity do
namespace :users do
put '/', action: :update
patch '/', action: :update
get 'user_from_token', action: :update
end
end
# end
$ rake routes
Prefix Verb URI Pattern Controller#Action
v1_users PUT /api/v1/users(.:format) v1/identity/users#update
PATCH /api/v1/users(.:format) v1/identity/users#update
v1_users_user_from_token GET /api/v1/users/user_from_token(.:format) v1/identity/users#update
rails_service_blob GET /rails/active_storage/blobs/:signed_id/*filename(.:format) active_storage/blobs#show
rails_blob_representation GET /rails/active_storage/representations/:signed_blob_id/:variation_key/*filename(.:format) active_storage/representations#show
rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show
update_rails_disk_service PUT /rails/active_storage/disk/:encoded_token(.:format) active_storage/disk#update
rails_direct_uploads POST /rails/active_storage/direct_uploads(.:format) active_storage/direct_uploads#create

Related

Rails new path in nested routes

I've got controller below controller:
module Api
module V1
module Account
class PasswordsController < Devise::PasswordsController
respond_to :json
def create
# some code
end
def update
# some code
end
def is_token_valid
::Account.find_by(reset_password_token: params[:token])
end
end
end
end
end
I want to setup an endpoint for front-end dev where he will check if reset_password_token exist in DB (devise here). I don't know how to made a path like: /api/v1/account/password/is_token_valid
My routes:
namespace :api, defaults: { format: :json } do
namespace :v1 do
namespace :account do
devise_for :accounts, singular: 'account', path: '', controllers: {
sessions: 'api/v1/account/sessions',
registrations: 'api/v1/account/registrations',
confirmations: 'api/v1/account/confirmations',
passwords: 'api/v1/account/passwords',
}
end
resource :account, only: [:show]
EDIT
routes:
root#b2faabb49f91:/usr/src/app# rake routes | grep account
new_account_session GET /api/v1/account/sign_in(.:format) api/v1/account/sessions#new {:format=>:json}
account_session POST /api/v1/account/sign_in(.:format) api/v1/account/sessions#create {:format=>:json}
destroy_account_session DELETE /api/v1/account/sign_out(.:format) api/v1/account/sessions#destroy {:format=>:json}
new_account_password GET /api/v1/account/password/new(.:format) api/v1/account/passwords#new {:format=>:json}
edit_account_password GET /api/v1/account/password/edit(.:format) api/v1/account/passwords#edit {:format=>:json}
account_password PATCH /api/v1/account/password(.:format) api/v1/account/passwords#update {:format=>:json}

jsonapi-resources; wrong self link

I configured json-resouce-api.
But the self link generated by json-resource-api is wrong.
The code seems to checks the module hierarchy of the resource class and completely ignores how rails generates the routes.
routes.rb
require 'api_constraints'
Rails.application.routes.draw do
namespace :api, defaults: { format: :json }, constraints: { subdomain: 'api' }, path: '/' do
scope module: :v1, constraints: ApiConstraints.new(version: 1, default: true) do
# resources :subscriptions, only: [:index, :new, :create]
# jsonapi_resources :subscriptions, only: [:index, :new, :create]
jsonapi_resources :subscriptions
end
end
resouces/api/V1/subscription_recource.rb
class Api::V1::SubscriptionResource < JSONAPI::Resource
attributes :id, :third-service_id, :created_at, :updated_at
model_name 'Subscription'
# def custom_links(options)
# {self: nil}
# end
end
What I got => http://api.localhost.local:3000/api/v1/subscriptions/1
but it should be http://api.localhost.local:3000/subscriptions/1
How can I fix this?
UPDATE
rake routes
[DUPLICATE ATTRIBUTE] `id` has already been defined in SubscriptionResource.
Prefix Verb URI Pattern Controller#Action
api_v1_subscriptions GET /subscriptions(.:format) api/v1/subscriptions#index {:format=>:json, :subdomain=>"api"}
POST /subscriptions(.:format) api/v1/subscriptions#create {:format=>:json, :subdomain=>"api"}
api_v1_subscription GET /subscriptions/:id(.:format) api/v1/subscriptions#show {:format=>:json, :subdomain=>"api"}
PATCH /subscriptions/:id(.:format) api/v1/subscriptions#update {:format=>:json, :subdomain=>"api"}
PUT /subscriptions/:id(.:format) api/v1/subscriptions#update {:format=>:json, :subdomain=>"api"}
DELETE /subscriptions/:id(.:format) api/v1/subscriptions#destroy {:format=>:json, :subdomain=>"api"}
stripe_event /stripe-events StripeEvent::Engine
UPDATE2
This issue is totally the same as github.com/cerebris/jsonapi-resources/issues/591
Monkey Pack can be applied, but it's little bit risky.
For now ( 2016, Oct 5th ), I couldn't find any other ways than
namespace :api do
namespace :v1 do
jsonapi_resources :subscriptions
end
end
You can try it:
namespace :api, defaults: { format: :json }, constraints: { subdomain: 'api' }, path: "" do
namespace :v1, path: "" do
jsonapi_resources :subscriptions
end
end

Is there a method to change URL using namespaces?

I'm trying to change the URL name.
This is my routes.rb:
namespace :user_management do
resources :user do
collection do
get 'main'
end
end
end
match ':controller(/:action(/:id(.:format)))', :via => [:get, :post]
Rake routes:
Prefix Verb URI Pattern Controller#Action
root GET / user_management/login#login
user_main_user_management_user_index GET /user_management/user/main(.:format) user_management/user#main
user_management_user_index GET /user_management/user(.:format) user_management/user#index
POST /user_management/user(.:format) user_management/user#create
new_user_management_user GET /user_management/user/new(.:format) user_management/user#new
edit_user_management_user GET /user_management/user/:id/edit(.:format) user_management/user#edit
user_management_user GET /user_management/user/:id(.:format) user_management/user#show
PATCH /user_management/user/:id(.:format) user_management/user#update
PUT /user_management/user/:id(.:format) user_management/user#update
DELETE /user_management/user/:id(.:format) user_management/user#destroy
GET|POST /:controller(/:action(/:id(.:format))) :controller#:action
My URL is:
localhost/user_management/user/main
And I want:
localhost/user_main
I tried this but it is not working:
namespace :user_management do
resources :user do
collection do
get 'main', as: :user_main
end
end
end
I tried this but is not working either:
namespace :user_management do
resources :user do
collection do
get '/user_main', as: "user_management/user#main
end
end
end
try
match "user_admin", :to => "user_management/user#main"
you can do like in example:
get 'exit', to: 'sessions#destroy', as: :logout # url => /logout
FROM RAILS ROUTING

Rspec testing API versioning

I'm working with a web api in rails 4.1.8 and i'm doing some testing but always is returning me
Failure/Error: post '/v1/users', subdomain: 'api', user: #user.to_json, format: :json
ActionController::UrlGenerationError:
No route matches {:action=>"/v1/users", :controller=>"api/v1/users", :format=>:json, :subdomain=>"api", :user=>"JSON_OBJ"}
but if i test it in the browser with Advance Rest Console it works, i don't know what i'm doing wrong
here is me code
routes.rb
namespace :api, path: '/', constraints: {subdomain: 'api'}, defaults: { format: :json } do
namespace :v1 do
with_options except: [:edit, :new] do |except|
except.resources :users do
collection do
post 'login'
post 'showme'
end
end
except.resources :products
except.resources :locations
end
end
end
and my controller spec
module API
module V1
describe UsersController do
before do
request.host = "api.example.com"
expect({:post => "http://#{request.host}/v1/users"}).to(
route_to( controller: "api/v1/users",
action: "create",
subdomain: 'api',
format: :json
)
) # => PASS
# token expectations
#auth_token = allow(JWT::AuthToken).to(
receive(:make_token).and_return("mysecretkey")
)
expect(JWT::AuthToken.make_token({}, 3600)).to eq("mysecretkey")
end
describe "Create User" do
before(:each) do
#user = FactoryGirl.attributes_for :user
end
it 'should return a token' do
post '/v1/users', subdomain: 'api', user: #user.to_json, format: :json # Error
response_body = JSON.parse(response.body, symbolize_names: true)
expect(response_body['token']).to eql "mysecretkey"
end
end
end
end
end
rake routes
login_api_v1_users POST /v1/users/login(.:format) api/v1/users#login {:format=>:json, :subdomain=>"api"}
showme_api_v1_users POST /v1/users/showme(.:format) api/v1/users#showme {:format=>:json, :subdomain=>"api"}
api_v1_users GET /v1/users(.:format) api/v1/users#index {:format=>:json, :subdomain=>"api"}
POST /v1/users(.:format) api/v1/users#create {:format=>:json, :subdomain=>"api"}
api_v1_user GET /v1/users/:id(.:format) api/v1/users#show {:format=>:json, :subdomain=>"api"}
PATCH /v1/users/:id(.:format) api/v1/users#update {:format=>:json, :subdomain=>"api"}
PUT /v1/users/:id(.:format) api/v1/users#update {:format=>:json, :subdomain=>"api"}
DELETE /v1/users/:id(.:format) api/v1/users#destroy {:format=>:json, :subdomain=>"api"}
As described in http://api.rubyonrails.org/classes/ActionController/TestCase/Behavior.html which is referenced in https://www.relishapp.com/rspec/rspec-rails/docs/controller-specs, the first argument to post in your RSpec example is the name of the controller method to be called (i.e. :create in your case), not the route to that method.

Rails Route Redirecting to different view by default

The following is my routes.rb
Rails.application.routes.draw do
resources :admin do
post 'user_creation', on: :collection
end
get 'admin/user_creation'
resources :admin, :only => [:user_creation, :create, :new]
end
the following is my controller
class AdminController < ApplicationController
def new
#dashboard_user = DashboardUser.new
end
def create
#dashboard_user = DashboardUser.new(dashboard_params)
#dashboard_user.save!
end
def user_creation
end
private
def dashboard_params
params.require(:dashboard_user).permit(:user_name, :password, :last_name, :first_name, :middle_name , :phone)
end
end
then its a simple .html.erb file
what happens is that when i try to open the link admin/user_creation it automatically redirects to show.html.erb file
rake routes output
rake routes
Prefix Verb URI Pattern Controller#Action
user_creation_admin_index POST /admin/user_creation(.:format) admin#user_creation
admin_index GET /admin(.:format) admin#index
POST /admin(.:format) admin#create
new_admin GET /admin/new(.:format) admin#new
edit_admin GET /admin/:id/edit(.:format) admin#edit
admin GET /admin/:id(.:format) admin#show
PATCH /admin/:id(.:format) admin#update
PUT /admin/:id(.:format) admin#update
DELETE /admin/:id(.:format) admin#destroy
admin_user_creation GET /admin/user_creation(.:format) admin#user_creation
POST /admin(.:format) admin#create
GET /admin/new(.:format) admin#new
root GET / admin#new
You are clicking the link for the url. admin/user_creation
But your route says it is a POST url .
It need to added as GET.
Rails.application.routes.draw do
resources :admin do
post 'user_creation', on: :collection
get 'user_creation', on: :collection
end
get 'admin/user_creation'
resources :admin, :only => [:user_creation, :create, :new]
end
Your route not looking good. Whenever we try to go to any url directly from browser it consider request as get type. routes file try to find route from top to bottom. That why it's considering /admin/user_creation as show action of admin_controller and user_creation as id to show.
Don't know why you need get and post request for same action. But your routes should look like this:
Rails.application.routes.draw do
resources :admin, :only => [:create, :new] do
post 'user_creation', on: :collection
get 'user_creation', on: :collection
end
end
One thing with above routes there is no route for show action. Now you will properly go to user_creation path.
if you don't need any post path then it should look like:
Rails.application.routes.draw do
resources :admin, :only => [:create, :new] do
get 'user_creation', on: :collection
end
end
if you need all the default action then:
Rails.application.routes.draw do
resources :admin do
get 'user_creation', on: :collection
end
end

Resources