アクセサが好きならばハッシュよりも構造体がよい

Rubyのハッシュのアクセスを手抜きする方法 | おごちゃんの雑文

それはいいんだけど、こいつはJSONをハッシュに変換する。データがアクセス出来れば結局は
何でもいいのだけど、深い階層を持ったデータだとアクセスがなかなか面倒臭い。Rubyで構造
付きのものを扱うのはハッシュにするのが定石らしいのだけど、階層が深くなると面倒になる
。

hash['a']['b']['c']

みたいになるのは、キー入力が厄介。プログラム中でアクセスするのは別にどうってことない
のだけど、即値があると面倒。みんなはどうしてるか知らないけど、面倒臭いなーと思ってい
た。

そこで、これを、

hash.a.b.c

のようにアクセスする方法を考えた。

method_missingはおすすめできない。既存のメソッドとたまたまかぶってしまったらハマる。

アクセサ好きならば構造体がよい。一旦根っこを構造体にしてしまえば、あとは芋蔓式に。ただし、キーが固定されている場合のみ。固定されている場合ならば、タイプミスでエラーが出てくれる。

def Struct(hash)
  Struct.new(*hash.keys).new(*hash.values.map{|s| Hash === s ? Struct(s) : s})
end
obj = Struct(:a => {:b => {:c => { :d => [:foo, :bar, :baz] }}})
obj              # => #<struct a=#<struct b=#<struct c=#<struct d=[:foo, :bar, :baz]>>>>
obj.a            # => #<struct b=#<struct c=#<struct d=[:foo, :bar, :baz]>>>
obj.a.b          # => #<struct c=#<struct d=[:foo, :bar, :baz]>>
obj.a.b.c        # => #<struct d=[:foo, :bar, :baz]>
obj.a.b.c[0]     # => [:foo, :bar, :baz]
obj.a.b.c[0][2]  # => :baz
obj.a.b.c.d[2]   # => :baz
obj.a.b.c.d = 5
obj.a.b.c.d      # => 5
obj.a.b.c        # => #<struct d=5>
obj.a.b = 77
obj              # => #<struct a=#<struct b=77>>
obj.a            # => #<struct b=77>
obj.a.b          # => 77

標準ライブラリで定義されているOpenStructはハッシュっぽい構造体で同様にmethod_missingで定義されている。おすすめできない。

require 'ostruct'
os = OpenStruct.new
os.a = 1
os.a  # => 1
os.b = 2
os    # => #<OpenStruct a=1, b=2>

Rubyの構造体は萌えるで〜