Pythonのdecorator は格好いいよね。Rubyでこんな感じのものを作れないかと何回か考えたものの、上手いAPIを思いつかずにいた。
このたび、 technohippyさんの挑戦 に刺激されて書いてみた。
仕様としてはPythonに合わせて、呼び出し可能オブジェクトを返す関数としてdecoratorを作成する。そして、declareというメソッドにdecoratorを渡すと、ブロック内で定義されたメソッドはdecoratorで修飾される。
例
class Module def type(name, types) before(name) do |name, args| args.zip(types).each_with_index do |(arg, type), i| raise TypeError, "#{i}th argument #{arg} should be #{type}" unless arg.instance_of?(type) end end end def always_one(name) proc{|*args| 1 } end end class Calc declare :type, [Float, Float] do def add(x,y); x+y end def sub(x,y); x-y end def mul(x,y); x*y end end declare :always_one do def div(x,y); x/y end end end calc = Calc.new p calc.add(1.0, 2.0) p.calc.div(1.0, 2.0) #=> 1 p calc.sub(1.0, 2) #=> TypeError
ポイント
- Rubyなんだからブロック使おうよ、という。Module#privateみたいに頑張ってブロックなしにするのも考えたけど、労多くして実り少なしだよね。
- Ruby 1.8.7以降で動作する。
- 特異メソッドに非対応
- イテレータは修飾できない。define_methodにブロックを渡した場合、呼び出し時にメソッド本体へブロックを渡す方法がないので。Ruby 1.9だと頑張ればできるんだけど。
- UnboundMethod経由のメソッドすり替えを試せて楽しかった。
- もうちょっと真面目に書いてgem化したら誰か使う?