Changing URL of single route in resource - ruby-on-rails

Let's say I have
resources :users, only: %i[new create edit update]
I would like to change typical /users/new to /signup.
Code like below is ugly.
resources :users, only: %i[create edit update]
get '/signup', to: 'users#new'
Is there a better (cleaner) way to do it?
EDIT: Seems like it's the best way that there is. Guess grouping is the only way to keep it readable.

As far as I'm aware the best you're going to be able to do is
resources :users, only: [:new], path_names: { new: "sign_up" }, path: '/'
resources :users, only: [:create, :edit, :update]
which at least makes it easier to tell that 'sign_up' is part of the users resource. If you're fine with /users/sign_up you can do it in one line, but wanting to get rid of the /users part makes this trickier.

If you wanted to keep them organized and do not mind the route being "/users/signup" then this will work.
resources :users, only: %i[create edit update] do
get 'signup', to: :new, on: :collection, as: :signup
end
Then rake routes
signup_users GET /users/signup(.:format) users#new
users POST /users(.:format) users#create
edit_users GET /users/:id/edit(.:format) users#edit
users PATCH /users/:id(.:format) users#update
PUT /users/:id(.:format) users#update

Related

Rails routing: add a custom route to the standard list of actions

Rails routes create the 7 CRUD actions by default following REST.
resources :users
However, I have a confirm_destroy action that I use in almost every resource, because I have a lot of logic that goes on the confirmation page; it's not just a simple yes/no alert dialog.
resources :users do
get :confirm_destroy, on: :member
end
With 50+ resources, it gets tedious to write this out for each resource and my routes file is literally 3x longer because of this.
Is there any way to add an action to the standard 7 for the resources block such that
resources :users
would be the same as
resources :users do
get :confirm_destroy, on: :member
end
and I can use it in the routes as a standard action, ie:
resources :users, only: [:show, :confirm_destroy, :destroy]
resources :users, except: [:confirm_destroy]
While not quite as elegant as you might like, the Rails way would be to use a routing concern, as #dbugger suggested.
For example:
concern :confirmable do
get 'confirm_destroy', on: :member
end
resources :users, :widgets, concerns: :confirmable
$ rails routes
Prefix Verb URI Pattern Controller#Action
confirm_destroy_user GET /users/:id/confirm_destroy(.:format) users#confirm_destroy
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
PATCH /users/:id(.:format) users#update
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
confirm_destroy_widget GET /widgets/:id/confirm_destroy(.:format) widgets#confirm_destroy
widgets GET /widgets(.:format) widgets#index
POST /widgets(.:format) widgets#create
new_widget GET /widgets/new(.:format) widgets#new
edit_widget GET /widgets/:id/edit(.:format) widgets#edit
widget GET /widgets/:id(.:format) widgets#show
PATCH /widgets/:id(.:format) widgets#update
PUT /widgets/:id(.:format) widgets#update
DELETE /widgets/:id(.:format) widgets#destroy

How to nest a namespaced ruby on rails controller

I have a controller in a name spaced controller/user directory, so its first line is
class User::BookingsController < ApplicationController
I have set the routes as follows
resources :users do
namespace :user do
resources :bookings
end
end
The path to index action is
user_user_bookings_path or
/users/:user_id/user/bookings(.:format)
which works fine, but the double user_user sounds like a Catch 22 joke. Is there a more elegant way to do this?
What would you like to achieve? You are namespacing under "user" after all. You can try options like as: nil, but I think this will be even funnier (user__bookings_path).
What you can do is write every route by hand, something like:
resources :users do
post 'bookings', to: 'user/bookings#create'
end
# => user_bookings_path, POST /users/:user_id/bookings
or if you want to preserve the URL
resources :users do
post '/user/bookings', to: 'user/bookings#create', as: 'bookings'
end
# => user_bookings_path, POST /users/:user_id/user/bookings
Try this
resources :users do
resources :bookings
end
According to the docs you can namespace your resource
by using the namespace block
namespace :api do
resources :users
end
this would give you those routes:
/api/users [GET, POST]
/api/users/:id [GET, PUT, DELETE]
However you want to nest the resource within another resource (docs) you would do this:
resources :users do
resources :bookings
end
Which would result in these routes:
/users/:user_id/bookings [GET, POST]
/users/:user_id/bookings/:id [GET, PUT, DELETE]
Because you have your controller scoped under User you have to set the user scope like this:
resources :users do
resources :bookings, module: :user
end
Which results in those routes:
➜ playground rake routes
Prefix Verb URI Pattern Controller#Action
user_bookings GET /users/:user_id/bookings(.:format) user/bookings#index
POST /users/:user_id/bookings(.:format) user/bookings#create
new_user_booking GET /users/:user_id/bookings/new(.:format) user/bookings#new
edit_user_booking GET /users/:user_id/bookings/:id/edit(.:format) user/bookings#edit
user_booking GET /users/:user_id/bookings/:id(.:format) user/bookings#show
PATCH /users/:user_id/bookings/:id(.:format) user/bookings#update
PUT /users/:user_id/bookings/:id(.:format) user/bookings#update
DELETE /users/:user_id/bookings/:id(.:format) user/bookings#destroy
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
PATCH /users/:id(.:format) users#update
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy

Rails same route (POST /users) for different controller actions [Devise]

I have a rails api application where I am using Devise gem for user management. I created a user model off the devise gem. After that, I noticed that I have two same routes listed in the rake routescommand. I want POST (/users) to call api/v1/users#create action first and then call devise/registrations#create.
user_registration POST /users(.:format) devise/registrations#create
api_users POST /users(.:format) api/v1/users#create {:format=>:json}
When I test POST (/users) using users_controller_spec file, api/v1/users#create action is called. However, when I do a POST (/users) using POSTMAN, the logs indicates that devise/registrations#createaction is called instead.
How do I correct this so that the POST (/users) I do using POSTMAN or curl calls api/v1/users#create first to create the user model and then calls devise/registrations#create to register the user?
I am not 100% sure how devise works so any help here would be helpful.
This is my config/routes.rb
Rails.application.routes.draw do
devise_for :users
# Api definition
namespace :api, defaults: { format: :json }, path: '/' do
scope module: :v1, constraints: ApiConstraints.new(version: 1, default: true) do
# We are going to list our resources here
resources :users, only: [:show, :create, :update, :destroy]
resources :sessions, only: [:create, :destroy]
end
end
end
So, the thing with Rails Routes is, when you make a request, routes are checked as they are defined in the routes.rb from top to bottom.
Now, when you make a request via POSTMAN, the /users path matches with a path generated via devise_for, as it is the first line in the file.
Now, when you are writing tests for the controller, you are not really accessing /users, you are just telling the api/v1/users_controller to invoke the create method, which is bound to hit the api/v1/users#create
Now, a way you can resolve this conflict is by changing what devise names its routes. If you do something like this:
Rails.application.routes.draw do
devise_for :users, path: 'customer'
# Api definition
namespace :api, defaults: { format: :json }, path: '/' do
scope module: :v1, constraints: ApiConstraints.new(version: 1, default: true) do
# We are going to list our resources here
resources :users, only: [:show, :create, :update, :destroy]
resources :sessions, only: [:create, :destroy]
end
end
end
This is what the devise routes will be:
new_user_session GET /customer/sign_in(.:format) devise/sessions#new
user_session POST /customer/sign_in(.:format) devise/sessions#create
destroy_user_session DELETE /customer/sign_out(.:format) devise/sessions#destroy
user_password POST /customer/password(.:format) devise/passwords#create
new_user_password GET /customer/password/new(.:format) devise/passwords#new
edit_user_password GET /customer/password/edit(.:format) devise/passwords#edit
...

Is there a better way to define these routes?

I'm working on supporting users in my Rails app. There's no need for a user to even be aware that there are other users out there. I don't want to just use resources :users, because the routes that generates are these:
users GET /users users#index
POST /users users#create
new_user GET /users/new users#new
edit_user GET /users/:id/edit users#edit
user GET /users/:id users#show
PATCH /users/:id users#update
PUT /users/:id users#update
DELETE /users/:id users#destroy
(.:format) removed for improved readability.
You'd have to put the user id number in the URL, and that offers a chance for users to be aware that other users exist. I want these routes:
users GET /users users#index
POST /users users#create
new_user GET /users/new users#new
edit_user GET /users/me/edit users#edit
user GET /users/me users#show
PATCH /users/me users#update
PUT /users/me users#update
DELETE /users/me users#destroy
Yes. /users/me is the path of your user, and that's the only user path you can get at.
But the problem is defining these routes. Here's one idea:
resources :users, constraints: { id: 'me' }
And in the User model:
def to_param
'me'
end
But that seems too kludgy for me. Any better ideas?
With Singular Resources your are good to go ;-)
Define your routes like this:
# config/routes.rb
resources :users, only: [:index, :create, :new]
resource :user, path: '/users/me', only: [:show, :edit, :update, :destroy]
Your routes will be singular (/user) if you leave the path: option. Play around with the options ;-)
And your rake routes result should look like this:
Prefix Verb URI Pattern Controller#Action
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/me/edit(.:format) users#edit
user GET /users/me(.:format) users#show
PATCH /users/me(.:format) users#update
PUT /users/me(.:format) users#update
DELETE /users/me(.:format) users#destroy
You can also test the routes on the Rails console (rails c)
2.1.3 :001 > Rails.application.routes.url_helpers.user_path
=> "/users/me"
2.1.3 :002 > Rails.application.routes.url_helpers.edit_user_path
=> "/users/me/edit"
If you want, you can pluralize :user on the Singular Resources, but don't forget to set the as: option, her an example:
# config/routes.rb
resources :users, only: [:index, :create, :new]
resource :users, path: '/users/me', as: 'user', only: [:show, :edit, :update, :destroy]
Please take a look to the notes and warnings in the Rails guide! Here is an excerpt:
A long-standing bug prevents form_for from working automatically with singular resources. ...

Rails routes map incorrectly

I have the following statement in routes:
resource :users, :only => [:index, :show, :update, :destroy] do
get "users/dashboard"
end
However, when I do rake routes, I got:
dashboard_users GET /users/users/dashboard(.:format) users/users#dashboard
users GET /users(.:format) users#show
PUT /users(.:format) users#update
DELETE /users(.:format) users#destroy
Note that users_path was incorrectly mapped to users#show. I was expecting:
users GET /users/:id/(.:format) users#show
What might cause this issue and how I can fix it?
you should use resources instead of resource. e.g.
resources 'users'
the difference between resource and resources is:
resource is for operations for a single model. when using resource model, there's no "index" action.
see: http://guides.rubyonrails.org/routing.html
further more, I notice that your route is a bit strange. if you just want to add an action named 'dashboard' with GET accessing method, just declare like this:
resources 'users' do
get :dashboard
end
Instead of doing this
resource :users, :only => [:index, :show, :update, :destroy] do
get "users/dashboard"
end
Do this
namespace :dashboard do
resource :users, :only=>[:index,:show,:udpate,:destroy]
end
I think this will work for you.

Resources