はじめに
未経験、独学でPFを作成中で、Device gem が今のRailsログインシステムのデファクトスタンダートになっていると知り、導入しました。
しかし、Rails 7.0にまだ完全には対応していないよう(参考)なのでエラーが起き、原因と解決策を忘れないためにこの記事を書きました。
例).
- 新規登録(sign_up)時に「Undefined method ‘user_url’」エラー発生
- 新規登録(sign_up)時のバリデーションエラー時にメッセージが表示されない
実行環境
この記事は以下の実行環境で動作確認しました。
- Rails 7.0.4.1
- Ruby 3.0.4
- Devise 4.8.1
- turbo-rails 1.1.1
注意事項
この情報は2023年1月24日時点の情報です。時間が経てば修正されるかもしれません。
Web制作勉強中の者が書いているため、間違えているかもしれません。コメント等で指摘していただけると幸いです。
参考にさせていただいた記事
https://hotwired.dev/
https://turbo.hotwired.dev/handbook/introduction
https://qiita.com/jnchito/items/5c41a7031404c313da1f
https://qiita.com/jnchito/items/48db78c465493837c41f
https://kobacchi.com/rails7-devise-responded-to-turbo/
なぜ正しく機能しないのか
Rails 7.0版よりJavaScriptの送信に関する処理などが書かれたライブラリであるrails-ujsが推奨ではなくなり、rails newで環境を構築すると、
Hotwire(Turbo)が代わりに使われるようになりました。
Turboによって、モダンなウェブアプリケーションをたくさんのJavaScriptなしで、JSONの代わりにHTMLを送って作ることができる(jQueryを使わない)ようになり、サーバーサイドでSPA化をできるようになりました。
しかし、2021年12月にバージョン7になったこともあり、まだ互換性が低いgemがあるようです。
解決策
Deviseはインストール済みとします。
基本的にはこちらの記事に従って作業を進めます。
https://qiita.com/jnchito/items/48db78c465493837c41f
・ 「Undefined method ‘user_url’」エラーへの対処
Rails7ではデフォルトでTurbo Streamが有効になっているため、Deviseでsign_upを実行しようとすると、「Undefined method ‘user_url’」エラーが発生するようです。
config/initializers/devise.rbに追記します。
- #config.navigational_formats = ['*/*', :html]
+ config.navigational_formats = ['*/*', :html, :turbo_stream]
・ form_for を form_withに置き換える
Deviseで生成したViewのフォームヘルパーにはform_forが用いられていますが、Rails5.1以降はform_forとform_tagはform_withに統合されたため、form_withの利用が推奨されています(form_forは非推奨)。
- views/devise/registrations/new.html.erb
- views/devise/registrations/edit.html.erb
- views/devise/sessions/new.html.erb
- views/devise/passwords/new.html.erb
- views/devise/passwords/edit.html.erb
- views/devise/confirmations/new.html.erb
registrations/new.html.erbおよびregistrations/edit.html.erbの場合は以下のように変更します。
# 変更前
= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f|
↓↓↓
# 変更後
= form_with model: @user, url: registration_path(resource_name), local: true do |f|
sessions/new.html.erbの場合は以下のように変更します。
# 変更前
= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f|
↓↓↓
# 変更後
= form_with model: @user, url: session_path(resource_name), local: true do |f|
・ sign_up時のエラーが表示されない、after_sign_out_path_forが効かない
このエラーが出なくて、動作がおかしいことに気づきました。
コントローラーを書き換えることで治るようです。
app/controllers/turbo_devise_controller.rbに追記します。
class TurboDeviseController < ApplicationController
class Responder < ActionController::Responder
def to_turbo_stream
if @default_response
@default_response.call(options.merge(formats: :html))
else
controller.render(options.merge(formats: :html))
end
rescue ActionView::MissingTemplate => error
if get?
raise error
elsif has_errors? && default_action
render rendering_options.merge(formats: :html, status: :unprocessable_entity)
else
navigation_behavior error
end
end
end
self.responder = Responder
respond_to :html, :turbo_stream
end
次に、config/initializers/devise.rbを開いて、TurboFailureAppクラスの定義を追加します。
# frozen_string_literal: true
class TurboFailureApp < Devise::FailureApp
def respond
if request_format == :turbo_stream
redirect
else
super
end
end
alias skip_format? is_navigational_format?
end
# Assuming you have not yet modified this file, each configuration option below
# (以下略)
さらに、config/initializers/devise.rbを変更します。
- # config.parent_controller = 'DeviseController'
+ config.parent_controller = 'TurboDeviseController'
- # config.warden do |manager|
- # manager.intercept_401 = false
- # manager.default_strategies(scope: :user).unshift :some_external_strategy
- # end
+ config.warden do |manager|
+ manager.failure_app = TurboFailureApp
+ end
あとは、ログアウトのリンクはmethod: :deleteではなく、data: { turbo_method: :delete }にしたり、
アカウント削除のボタンはdata: { confrim: "Are you sure?" }ではなく、data: { turbo_confirm: "Are you sure?" }とします。
まとめ
Railsを学習して間もないため、HotwireもTurboもあまり分かっていなくなぜDevice gemと相性が良くないのかが分かっていませんでしたが、
記事を書くことで原因と解決法を整理して考えることができました。
今後もrailsのバージョンやgemのバージョンによってエラーが起きることも考えられるので、原因を1つ1つ追って整理して理解を深めたいです。