【Rails】flash[:notice]とか書くから、flashのキーはシンボルだと思ってたら「文字列」だった
Railsでの開発中に遭遇した不具合について、原因と解決策を投稿します。
環境
背景
flashを使って以下のようにメッセージを表示していました。
<% if flash[:notice] %> <div class="alert alert-primary"> <%= flash[:notice] %> </div> <% end %>
flash[:notice]だけではなく、flash[:alert]を受け取った場合もメッセージを表示します。
<% if flash[:notice] %> <div class="alert alert-primary"> <%= flash[:notice] %> </div> <% end %> <% if flash[:alert] %> <div class="alert alert-danger"> <%= flash[:alert] %> </div> <% end %>
しかし、上記のように種類が増えるにつれてif文を増やしていくのは、少し冗長的なので、case文を使ってもう少しスッキリさせてみます。
<% flash.each do |type, message| %> <% case type when :notice %> <div class="alert alert-primary"> <%= message %> </div> <% when :error %> <div class="alert alert-danger"> <%= message %> </div> <% end %> <% end %>
不具合の内容
既述の case文を使った書き方にすると、なぜかflashが画面に表示されなくなってしまいました。
不具合の原因
flashはflash[:notice] = "message"
のように指定するため、flashのキーはシンボルかと思っていました。
しかし、実際に確認してみると、キーは「文字列」になっていることがわかります。
{"alert"=>"An error occurred: Quantity must be greater than 0"}
そのため、シンボルで指定すると条件に一致しません。
これはHashWithIndifferentAccess
というRailsの機能で、シンボルと文字列の両方をキーとして受け取れるが、内部的にはそれを「文字列」として保存する仕様によるものだと思います。
解決策
この問題を解決するためには、case文でflashのキーを指定する際に「文字列」で指定する必要があります。
<% flash.each do |key, message| %> <% case key when "notice" %> <div class="alert alert-primary"> <%= message %> </div> <% when "alert" %> <div class="alert alert-danger"> <%= message %> </div> <% end %> <% end %>
なお、このcase文はヘルパーメソッドとして、外部で定義した方がよりビューが綺麗になりそうです。
# app/helpers/application_helper.rb def flash_class(type) case type when "notice" then "alert alert-primary" when "alert" then "alert alert-danger" # 他のflashタイプもここに追加できます end end
<% flash.each do |type, message| %> <div class="<%= flash_class(type) %>"> <%= message %> </div> <% end %>