How To: Upgrade to Devise 4.9.0 [Hotwire Turbo integration] · heartcombo/devise Wiki の日本語訳です。
ところどころ意訳していますが、明らかに間違った訳があればコメント欄で教えてください。
日本語版の最終更新日: 2023-02-17
https://github.com/heartcombo/devise/blob/main/CHANGELOG.md
本バージョンのDeviseはHotwire / Turbo / Turbo-Railsの各フレームワークに対応しました。
Changelogでは利用開始に必要な大半の情報を記述していますが、このwikiでもひとつずつ変更点を説明していきます。
フォーム送信に関するTurboのドキュメントを引用します。
フォーム送信によってステートフルなリクエストが送られると、Turbo DriveはサーバがHTTP 303リダイレクトのレスポンスを返すことを期待します。これにより、リロードすることなくページを更新し、画面遷移を実現します。
このルールの例外は4xxまたは5xxのステータスコードでレスポンスがレンダリングされた場合です。この例外があることで、サーバが
422 Unprocessable Entity
を返したときはバリデーションエラーがレンダリングされ、サーバが壊れていて500 Internal Server Error
だった場合は"Something Went Wrong"が表示されます。
Devise(とResponders)はこれまでバリデーションエラーが発生したときは200 OK
を返し、リダイレクトでは302 Found
を返していました。これはどちらもレンダリングとリダイレクトのRailsのデフォルトの挙動でした。Hotwire/Turboの登場により、Railsは新しい要件に合わせて422 Unprocessable Entity
でレンダリングしたり、303 See Other
でGETしないリダイレクトを実現したりするように変わりました。
Hotwire/Turboに対応し、上記の新しいデフォルト挙動に合わせるため、Deviseは最新バージョンのResponders
(v3.1.0以上)を利用しなければなりません。これを使うと、バリデーションエラー発生時のエラーレスポンス(error_status
)と、POST/PUT/PATCH/DELETEリクエスト後のリダイレクトレスポンス(redirect_status
)を自由に設定できるようになります。
新規に作成したアプリでは、rails generated devise:install
を実行すると最初からこの新しい設定が導入されます。既存のアプリをアップグレードする場合は、以下の設定を追加することで新しいレスポンスの挙動を採用することができます。
# config/initializers/devise.rb
Devise.setup do |config|
# ...
config.responder.error_status = :unprocessable_entity
config.responder.redirect_status = :see_other
# ...
end
後方互換性のため、Deviseの内部ではerror_status
は:ok
(200 OK
)に、redirect_status
は:found
(302 Found
)に設定されています。上記の設定を追加することで、エラーとリダイレクトのステータスが422 Unprocessable Entity
と303 See Other
に設定されます。これにより、Hotwire/Turboの要件に適合できます。
最新のDeviseと、この設定をサポートしない古いバージョンのRespondersを組み合わせた状態で上記の設定を追加すると、警告が表示され、Deviseの挙動に変化は起きません。
注意:将来的にはRails + Hotwire/Turboのデフォルトに合わせて、Deviseのデフォルトも変更される可能性があります。
もしあなたが独自のresponderをアプリケーションに設定し、Deviseにもその挙動を反映させたい場合は、Deviseがそのresponderを利用するためにconfig.responder = MyApplicationResponder
のようにしてDeviseのresponderをまるっと置き換える必要があるかもしれません。
Deviseで独自のresponderを使う主な理由は、前述のとおりステータスを変更できるようにするためです。しかし、やろうと思えば独自のresponderの設定を変更することもできます。そうすればDeviseはその設定を使います。詳しくはRespondersのREADMEを参照してください。
重要:もしあなたがHotwire/Turboに対応するためだけに独自のresponderとfailure appの両方、もしくはどちらか一方を作ってレスポンスを変更していたなら、もうそのresponderとfailure appは必要ありません。
Turbo Railsは:turbo_stream
MIME フォーマットからのリクエストを識別できるように、そのフォーマットを登録しています。新しいDeviseはこのフォーマットをナビゲーション可能なフォーマット(navigational format)として認識します。これにより、Turboを使用する場合、このフォーマットがHTMLナビゲーションのように動作します。
注意:もしあなたがこれまで:turbo_stream
をナビゲーションしないフォーマット(non-navigational format)として扱っていた場合は、config/initializers/devise.rb
内のnavigational_formats
を変更することで、このフォーマットをナビゲーション可能なフォーマットから除外することができます。
以下の情報はDeviseが提供するデフォルトの shared views/links (コピーしてから変更してないもの) を使っており、なおかつ(もしくは)rails-ujsからTurboに移行する場合に関係する話題です。
OmniAuthの"Sign in with"リンクはこれまでmethod: :post
を使っていました。これはPOSTリクエストをサーバに送信し、それからプロバイダ(訳注:OAuthプロバイダ)へリダイレクトするためです。この動作にはrails-ujsが必須でした。この仕組みは(data: { turbo_method: :post }
のようにして)Turboで動かそうとしても動作しません。なぜならPOST fetchリクエストのあとでリダイレクトしようとするとCORSエラーが発生するからです。
デフォルトでHotwire/Turboとrails-ujsの両方で互換性を保つようにするため、shared views内のリンクは(button_to
ヘルパーを使って)ボタンに変更されました。これにより、method=POST
となるHTMLフォームが生成されます。新しいRailsアプリではrails-ujsはデフォルトで利用できなくなっていますが(訳注:Rails 7以降ではrails-ujsではなくHotwire/Turboがデフォルトで組み込まれる)、ボタンであればHotwire/Turboとrails-ujsの両方で動作します。
この変更はOmniAuthが有効かつ、Deviseが提供するデフォルトのdevise/shared/_links.html.erb
パーシャルを使っているアプリにのみ適用されます。大半のアプリはおそらくこのパーシャルをコピーして変更していると思われます。その場合はこの変更が適用されません。ですが、もしあなたがアプリ内でshared viewsのリンクを直接使っている場合、ボタンのスタイルを変更するか、(rails-ujsを使っているなら)viewをコピーしてボタンをリンクに戻すか、どちらかの作業が必要になるかもしれません。
data-confirm
オプション(ボタンやフォームをサブミットする前に表示する確認モーダルに追加するオプション)は、Turboで動作するようにdata-turbo-confirm
に変更する必要があります(rails-ujsは変更不要です)。Deviseでは後方互換性を維持するため、アカウント削除用の"Cancel my account"ボタンに両方のオプションを追加しています。
例
<%# rails-ujsでは動作するがTurboでは動作しない %>
<%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %>
<%= link_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %>
<%# Turboでは動作するがrails-ujsでは動作しない %>
<%= button_to "Cancel my account", registration_path(resource_name), data: { turbo_confirm: "Are you sure?" }, method: :delete %>
<%= link_to "Cancel my account", registration_path(resource_name), data: { turbo_confirm: "Are you sure?", turbo_method: :delete } %>
<%# Turboとrails-ujsの双方で動作する %>
<%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?", turbo_confirm: "Are you sure?" }, method: :delete %>
<%= link_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?", turbo_confirm: "Are you sure?", turbo_method: :delete }, method: :delete %>
Deviseはshared linksでbutton_to
を採用しています。これはTurboとrails-ujsの互換性を簡単かつ適切に提供できるためです。このviewをあなたのアプリにコピーする場合は、rails-ujsまたはTurboのどちらかに合わせて適切なコードを選択してください。
注意: ボタンにdata: { turbo_confirm: "<message>" }
を付ける場合はturbo-rails v1.3.0以上を使う必要があります。これより前のバージョンであれば、form: { data: { turbo_confirm: "<message>" } }
を使ってボタンを囲むフォームにこのオプションが付くようにしてください。(この変更点はTurbo 7.2.0に含まれています。このバージョンではこちらの修正によって挙動が変更されています。)
data-method
オプション(リンクからサブミットする際のリクエストメソッドを設定するオプション)はdata-turbo-method
に変更する必要があります。button_to
やform
であればTurboが適切に処理してくれるのでこの変更は必要ありませんが、rails-ujsでサブミットするリンクはTurbo用の修正が必要になります。
もしあなたが:delete
(これはデフォルトです)でログアウトするようにDeviseをセットアップし、method: :delete
オプションを使って(フォームで囲まれたボタンではなく)ログアウト用のリンクを使っている場合は、修正が必要になります。(Deviseはログアウト用のリンクやボタンをshared viewsで提供していません。)
例
<%# rails-ujsでは動作するがTurboでは動作しない %>
<%= link_to "Sign out", destroy_user_session_path, method: :delete %>
<%# Turboで動作する %>
<%= button_to "Sign out", destroy_user_session_path, method: :delete %>
<%= link_to "Sign out", destroy_user_session_path, data: { turbo_method: :delete } %>
<%# Turboとrails-ujsの双方で動作する %>
<%= button_to "Sign out", destroy_user_session_path, method: :delete %>
<%= link_to "Sign out", destroy_user_session_path, method: :delete, data: { turbo_method: :delete } %>
もしリンクを使っている場合は、あなたのアプリが適切なオプションを選択していることを確認するか、幅広い互換性(訳注:Turboでもrails-ujsでも使えるようにすること)を考えてフォームに囲まれたボタンを使うように実装を変更してください。
アップグレード中に何か問題が発生したら報告してください。