高木のブログ

【Ruby】ネストしたStructをスマートに書きたい

2021/06/11

response.data.solution.status #=> 'ACTIVE'

テストでこんな感じのモックが欲しくて、ネストしたStructをスマートに書く方法がないか調べた

response = Struct.new(:data).new(Struct.new(:solution).new(Struct.new(:status).new('ACTIVE')))
#=> #<struct data=#<struct solution=#<struct status="ACTIVE">>>

response.data.solution.status
#=> "ACTIVE"

普通に書いたら、なにこれひどいなって感じになった

solution = Struct.new(:status).new('ACTIVE')
data = Struct.new(:solution).new(solution)
response = Struct.new(:data).new(data)

変数に入れたら一応読みやすくはなるけど、スマートではない

調査結果

3種類の書き方が見つかった

1. 標準ライブラリ(ostruct)を使う

require 'ostruct'

response = OpenStruct.new({ data: { solution: { status: 'ACTIVE' } } })
#=> #<OpenStruct data={:solution=>{:status=>"ACTIVE"}}>

response.data[:solution][:status]
#=> "ACTIVE"

スマートに書けるけど、2階層以降はHashになってる

2. Gem(recursive-open-struct)を追加する

aetherknight/recursive-open-struct - GitHub

require 'recursive-open-struct'

response = RecursiveOpenStruct.new({ data: { solution: { status: 'ACTIVE' } } })
#=> #<RecursiveOpenStruct data={:solution=>{:status=>"ACTIVE"}}>

response.data.solution.status
#=> "ACTIVE"

求めてたスマートさで書けた
ただ、Gemを追加しないといけないのが難点

3. JSON.parseとostructを使う

require 'json'
require 'ostruct'

response = JSON.parse({ data: { solution: { status: 'ACTIVE' } } }.to_json, object_class: OpenStruct)
#=> #<OpenStruct data=#<OpenStruct solution=#<OpenStruct status="ACTIVE">>>

response.data.solution.status
#=> "ACTIVE"

Gemを追加しないで書けるけど、果たしてこれはスマートなのか?ってなった

結論

実用的なのはこの2つ

    1. Gem(recursive-open-struct)を追加する
    1. JSON.parseとostructを使う

Gemを追加しても問題ないのならrecursive-open-structを使うのがいいのかな

Hashクラスにモンキーパッチを当ててやるのもありかもしれない

require 'json'
require 'ostruct'

class Hash
  def to_o
    JSON.parse to_json, object_class: OpenStruct
  end
end

response = { data: { solution: { status: 'ACTIVE' } } }.to_o
#=> #<OpenStruct data=#<OpenStruct solution=#<OpenStruct status="ACTIVE">>>

response.data.solution.status
#=> "ACTIVE"

Written by ytkg, Twitter, GitHub

Pixela