LoginSignup
8
6

More than 1 year has passed since last update.

[rails7] turbo対応のdeviseの書き方

Last updated at Posted at 2022-09-07

この記事を読んで解決できる問題

  • link_toのmethod deleteが効かない問題
  • Error: Form responses must redirect to another locationの問題
  • deviseのUndefined method 'user_url'のエラー
  • after_sign_out_path_forが効かない問題
  • sign_up errorが表示できない問題

この記事を書こうとしたきっかけ

rails7から色々大きく変わって、rails6など、以前のやり方だと、ほとんどの場合はerrorが起こります。
そしてrails7とdeviseについては、今の資料ではほとんどの場合はturbo自体を無効化にして使っているため、rails7の理念に合っていないので不採用です。正直できれば自前でログイン機能を実装するのが一番ですが、なんとしてもdeviseを使いたい場合もあると思います。皆様の参考になれればいいなと思います。

rails7からの変更点はこちらの方がうまくまとめてくれていますので、何が変わったのかを詳しく知りたい方はぜひ:
https://qiita.com/ryohashimoto/items/f5382478c78f296d8291

主の環境:
OS -> ubuntu20.04LTS
rbenv -> 1.1.1
ruby -> 3.1.2p20
rails -> 7.0.3.1 (use importmap)

importmap というのはrails7からdefaultになったimport手段です(pin構文)
詳しくは以下の記事をどうぞ:
https://zenn.dev/takeyuweb/articles/996adfac0d58fb
https://techracho.bpsinc.jp/hachi8833/2022_06_29/112183


link_toのmethod deleteが効かない

rails7からturboがデフォルトになったので、従来のmethod: :postなどの書き方も合わせて
data: { turbo_method: :post }などに変更する必要がある

# delete methodの例
link_to "表示させたい文字", path, data: { turbo_method: :delete }
# confirm機能を利用したい場合は以下のように書けます
link_to "表示させたい文字", path, data: { turbo_method: :delete, turbo_confirm: "ほんとに削除しますか?" }

turbo streamのdeleteメソッドを使う時は、必ずrenderやredirectにstatus: 303(see other)を返してください。返さないとstream自体が止まっていないため、他のデータも一緒に削除されます。非常に危険です。

# redirect_toを使う場合のstatusの返し方
redirect_to root_path, status: 303 # この2行の働きは全く一緒です
redirect_to root_path, status: :see_other # この2行の働きは全く一緒です

詳しく知りたい方はこちらのリンクへどうぞ:
https://github.com/hotwired/turbo-rails


Error: Form responses must redirect to another location

上と同様、turboを使うと303statusが必須なので、statusで303を返してあげれば解決します。


Undefined method 'user_url'のエラー

deviseではまだturboに対応したいないので、手動でこちらのファイルを変更する必要があります。
ファイル内でコメントアウトされているので、ctrl+fで検索していただければ探せると思います

config/initializers/devise.rb
- config.navigational_formats = ['*/*', :html]
+ config.navigational_formats = ['*/*', :html, :turbo_stream]

こちらのissueもご参照ください:
https://github.com/heartcombo/devise/issues/5439


after_sign_out_path_forが効かない

  • 原因
    • sign outはデフォルトではdelete methodを受け取ってsessionを終了しているため、turboになったrails7では、delete methodのredirect_toは必ずstatus 303を受け取らないといけない。しかしdeviseでは303statusを返してない。
  • 解決法
    1. deleteを受け取ってsessionを終了するのをgetを受け取るように変更(非推奨)

      config/initializers/devise.rb
      - config.sign_out_via = :delete
      + config.sign_out_via = :get
      

      変更した後のsign outはgetを受け取ってsign outするようになったので、元々のsign outのlink_toのdeleteを消します

      - link_to "Log out", destroy_user_session_path,  data: { turbo_method: :delete }
      + link_to "Log out", destroy_user_session_path
      

      しかしこうなるとcsrfの脆弱性の可能性がありますので非推奨です

    2. delete methodをオーバーライドして303statusを返すようにする
      オーバーライドするにはまずdeviseでカスタムcontrollerを指定する必要があります

      config/routes.rb
      devise_for :users, controllers: { sessions: "sessions" }
      

      これでdeviseのsessionsの機能だけデフォルトのcontrollerではなく、カスタマイズできるapp/controllers/sessions_controller.rbを参照するようになりました。では実際にこのcontrollerを作って編集してオーバーライドしましょう

      app/controllers/sessions_controller.rb
      class SessionsController < Devise::SessionsController
          def respond_to_on_destroy
              respond_to do |format|
                  format.all { head :no_content }
                  format.any(*navigational_formats) { redirect_to after_sign_out_path_for(resource_name), status: 303 } # ここで303statusを返します
              end
          end
      end
      

      このコードでは、deviseのSessionControllerの中のrespond_to_on_destroyメソッドをオーバーライト(上書き)しています。元々303statusを返してないredirect_toに303を付けました。これでlink_toはdeleteのままturbo_methodを使えるようになりました

deviseのカスタムcontrollerについて

deviseでは普通、gem installされたデフォルトのcontrollerを参照しています。このcontrollerのファイルはgithub上のapp/controllers/deviseフォルダー内で見れます。
https://github.com/heartcombo/devise

deviseをinstallしたlocal環境でも同じコードが見れます。場所はgem install先のgemsフォルダーの中にあります。
gemのinstall先はgem environmentコマンドで見れます。出力の中のINSTALLATION DIRECTORYに記載されています。
このINSTALLATION DIRECTORYに続いてgems/devise-x.x.x/app/controllers/deviseの中に、github上と同じコードが入っている。

先のオーバーライドがやっていることとは、この中のsessions_controller.rbというファイルの中のrespond_to_on_destroyを上書きしています。ctrl+fで検索すれば元コードが見つかるはずです。

deviseのカスタムcontrollerについて詳しく知りたい方こちらの記事をどうぞ:
https://toarurecipes.com/devise-customize/
https://github.com/heartcombo/devise#configuring-controllers


sign_up errorが表示できない

上と同じく、sing upに関するメソッドをオーバーライドして、303 statusを返してあげれば解決できます。

config/routes.rb
devise_for :users, controllers: { registrations: "registrations" } # 上はsessionsを指定したが、今回はregistrationsを指定する
devise_for :users, controllers: { sessions: "sessions", registrations: "registrations" } # 同時指定することもできます

これでdeviseがapp/controllers/registrations_controller.rbを参照してくれるようになったので、実際にファイルを作ってコードを書きましょう

app/controllers/registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
    def create
        build_resource(sign_up_params)
    
        resource.save
        yield resource if block_given?
        if resource.persisted?
            if resource.active_for_authentication?
                set_flash_message! :notice, :signed_up
                sign_up(resource_name, resource)
                respond_with resource, location: after_sign_up_path_for(resource)
            else
                set_flash_message! :notice, :"signed_up_but_#{resource.inactive_message}"
                expire_data_after_sign_in!
                respond_with resource, location: after_inactive_sign_up_path_for(resource)
            end
        else
            clean_up_passwords resource
            set_minimum_password_length
            respond_with resource, status: 303 # 今回は失敗するときのrespond_withにerror出したいので、ここで303 statusを追加
        end
    end
end

実際にどのメソッドをオーバーライドすれば良いかについては、状況に応じて、コードをじっくり読んで、deviseの挙動を知った上で判断いたしましょう。


以上です

8
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
6