【Rails】flash[:key]とflash.now[:key]の違いをざっくり説明してみた。【結論: 寿命が違う】
1.この記事の対象者
以下の方へ向けてこの記事を書いています。
- Railsのflashを勉強したことがある方
- 「flash勉強したけどよく分からん」と感じている方
- flash[:key]とflash.now[:key]の違いを知りたい方
renderメソッドとredirect_toメソッドの違いを理解していない場合、この記事の内容が難しいと感じるかもしれません。その場合は以下の記事を一読することをオススメします。
結論を早く知りたい方は4章から読むことをオススメします。
2.この記事をなぜ書いたか
この記事を書いた理由は以下の2点です。
- flash[:key]とflash.now[:key]の違いを説明している記事が少ないと感じた為。
- 自分が勉強したことをアウトプットしたい為。
間違いがある場合、コメント頂けると嬉しいです。
3.そもそもflashとは何?
結論から言うと、flashはActionDispatch::Flash::FlashHashというクラスのオブジェクトです。
言葉で説明しても分かりづらいので、ログインフォームを使って説明します。- まず、事前にDBに登録してある情報を使ってログインします。各情報を入力後にログインボタンを押すと、画面が遷移してログインできます。ログイン後のページ上部にある緑色の通知内に書かれたメッセージが、flashオブジェクトに格納されているメッセージです。(UserSessionsコントローラのcreateアクション内でflashにメッセージを代入しています。今回の場合、keyはsuccessです。また、flashに格納したメッセージを画面上に表示するために、_flash_message.html.erbを作成しました。)
class UserSessionsController < ApplicationController
def create
@user = login(params[:email], params[:password])
if @user
redirect_back_or_to(posts_path, success: I18n.t('flash_message.login_success'))
else
flash.now[:danger] = I18n.t('flash_message.login_failure')
render action: 'new'
end
end
end
<% flash.each do |message_type, message| %>
<div class="alert alert-<%= message_type %>"><%= message %></div>
<% end %>
- 画面が遷移していく間で、どのような処理が行われているかをbinding.pryを使って確認します。このWebアプリケーションのPostsコントローラ内のindexアクションにbinding.pryを記述をします。(以下のコードでは、Postsコントローラ内の他のアクションを省略しています。)
class PostsController < ApplicationController
before_action :set_post, only: %i[show edit update destroy]
# GET /posts
def index
binding.pry
@posts = Post.all
end
end
-
binding.pryをindexアクション内に書いたため、indexアクションの処理が途中でストップしています。そのため、ログインしようとすると画面が止まってしまいます。
-
サーバー側の画面を確認すると、binding.pryを書いた箇所で処理が止まっていることが分かります。また、この画面から、rubyのコードを入力することができます。コマンドを入力することによって、パラメータの現在の状態を確認することができます。
-
終わり
以上から、flashのクラスが確認できたので、flashはオブジェクトであることが分かりました。(flash.object_idと打っていただくとflashのオブジェクトidも確認できます。)
4.本題
結論から言うと、flash[:key]とflash.now[:key]の違いは寿命です。flash[:key]の方が寿命が長いです。(寿命というのは、変数の寿命のことです。寿命が長いということは、長い時間変数に情報が保持されていることを意味します。)つまり、flash[:key]の方が長い時間メッセージが格納されているのです。
なぜそのような結論になったのかを説明したいので、以下の記事の引用文を見てください。
Flash[:notice]’s message will persist to the next action and should be used when redirecting to another action via the ‘redirect_to’ method.
Flash.now[:notice]’s message will be displayed in the view your are rendering via the ‘render’ method.
この記事を読んで分かったことは、flash[:notice]に代入されたメッセージは次のアクション終了時までflash[:notice]に残り続けるということです。(この記事ではkeyをnoticeとしています。)つまり、flash[:notice]はredirect_toメソッドを使って、別のアクションへリダイレクトしたい際に使うのがベストであることが分かります。また、flash.now[:notice]に代入されたメッセージはrenderメソッドを使ってレンダリングしたビュー上に表示されることが分かります。
ただし、上の記事だけだと、flash.now[:key]の寿命が本当に短いのかが分かりません。
そこで、以下の記事の引用文を見てください。
flash persists across an action, so if you use flash for a render, the flash message will show up on the rendered page AND the next page after clicking a link. Conversely, if you run flash.now on a redirect_to, you won't even see the message because the flash.now doesn't persist across requests.
この記事を読んで分かったことは、flash[:key]をもしrenderメソッドと一緒に使った場合、フラッシュメッセージはレンダリングされたページとクリックした次のページまで表示されるということです。つまり、flash[:key]にメッセージが保持し続けられることが分かります。(ページ内のリンクをクリックしたってことはサーバー側に特定のアクションに対応したHTTPリクエストを送っています。)また、もしflash.nowをredirect_toメソッドと一緒に使った場合、フラッシュメッセージは画面上に表示されないことも分かりました。(なぜなら、flash.nowは次のHTTPリクエストをまたいで、生存することができないからです。)
以上の2つの記事から、flash[:key]とflash.now[:key]の違いは寿命であり、flash[:key]の寿命の方が長いことが分かりました。
しかし、信頼性にかける記事である為、2つ目の記事の引用文を実装してみます。
5.実装
この記事の引用文を実装項目ごとに引用文1と引用文2に分けます。
flash persists across an action, so if you use flash for a render, the flash message will show up on the rendered page AND the next page after clicking a link. Conversely, if you run flash.now on a redirect_to, you won't even see the message because the flash.now doesn't persist across requests.
- 引用文1
flash[:key]をもしrenderメソッドと一緒に使った場合、フラッシュメッセージはレンダリングされたページとクリックした次のページまで表示される
まず、上の引用文1を実装します。
- 先程のログインフォームのコントローラは以下のコードで作成されています。(newアクションやdestroyアクションは割愛してます。)
class UserSessionsController < ApplicationController
def create
@user = login(params[:email], params[:password])
if @user
redirect_back_or_to(posts_path, success: I18n.t('flash_message.login_success'))
else
flash.now[:danger] = I18n.t('flash_message.login_failure')
render action: 'new'
end
end
end
-
もし、ログインに失敗するとエラーメッセージが出ます。その後、ページ内のリンクをクリックしたり、ページを再読み込みするとエラーメッセージが消えることが分かります。
-
user_sessions_controller.rb内のflash.now[:danger]をflash[:danger]に変更します。(今回は、flash.now[:key]とflash[:key]のkeyをdangerとしています。)
class UserSessionsController < ApplicationController
def new; end
def create
@user = login(params[:email], params[:password])
if @user
redirect_back_or_to(posts_path, success: I18n.t('flash_message.login_success'))
else
flash[:danger] = I18n.t('flash_message.login_failure')
render action: 'new'
end
end
def destroy
logout
redirect_to(login_path, success: I18n.t('flash_message.logout'))
end
end
-
コードを変更後、ログイン失敗してエラーメッセージを表示させます。その後、別のページへ遷移すると、なんとエラーメッセージがまだ残っています。その後、別のページへ遷移するとエラーメッセージは消えています。つまり、flash[:key]に代入されたメッセージは最初のアクション終了後と次のアクション終了後まで、生存していることが分かります。
-
引用文1は立証できたので終了です。
次に以下の引用文2を実装します。
- 引用文2
もしflash.nowをredirect_toメソッドと一緒に使った場合、フラッシュメッセージは画面上に表示されない
- ログインするとフラッシュメッセージが画面上に表示されます。この画面から別の画面へ行くとフラッシュメッセージは消えます。
- 先程のコントローラのコードを一部変更します。redirect_back_toのオプションに、 success: I18n.t('flash_message.login_success')が指定されていますが、この記述を消します。その後、redirect_back_toが書かれた行の上にflash.now[:success]を追加します。変更後のコードを以下に記載します。(redirect_back_toメソッドはsorceryが提供するメソッドです。redirect_toメソッドと同じような挙動をします。)
class UserSessionsController < ApplicationController
def create
@user = login(params[:email], params[:password])
if @user
flash.now[:success] = I18n.t('flash_message.login_success')
redirect_back_or_to(posts_path)
else
flash[:danger] = I18n.t('flash_message.login_failure')
render action: 'new'
end
end
end
- 実際にログインしてみます。ログインに成功しているのに、フラッシュメッセージが表示されないことが分かりました。つまり、flash.now[:key]に代入されたメッセージはアクション終了までしか生存できないことが分かります。(リダイレクト系のメソッドはHTTPリクエストをもう一度送れとHTTPレスポンスを通してブラウザ側に指示します。ブラウザ側が次のHTTPリクエストを送った瞬間、flash.now[:key]に代入されたメッセージは消えます。flash.now[:key]に代入されたメッセージは次のHTTPリクエストをまたいで生存することができないからです。)
- 引用文2を立証できたので終了です。もし、flash.now[:key]に代入されたメッセージがどのタイミングで消えたのかを知りたい方はbinding.pryを使って検証することがオススメです。
6.終わり
flash[:key]とflash.now[:key]の違いは分かりづらいです。この記事を通して一人でも多くの人の問題が解決されたら幸いです。
7.参考文献
Discussion