devise サインアウトのリダイレクト先設定時にエラーのが起きてしまう原因について
初めに
こんにちは。DMM WEBCAMP Advent Calendar 2021 8日目を担当します@Hbk__17です!
普段受講生さんに教えていた際に、よく受講生さんがつまづいていた箇所で「deviseを使用した際のログアウトの条件分岐でエラーが出てしまい直せません」といったような質問をよく頂いていたのですが、しっかり解説をしている記事を見かけなかったので、今回私の記事ではなぜこのようなエラーが出現するのかについてと、どのように直せばいいのかを解説していければと思っております!先に早くエラーを治したいという方はこちらをご参照ください。
エラーについて
今回主に解説を行うエラー文は、deviseを使用した際のログアウトを設定した際に起こるこのエラー↓について解説したいと思います!
エラーの和訳
そうしましたら、cannot redirect_to nil!というエラー文の和訳から始めていきたいと思います!
cannotはできませんという意味になり、redirect_toは画面の遷移をするメソッドになります、そして最後のnilに関してはよくエラー文で見かけることが多いかなと思いますが空であったり、何もないと説明されることが多いですね。
つまり今回のエラー分の意味としては、リダイレクト先が設定されていませんよ!と怒られていることがわかりますね!
エラーの原因の解説
それではエラー(文)の意味が理解できたところで、application_controllerでサインアウト先を設定しているのにcannot redirect_to nil!と怒られてしまうのかを見ていきましょう!
実際のコード↓
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
def after_sign_in_path_for(resource)
case resource
when Admin
admin_items_path
when EndUser
mypage_customers_path
end
end
def after_sign_out_path_for(resource)
case resource
when Admin
admin_items_path
when EndUser
mypage_customers_path
end
end
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:last_name, :first_name, :last_name_kana, :first_name_kana, :postal_code, :adress, :phone_number, :email])
end
end
実際にコードを見ていくと、after_sign_out_pathという設定がありますね!設定があるのにも関わらずなぜ反応しないのでしょうか?実際にエラーを解消する際に便利なのがデバックツールでしたね。
今回は、サインアウトをした際にresourceという引数を受け取りその値を条件分岐をして管理者と会員でサインアウト先を変更しているので、resourceの値をデバックツールを使用して中身を見てあげましょう!
今回デバックツールとしてgem 'pry-rails'
を使用したいと思います!
def after_sign_out_path_for(resource)
binding.pry
case resource
when Admin
admin_items_path
when EndUser
mypage_customers_path
end
end
上記のように、case文の上にbinding.pry
を挿入してあげましょう!
デバックツールも設置できたところで、実際にresourceの中身を見てあげましょう。
デバックツールで、resourceを出力してみると:end_userという値が出力されました。
そこで、疑問に思うのがサインインの時はうまく行っていたのがなぜサインアウトだとうまくいかないのだろうという疑問が生まれると思います。そこでサインインの場合も、デバックツールで止めて中身を見てあげましょう!
すると、先頭にモデル名、そしてモデルの中身のレコードが出力されているのが確認できるかと思います!ここで、サインインの際には、resourceとしてモデル名と中身が受け取れていて、サインアウトの際には:がついたモデル名が返ってきているのがわかるかと思います。ここでわかるのは、そもそものresourceとして受け取っているものが違うためアプリケーションコントローラーで記述している条件分岐がうまくいかないのだとわかります。
:←これは何?
今回の、サインアウトした時に渡されている:モデル名は普通のモデル名と何が違うの?という疑問が出てくるのではないかなと思います!実際に、:モデル名は普通のモデルとは違うものになります。では、:←これは何かというとシンボルと呼ばれるものになります。シンボルとは、モデルなどを整数として管理を行うものになります。見た目上は、文字列ですが、rubyの裏側では整数として管理をされているらしいです。笑
シンボルを使用する理由としては、メモリとして軽いため処理の速度が速くなるらしいです。
こちらの記事がかなりシンボルの理解をするのに理解が深まったので参考にしてみるのもいいかもしれないです!
シンボルのついた:end_userはモデルとは別物になり、モデルの中身を参照することはできないものとなります。そのため、上記のアプリケーションコントローラーは、モデルに対して条件分岐をしていたため、どの条件にも当てはまらずリダイレクト先が設定されていませんよ!と怒られてしまったというわけです。
なぜシンボルの値が返ってきてしまう?
ではなぜ、シンボルのついたモデル名がサインアウトした際に返されるのでしょう?
今回は、デバイスの仕様書であるdeviseDocumentを見てみましょう。
devise document↓
このスクリーンショットの、コメントアウト部分に記述があるようにサインアウトの際にはシンボルを返すという仕様にしているようです。
今回のエラーの解決方法
解説が長くなりましたが、ここからはどのようにエラーを解消すればいいのかを解説します!
先ほどの解説で、サインインの際はresourceとしてモデルが返却されますが、サインアウトの際はモデル名のシンボルが帰ってくるということをお話ししました。つまり、エラーが出ている状態のコードではresourceの中身がモデルであると考えコードを記述していたので、resourceの中身がシンボルであることを念頭において条件分岐を記述してあげればコードのエラーが治るのではないかなと思います!具体的には、
def after_sign_out_path_for(resource)
case resource
when :admin
admin_items_path
when :end_user
mypage_customers_path
end
end
このように変更をしてあげることによって、resourceの中身と条件分岐をしているものが一致をするのでエラーなくサインアウト先を状況に応じて変更することができるようになると思います。
if分で書くとするならば、このようになるかなと思います!↓
def after_sign_out_path_for(resource)
if resource == :admin
admin_items_path
elsif resource == :end_user
mypage_customers_path
end
end
最後に
ここまで、railsのdeviseのサインアウトについて解説を行ってきましたがいかがでしたか?
少しでもこの記事を読んでくださった方の理解が深まれば幸いです!
今回この記事を書いてみて、コードを書く際の些細ななぜという疑問を大切に勉強していこうと思いました!
最後まで読んでくださってありがとうございました〜〜〜!