QA@IT

ネストしたハッシュのデフォルト値設定

1845 PV

link_to をラップしたヘルパーメソッドで、デフォルト値を以下のように設定していたのですが、
Rails 3.2.6 で :confirm が deprecated になったので、 data: { confirm: ... } に書き換えたいです。

    html_options = {
      method: :delete,
      confirm: t("form.confirm"),
    }.merge(html_options)

たとえば

    html_options = {
      method: :delete,
      data: { confirm: t("form.confirm"), },
    }.merge(html_options)

だと data があって confirm がないときに困ります。

    html_options = {
      method: :delete,
    }.merge(html_options)
    html_options[:data] ||= {}
    html_options[:data][:confirm] ||= t("form.confirm")

だと html_options = {data: { 'confirm' => message } } のようにキーが文字列のときに困りそうです。

元の Hash#merge を使っている部分もキーが文字列の場合に問題がありそうですが、
どう書き換えるのが良さそうでしょうか?

回答

Hash#deep_mergeあるいはHash#deep_merge!で再帰マージしてはどうでしょうか。

https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/hash/deep_merge.rb

追記

なお、文字列かシンボルかという問題は、paramsのようにActiveSupport::HashWithIndifferentAccessを経由してあげれば、意識しなくてよくなります。

{ :a => 1 }.with_indifferent_access["a"] # => 1

余談ですが、シンボルテーブルは静的な上限がありGCも行われないため、ユーザが入力する信頼できない文字列をto_symしてるコードがあると割と簡単にDoS攻撃ができたりします。ユーザが入力する信頼できない文字列をハッシュのキーとして使う場面もありうるため、HashWithIndifferentAccessでは内部的にはシンボルではなく文字列に正規化して持っています。

編集 履歴 (1)
ウォッチ

この質問への回答やコメントをメールでお知らせします。