ねんがんのObject#instance_execをてにいれたぞ! 〜Object#instance_evalに引数が渡せるようになった〜
Object#instance_exec欲しいなあ - http://rubikitch.com/に移転しました
instance_execキターーーーーー(゜∀゜)ーーーーーー!!
たった今、Ruby 1.8.7-preview4がリリースされた。
念願叶ってやっとObject#instance_execがbackportされたのだ!つまり、Ruby 1.8.7でinstance_execが使えるということだ!!
instance_execとは、Object#instance_evalに引数が渡せるようになったもの。
ついでに、関数的メソッド呼び出しで使用するとLispのletみたいになる。
Ruby 1.8.6以前でも実装することはできたが、かなり大変だった。
instance_execを使うと、コードの可読性を上げることができるので使ってみよう。
RUBY_VERSION # => "1.8.7" RUBY_RELEASE_DATE # => "2008-05-26" s = Struct.new(:foo, :bar).new(6, 8) t = "hoge" s.instance_exec(t) do |x| x # => "hoge" self # => #<struct #<Class:0xb7e0bb24> foo=6, bar=8> foo # => 6 end instance_exec(s, t) do |x, y| x # => #<struct #<Class:0xb7e0bb24> foo=6, bar=8> y # => "hoge" end
Ruby 1.8.7で使えるようになったRuby 1.9のメソッドたち - http://rubikitch.com/に移転しました←テストケースを更新!他の使い方はこちら。
使用例:複数の似たようなメソッドを定義する Module#def_each
Jay Fieldsのブログからネタを拝借。
似たようなメソッドを定義することがある。そのときに、コピペで定義するのは面倒だし、DRY原則に反する。
こんな感じでクラス定義の中にdefine_methodを入れてもいいのだが、いささか可読性(readability)に欠ける。サンプルは信号機クラスだ。
class TrafficSignal [:green, :yellow, :red].each do |meth| define_method(meth) do "Signal turned #{meth}." end end end
そこで、メソッド名をブロック引数にとるようにして、ブロック内容をメソッド本体にする Module#def_each を定義する。
「黒魔術を具象クラスの外に追い出す」ことで、可読性は格段と上がる。
class Module def def_each(*methods, &block) methods.each do |meth| define_method(meth) do instance_exec(meth, &block) end end end end class TrafficSignal def_each :green, :yellow, :red do |c| "Signal turned #{c}." end end ts = TrafficSignal.new ts.green # => "Signal turned green." ts.yellow # => "Signal turned yellow." ts.red # => "Signal turned red."
日本語版はこんなの。
class JapaneseTrafficSignal en_ja_table = {:green => "青", :yellow => "黄", :red => "赤"} def_each :green, :yellow, :red do |c| "#{en_ja_table[c]}信号" end end ts = JapaneseTrafficSignal.new ts.green # => "青信号" ts.yellow # => "黄信号" ts.red # => "赤信号"
[2008/05/26]追記
なぜかModule#module_execはbackportされていないね。