無料で使えるシステムトレードフレームワーク「Jiji」 をリリースしました!

・OANDA Trade APIを利用した、オープンソースのシステムトレードフレームワークです。
・自分だけの取引アルゴリズムで、誰でも、いますぐ、かんたんに、自動取引を開始できます。

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でインターセプトがまさにそれ。delegateAPI呼び出しは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)