Deviseのルーティングが微妙なのでgemを作ってみた

Devise便利ですよね。簡単にリッチな認証機能がつくれます。

# config/routes.rb
devise_for :users
        new_user_session GET    /users/sign_in(.:format)          devise/sessions#new
            user_session POST   /users/sign_in(.:format)          devise/sessions#create
    destroy_user_session DELETE /users/sign_out(.:format)         devise/sessions#destroy
           user_password POST   /users/password(.:format)         devise/passwords#create
       new_user_password GET    /users/password/new(.:format)     devise/passwords#new
      edit_user_password GET    /users/password/edit(.:format)    devise/passwords#edit
                         PUT    /users/password(.:format)         devise/passwords#update
cancel_user_registration GET    /users/cancel(.:format)           devise/registrations#cancel
       user_registration POST   /users(.:format)                  devise/registrations#create
   new_user_registration GET    /users/sign_up(.:format)          devise/registrations#new
  edit_user_registration GET    /users/edit(.:format)             devise/registrations#edit
                         PUT    /users(.:format)                  devise/registrations#update
                         DELETE /users(.:format)                  devise/registrations#destroy
       user_confirmation POST   /users/confirmation(.:format)     devise/confirmations#create
   new_user_confirmation GET    /users/confirmation/new(.:format) devise/confirmations#new
                         GET    /users/confirmation(.:format)     devise/confirmations#show

いまいちなところ

Devise::RegistrationsControllerの役割があいまい

/usersが Devise::RegistrationsController に取られてるのはひどい。普通 UsersController になるはずでしょう。
PUT /usersDELETE /usersも地味にひどい)

さらにここによく使うresources :usersを足すとカオスに…。

cancel_user_registration GET    /users/cancel(.:format)          devise/registrations#cancel
       user_registration POST   /users(.:format)                 devise/registrations#create
   new_user_registration GET    /users/sign_up(.:format)         devise/registrations#new
  edit_user_registration GET    /users/edit(.:format)            devise/registrations#edit
                         PUT    /users(.:format)                 devise/registrations#update
                         DELETE /users(.:format)                 devise/registrations#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
                         PUT    /users/:id(.:format)             users#update
                         DELETE /users/:id(.:format)             users#destroy

何をどう使えばいいんでしょうか、これ…。

何が問題なのか

  • 複数形であるusersは普通「ユーザー全体」をさすはず(コレクションリソース)。なのに registrations#update は「自分自身を更新」つまり「自分自身」をさしている。 #cancel, #edit, #destroy も同様
  • そもそもURLにも出てこない“registrations”っていったい何?

「自分自身」をさすリソースは1つしかないので単数形で表現できます。

resource  :user  # (a)単数
resources :users # (b)複数
resource  :users # (c)まぎらわしいのでよくない

(a)と(b)は別のリソースなので区別しましょう。

解決策(案)

devise-better_routes gemを使うと、ルーティングはこのようになります。

# Gemfile
gem 'devise-better_routes'
              users POST   /users(.:format)               users#create
           new_user GET    /users/new(.:format)           users#new
cancel_current_user GET    /current_user/cancel(.:format) current_users#cancel
  edit_current_user GET    /current_user/edit(.:format)   current_users#edit
       current_user PATCH  /current_user(.:format)        current_users#update
                    PUT    /current_user(.:format)        current_users#update
                    DELETE /current_user(.:format)        current_users#destroy

謎のregistrationsは消えて、コントローラがusersとcurrent_usersに整理されました。
このコントローラは用意されていませんが、基本は単にこんな感じで作ってやればOKです。

# app/controllers/users_controller.rb
class UsersController < Devise::RegistrationsController
end

# app/controllers/current_users_controller.rb
class CurrentUsersController < Devise::RegistrationsController
end

同様に、passwordとsessionもパスが変更されます。

オプション

/current_userって名前はいまいち…、というときは、オプションで変更することができます。

# config/routes.rb
devise_for :users, path_names: {current_user: 'me'}
    users POST   /users(.:format)     users#create
 new_user GET    /users/new(.:format) users#new
cancel_me GET    /me/cancel(.:format) me#cancel
  edit_me GET    /me/edit(.:format)   me#edit
       me PATCH  /me(.:format)        me#update
          PUT    /me(.:format)        me#update
          DELETE /me(.:format)        me#destroy

いい感じじゃないですか?

Deviseへissueを投げてみた

Thanks for the proposal. Yes, it make sense, even though we have originally decided to not pollute two namespaces for the same Devise controller. Unfortunately, this is a very backwards incompatible change, so any change of this dimension would have to wait until Devise 4.0 (and we just released 3.0). Fortunately, you can customize it on your own, as you did. :)


So I am closing this but leaving a note to myself that we could possibly revisit this (in a year or two).

Registration routes are confusing · Issue #2505 · plataformatec/devise · GitHub

ってことで、リップサービスかもしれないけどDevise 4.0に期待w