Method#callとObject#send
があります。どちらも同じことが可能ですが、指定されたメソッドが存在しなかった場合の動作が違います。
方法 | メソッドが存在しなかった場合の動作 |
---|---|
Method#call | エラー(メソッドが未定義) |
Object#send | Object#method_missingが実行される。 |
class Kitten def initialize( name ) @name = name end def meow ( count ) print @name + " : " count.times { |i| print "meow!" } print "\n" end def method_missing ( name, *arg ) print "method " + name.to_s + " is not found." end end mii = Kitten.new( "mii" ) # 存在するメソッドを呼び出す。 mii.send(:meow, 3) mii.method( :meow ).call( 3 ) # 存在しないメソッドを実行 / 後者はエラーになる mii.send(:undefined, 3) mii.method( :undefined ).call( 3 )
実行結果です。
mii : meow!meow!meow! mii : meow!meow!meow! method undefined is not found. xxx/method.rb:24:in `method': undefined method `undefined' for class `Kitten' (NameError) from xxx/method.rb:24
両者の違いを考えると納得感がある(というか当たり前?)挙動ですが、Object#method_missingを利用しているクラスがある場合、注意が必要です。
例
以前に書いたmethod_missingでインターセプトがまさにそれ。delegateのAPI呼び出しはObject#sendで行う必要があります。
Method#callを使っていると、インターセプタを2重に適用した場合にエラーになります。
class EchoInterceptor def initialize(delegate) @delegate = delegate end def method_missing( name, *args ) begin # メソッド呼び出しの前に実行する処理 print "method " << name.to_s << " start.\n" return @delegate.method(name).call(*args) ensure # メソッド呼び出しの後に実行する処理 print "method " << name.to_s << " end.\n" end end end class Tora def meow print "meow!\n" end end tora = Tora.new tora = EchoInterceptor.new(EchoInterceptor.new(tora)) # インターセプタを2重に適用 tora.meow
実行結果です。
xxx/method.rb:35:in `method': undefined method `meow' for class `EchoInterceptor' (NameError) from xxx/method.rb:35:in `method_missing' from xxx/method.rb:51
Object#sendを使うと期待通りの動作をします。
#return @delegate.method(name).call(*args) return @delegate.send( name, *args)