[Devise How-To] sign_inとsign_outのデフォルトルーティングを変更する(翻訳)

こんにちは、hachi8833です。
Devise gemのWiki How-To翻訳、第2弾です。DeviseもRails 5.1対応で忙しそうですね。

概要

原文が更新されていることにお気づきの場合は、ぜひ@techrachoまでお知らせください。更新いたします。

[How-To] sign_inとsign_outのデフォルトルーティングを変更する

Deviseで使うロール(役割)が1つしかない状態で、ログイン(sign in)とログアウト用(sign out)のルーティングをデフォルトの /users/sign_in/users/sign_outから/login/logoutに変更したいことがあります。

これはデフォルトのままのDeviseではできません。Deviseは、URLをチェックすることによってユーザーのアクセスするスコープを決定するからです。URLが/users/loginであればスコープはuserであることがDevise側でわかりますが、/loginにアクセスすると、使われるべきスコープをDevise側で決定できません。幸いなことに、Deviseにはデフォルトスコープを指定するしくみがあるので、これによってURLを短くできます。

手順(Rails 3.0以降)

この設定は、アクセスに使うURLをルーティングのdevise_scopeに記述するだけで完了します。

devise_scope :user do
  get 'login', to: 'devise/sessions#new'
end

以下のように、devise_scopeのエイリアスであるasも使えます。

as :user do
  get 'login', to: 'devise/sessions#new'
end

sign_outの場合は次のようにします。

devise_scope :user do
  delete 'logout', to: 'devise/sessions#destroy'
end

次のようにskip:オプションですべてのセッションルーティングをスキップし、必要なルーティングだけを定義することもできます。

devise_for :users, skip: [:sessions]
as :user do
  get 'signin', to: 'devise/sessions#new', as: :new_user_session
  post 'signin', to: 'devise/sessions#create', as: :user_session
  delete 'signout', to: 'devise/sessions#destroy', as: :destroy_user_session
end

このようにすると、#authenticate_user!などのヘルパーを使ったときに、定義済みの正しいカスタムページにユーザーをリダイレクトできます。

ただし、:sign_out_viaという設定オプションを使っていると上のsignoutアクションがエラーになることがあります。その場合は、デフォルトの動作を複製して次のように:sign_out_viaによってdeletegetに変更します。

devise_for :users, skip: [:sessions]
as :user do
  get 'signin', to: 'devise/sessions#new', as: :new_user_session
  post 'signin', to: 'devise/sessions#create', as: :user_session
  match 'signout', to: 'devise/sessions#destroy', as: :destroy_user_session, via: Devise.mappings[:user].sign_out_via
end

別のもっとシンプルな方法

#devise_forメソッドにはこういうときに便利なオプションパラメータが多数用意されています。

たとえば、usersという名前空間をすべてのルーティングから取り除き、sign_insign_outをそれぞれloginlogoutに置き換えるには、次のようにします。

devise_for :users, path: '', path_names: { sign_in: 'login', sign_out: 'logout'}

ただし、Deviseで複数のコントローラ(パスワード用など)を使いたい場合は、最初の方法を使う必要があります。

morimorihogeより追記

上記の sign_out_via に関連してすこし補足です。

Deviseではログインセッションをsessionという概念で管理しており、RESTful的には「新規sessionを作る(Devise::SessionsController#create)=ログインする」といった抽象化がされています。
そのため「ログアウトする」はsessionを破棄する、すなわちsessionの削除(Devise::SessionsController#destroy)というインターフェースになっています。

このとき、確かにsession削除はDELETEメソッドで呼ばれるのがRESTの概念上あるべき姿なのですが「ログアウトする」という機能自体はRailsの外から呼びたかったり、リダイレクト処理の中で強制ログアウトさせたいといったケースが考えられるため、DELETEメソッドよりもGETメソッドでログアウトできた方が便利です( _method=delete とか付ければGETでもDELETEメソッドにbypassできますが、いちいち考えるのが面倒)。

というわけで、以下の様に sign_out_via を設定してやることで、ログアウトについてはGETリクエストで処理でき、 link_to ヘルパでも単なる destroy_user_session_path へのリンクとして記述できるようになります。

Devise.config.sign_out_via = :get
= link_to 'ログアウト', destroy_user_path

関連記事(Devise)

Ruby on RailsによるWEBシステム開発、Android/iPhoneアプリ開発、電子書籍配信のことならお任せください この記事を書いた人と働こう! Ruby on Rails の開発なら実績豊富なBPS

この記事の著者

hachi8833

Twitter: @hachi8833 コボラー、ITコンサル、ローカライズ業界を経てなぜかWeb開発者志願。 これまでにRuby on Rails チュートリアルの大半、Railsガイドのほぼすべてを翻訳。 かと思うと、正規表現の粋を尽くした日本語エラーチェックサービス enno.jpを運営。 仕事に関係ないすっとこブログ「あけてくれ」は2000年頃から多少の中断をはさんで継続、現在はnote.muに移転。

hachi8833の書いた記事

週刊Railsウォッチ

インフラ

Rubyスタイルガイドを読む

BigBinary記事より

ActiveSupport探訪シリーズ