概要
Little Things: Introspecting Block Parameters という記事で紹介されていた Hash の拡張がエレガントでかつ便利だったのでご紹介します。
利用方法
まず、以下のコードを実行して Hash#using を実装します。
(少し改変していますが、上のリンクに記載されているコードと内容は同じです。)
class Hash
include(
Module.new do
def using(&block)
values = block.parameters.map { |_, name| self[name] }
block.call(*values)
end
end
)
end
次に、実際に Hash#using を使ったコードを実行してみます。
students = [{ name: 'ヒロ', from: '山形県', room: 101 }, { name: '沙英', room: 102 }]
students.each do |student|
description = student.using do |name, from, room|
"#{name}の出身地は#{from || '不明'}です。ひだまり荘の #{room} 号室に住んでいます。"
end
puts(description)
end
ヒロの出身地は山形県です。ひだまり荘の 101 号室に住んでいます。
沙英の出身地は不明です。ひだまり荘の 102 号室に住んでいます。
このように Hash が持つ情報を使った処理をサクッと実装したい時に使えると思います。
普段、例えば Struct を使って、
Student = Struct.new(:name, :from, :room) do
def description
"#{name}の出身地は#{from || '不明'}です。ひだまり荘の #{room} 号室に住んでいます。"
end
end
students = [{ name: 'ヒロ', from: '山形県', room: 101 }, { name: '沙英', room: 102 }]
students.each do |student|
arguments = student.values_at(*Student.members) # ["ヒロ", "山形県", 101] or ["沙英", nil, 102]
description = Student.new(*arguments).description
puts(description)
end
と書いていたのを Hash だけで書けるようになるので便利だなと感じました。
Proc#parameters について
Proc#parameters は Proc オブジェクトの引数を返すメソッドです。
procedure = lambda { |yuno, miyako = '宮ちゃん', *others| }
procedure.parameters #=> [[:req, :yuno], [:opt, :miyako], [:rest, :others]]
このように引数名を取得できるので、上で紹介した手法は引数名を Hash の key と一致させることで実現していたのですね。この引数名をうまく使ったトリックが目から鱗でエレガントに感じました。