Adding get route to registrations#show for Devise

I’ve got Devise set up with all controllers under “user” like so:

  devise_for :users, module: "users"

Now I want to add a get /account route to show a user’s “registration”, and instinctively thought that this would do it:

  devise_for :users, module: "users" do 
    get "/account", to: "registrations#show", as: "user_registration"
  end

But no get /account route is added:

 cancel_user_registration GET    /users/cancel(.:format)                           users/registrations#cancel
    new_user_registration GET    /users/sign_up(.:format)                          users/registrations#new
   edit_user_registration GET    /users/edit(.:format)                             users/registrations#edit
        user_registration PATCH  /users(.:format)                                  users/registrations#update
                          PUT    /users(.:format)                                  users/registrations#update
                          DELETE /users(.:format)                                  users/registrations#destroy
                          POST   /users(.:format)                                  users/registrations#create

So I try another way:

  devise_for :users, module: "users"
  devise_scope :user do
    get "/account", to: "registrations#show", as: "user_registration"
  end

But now the router complains that I’m intruding in the user_registration helper space:

 `add_route': Invalid route name, already in use: 'user_registration'  (ArgumentError)
You may have defined two routes with the same name using the `:as` option, or you may be overriding a route already defined by a resource with the same naming. For the latter, you can restrict the routes created with `resources` as explained here: 
https://guides.rubyonrails.org/routing.html#restricting-the-routes-created

Adding only: [:show] makes no difference, but it does work if I name the helper to something else, e.g.

  devise_for :users, module: "users"
  devise_scope :user do
    get "/account", to: "registrations#show", as: "something_else"
  end

Resulting in the routes:

 cancel_user_registration GET    /users/cancel(.:format)                           users/registrations#cancel
    new_user_registration GET    /users/sign_up(.:format)                          users/registrations#new
   edit_user_registration GET    /users/edit(.:format)                             users/registrations#edit
        user_registration PATCH  /users(.:format)                                  users/registrations#update
                          PUT    /users(.:format)                                  users/registrations#update
                          DELETE /users(.:format)                                  users/registrations#destroy
                          POST   /users(.:format)                                  users/registrations#create
           something_else GET    /account(.:format)                                registrations#show

Unfortunately I’m a picky bastard, and I do not like this option; the helper name should be user_registration as already present for patch, put, delete and post and the method should be get. Is there a nice way to satisfy my OCD?

I’m going to answer this myself: there’s a little known option (completely unknown to me at least) for as: that will make the route inherit the helper name when set to nil:

  devise_for :users, module: "users", path: "user"
  devise_scope :user do
    get "account", to: "users/registrations#show", as: nil
  end

Resulting in the routes:

 cancel_user_registration GET    /users/cancel(.:format)                           users/registrations#cancel
    new_user_registration GET    /users/sign_up(.:format)                          users/registrations#new
   edit_user_registration GET    /users/edit(.:format)                             users/registrations#edit
        user_registration PATCH  /users(.:format)                                  users/registrations#update
                          PUT    /users(.:format)                                  users/registrations#update
                          DELETE /users(.:format)                                  users/registrations#destroy
                          POST   /users(.:format)                                  users/registrations#create
                          GET    /account(.:format)                                users/registrations#show

:partying_face:

1 Like

Hmmm. It’s a little bit odd in that the helper seems to ignore the route path:

puts user_registration_path

> /users

A get to /users correctly triggers the registrations#show action, so that’s good. But if I change it in the route:

  devise_for :users, module: "users"
  devise_scope :user do
    get "something_else", to: "users/registrations#show", as: nil
  end

I now get a 404 when doing a get to users, though the user_registration_path helper still returns /users. Odd. It seems the scoped path override only applies to the request routing, and not the associated helper method?

In any case, I think I’ve found a happy compromise that will satisfy my OCD by setting the path for Devise to the singular “user”:

  devise_for :users, module: "users", path: "user"
  devise_scope :user do
    get "user", to: "users/registrations#show", as: nil
  end

Resulting in the following routes for the RegistrationsController:

 cancel_user_registration GET    /user/cancel(.:format)                            users/registrations#cancel
    new_user_registration GET    /user/sign_up(.:format)                           users/registrations#new
   edit_user_registration GET    /user/edit(.:format)                              users/registrations#edit
        user_registration PATCH  /user(.:format)                                   users/registrations#update
                          PUT    /user(.:format)                                   users/registrations#update
                          DELETE /user(.:format)                                   users/registrations#destroy
                          POST   /user(.:format)                                   users/registrations#create
                          GET    /user(.:format)                                   users/registrations#show

This works for me since the only place in my application where actions are performed on multiple users is in a separate namespace, and the singular “user” makes perfect sense everywhere else. YMMV.

1 Like