今回はメソッドに関するRubyのメタプログラミング技法についてまとめていきたいと思います。
JavaやC言語などの静的言語では、コンパイル時にすべてのメソッドに対して対応するメソッドが存在するかどうかを評価します。
存在しなければ、コンパイルエラーとなりバイナリファイルが作成されません。
一方Rubyのような動的言語では、この制約がなく、メソッドは呼ばれるときに存在すれば良く、そのメソッドをどのような型のインスタンスが呼んでいるかすら気にしません。
アヒルのように歩き、アヒルのように鳴くのなら、それはアヒルなのです。
このように、Rubyはメソッドの定義や呼び出しに対してかなり寛容です。
おさらいとして普通のメソッド呼び出し
class Awesome def hello 'World!' end end awesome = Awesome.new puts awesome.hello #-> World!
このように、普通のメソッド呼び出しは.演算子を用いて呼び出します。
ドット記法でなく、Object#sendを使ってメソッドを実行する
Object.send(:method,args)でもメソッドを呼び出すことが出来ます。
class Awesome def hello(word) word end private def concealed 'You Destroy Object Oriented' end end awesome = Awesome.new puts awesome.send(:hello,'World!') #-> World!
__send__はsendのエイリアスメソッド
また、sendというメソッド名は他のメソッド名と被りやすいため、sendというメソッドも用意されています。これはsendのエイリアスメソッドなので、同じ挙動を示します。
puts awesome.__send__(:hello,'World!') #-> World!
privateメソッドも呼び出せる
sendメソッドでは、privateメソッドも呼び出すことが出来ます。
puts awesome.__send__ :concealed #-> You Destroy Object Oriented
プライベートメソッドのRSpecテストを行うときに、そこそこ役に立つ技法です。
defキーワードを使わずにメソッドを動的に定義する
Module#define_methodを使えば、メソッドをその場で定義できます。それにはメソッド名とブロックを渡す必要があり、ブロックがメソッドの本体になります。
class Incrementer define_method :increment do |number| number+1 end end puts Incrementer.new.increment(999) #-> 1000
インスタンスのメソッド一覧を取得する
インスタンスのClass名が.classで取得できるように、インスタンスのメソッド名も.methodsで取得することができます。
puts Incrementer.new.methods =>メソッドがずらずらっと表示される
method_missingをオーバーライドする(ゴーストメソッド)
class EmptyObject def method_missing(method,*args) super unless method==:walk puts "I'm walking..." end end EmptyObject.new.walk # メソッドないけど歩かせてみよう # -> "I'm walking..."
メソッドを定義せずに、あたかも歩くことができるかのようにメソッドに振る舞わせることができました。
method_missingはかなりコアな機能であり、可能であれば動的定義を使ったほうがいいですが、最終奥義がmethod_missingのオーバーライドであるということは頭の片隅に置いておいたほうがよいでしょう。
詳しく学びたい方は、「メタプログラミングRuby」を読んでみると良いでしょう、