Subscribed unsubscribe Subscribe Subscribe

k-yamadaのブログ

プログラミングのメモ

net-sshで「NoMethodError: private method `send' called for :Net::SSH::Buffer」エラーが発生する

現象
em-websocketから、Net::SCPのstartメソッドを呼ぶと、

    Net::SCP.start("servername", "username", {:port => 22, :passphrase => passphrase}) do |scp|
      scp.download! file_path, "."
    end

NoMethodErrorが発生してしまう。

E, NoMethodError: private method `send' called for :Net::SSH::Buffer

Mainスレッドから普通に呼び出せば、問題は発生しない。



原因調査

./net-ssh/lib/net/ssh/buffer.rb

    def self.from(*args)
      raise ArgumentError, "odd number of arguments given" unless args.length % 2 == 0

      buffer = new
      0.step(args.length-1, 2) do |index|
        type = args[index]
        value = args[index+1]
        if type == :raw
          buffer.append(value.to_s)
        elsif Array === value
          buffer.send("write_#{type}", *value)
        else
          buffer.send("write_#{type}", value) # エラーが発生した箇所
        end
      end

      buffer
    end

buffer.send()メソッドは、引数で指定されたbufferクラスのメソッドを呼び出す処理。
おそらく、em-websocketでsendメソッドを再定義したため、NoMethodErrorが発生したのだと思われる。

rubyのリファレンスによると、ライブラリでは再定義された場合に備えて、sendの代わりに__send__を使うのが正しいらしい。
http://doc.okkez.net/static/192/class/Object.html

send(name, *args) -> object
send(name, *args) { .... } -> object
__send__(name, *args) -> object
__send__(name, *args) { .... } -> object
オブジェクトのメソッド name を args を引数に して呼び出し、メソッドの実行結果を返します。

ブロック付きで呼ばれたときはブロックもそのまま引き渡します。

send が再定義された場合に備えて別名 __send__ も 用意されており、ライブラリではこちらを使うべきです。また __send__ は再定義すべきではありません。

send, __send__ は、メソッドの呼び出し制限 にかかわらず任意のメソッドを呼び出せます。 クラス/メソッドの定義/呼び出し制限 も参照してください。

修正方法
./net-ssh/lib/net以下のディレクトリに対してsendを__send__に置換します。但しbuffered_io.rbは除外します。

$cd ./net-ssh/lib/net
$find . -type f -name '*.rb' | grep -v buffered_io.rb | xargs perl -i -pe 's/send\(/__send__\(/g'


■修正後のソースコード
https://github.com/k-yamada/net-ssh