__send__メソッド
以前、デジカメ写真の取り込みスクリプトで、ファイルをcpするかmvするかをオプションで切り替える処理を以下のように書いた。
if Options[:copy] || Options[:move] f = Proc.new {|src,dest,opt| case when Options[:copy] FileUtils.cp(src,dest,opt) when Options[:move] FileUtils.mv(src,dest,opt) end } Options[:events].each {|n| nikon.event(n).each {|photo| f.call(photo[0],".", {:verbose => Options[:verbose]}) } } else (省略)
条件別にProcオブジェクトを作って、それを実行するという形にしていたけど、どうももっと簡単な方法があったようだ。Rubyではメソッドを呼び出すときに、Object#sendというのが使えるらしい。sendは名前が衝突して上書きされる可能性があるので、実際には別名の__send__というのを使って、「object.__send__(:method,*args)」というように書く。アンダースコアが並びまくる名前って、だいたいおっかないもののような気もする。
method名は文字列かシンボル。
>> 1.__send__(:+, 2) => 3 >> 1.__send__("+", 2) => 3
(calc.rb) puts ARGV.shift.to_i.__send__(ARGV.shift, ARGV.shift.to_i) $ ruby calc.rb 2 + 5 7 $ ruby calc.rb 2 "*" 5 10
呼び出されるメソッドが実行時に決まるって、何か楽しげだ。もしかして、こういうところが「動的」というのだろうか。ともあれ、写真取り込みスクリプトの例では、何も大げさにProcオブジェクトなんか作らなくても、
if Options[:copy] || Options[:move] act = case when Options[:copy] then :cp when Options[:move] then :mv end Options[:events].each {|n| nikon.event(n).each {|photo| FileUtils.__send__(act,photo[0],".", {:verbose => Options[:verbose]}) } } else (省略)
という風にまとめられる。たぶん。「たぶん」、というのは、カメラを買い換えたら、このスクリプトが不要になってしまって、それきりだから……。