コード日進月歩

しんくうの技術的な小話、メモ、つれづれ、など

Railsで with_indifferent_access を使うときは一呼吸おいて考えて欲しい

ただのコラム

(この話を題材にする)環境

$ bin/rails -v
Rails 5.2.2

TL;DR

  • with_indifferent_accessは対象のハッシュのキーをシンボルでも文字列でも対応できるようにするもの
  • ただし、両方使えるようにすると懸念が増えるので、 deep_symbolize_keys もしくは deep_stringify_keys でいずれかに統一するほうがブレなくて良いと思われる。
    • 同一スコープ内(メソッドの中など)であれば、指定の方法を統一すればいい
    • 戻り値でも、メソッドの作り手側が統一をしてあげればいい

前提としてwith_indifferent_accessというメソッドはどういうメソッドか

Hashの拡張機能としてAvtiveSupportが用意しているメソッド

rails/hash_with_indifferent_access.rb at master · rails/rails · GitHub

Hashのキー値をsymbolでも文字列でも取得可能にするもの。

hash = { a: "A", "b" => "B" , c: "C"}
convert_hash = hash.with_indifferent_access

# symbolで設定したものでも、文字列でも取得ができる
pp convert_hash["a"]
# "A" 
pp convert_hash[:b]
# "B"

使う際に考えてほしいこと

with_indifferent_access の施されたハッシュを使う際に混乱が生じるポイント

このメソッドを通したほうが、 convert_hash["a"] でも convert_hash[:a] でも 取れるので一見便利だが普通のコードを書く場合は悩ましい部分ができる。

  1. このハッシュに内容をセットする側はsymbolで設定すべきか、文字列で設定すべきか悩んでしまう。
  2. 取り出す際もsymbolと文字列でどっちつかずになる可能性があり、取り出し記述の一貫性が失われる。

ただこの2つの観点はスコープ内でどっちの形式かに寄せてしまえばいいので deep_symbolize_keys もしくは deep_stringify_keys を使ってどちらかに全て変えてしまえば憂いはなくなる。そうなった場合に with_indifferent_access を使うケースは何があるかと考えると思い浮かぶのは「メソッドの戻り値などでシンボルでも文字列でも扱えるように with_indifferent_access を施す」というケースが考えられる。

ただその場合でもメソッドの作り手側がある程度どう使われて欲しいかが決めたほうが利用者側も悩まなくて済むので、あまり積極的に使う場面は思いつかない。

いまだと deep_****_key でどちらかに寄せてあげればよさそう

こう考えていくと with_indifferent_access が強く切望されるケースは少なめなため、deep_****_key で揃えるほうが処理実態もその変換後の受け取る側もシンプルに考えることができるため、「その with_indifferent_access はなんでしたいのか」を考えたほうが良さそう。

参考サイト