method_missingでsleepメソッドを呼んだ時にフリーズする理由

投稿者: Anonymous

こんにちは。
自分で一生懸命調べれば解決できるかも知れませんが、
ここで質問した方が役に立つお話が聞けるかも知れないと思って質問します。

Rubyで下のようなコードを実行すると

class A
  def method_missing(method, *args)
    puts "ZZZ" if method == :sleep
  end
end

a = A.new
a.sleep
a.send(:sleep)

a.sleep は期待通り “ZZZ” を出力するのですが、次の行の
a.send(:sleep) はフリーズします。

どうしてこうなるのでしょうか?

解決

send(:sleep)だと、privateメソッドの Kernel.#sleepを呼べるためです。
https://docs.ruby-lang.org/ja/latest/method/Kernel/m/sleep.html

Aクラスのインスタンスにはsleepメソッドはprivateメソッドとして存在します。
これは、Kernelモジュールに定義されたメソッドはprivateメソッドとしてAクラスにも定義されるためです。

これは例えばmethodメソッドを使うと確かめられます。

class A
  def method_missing(method, *args)
    puts "ZZZ" if method == :sleep
  end
end

a = A.new

p a.method(:sleep)
# => #<Method: A(Kernel)#sleep(*)>

p a.public_method(:sleep)
# => -:10:in `public_method': method `sleep' for class `A' is  private (NameError)
#   from -:10:in `<main>'

そして、a.sleepの形式でのメソッド呼び出しはpublicメソッドしか呼び出せないため、privateメソッドであるKernel.#sleepを呼び出せずmethod_missingに処理が移ります。

対してa.send(:sleep)はprivateメソッドでも呼び出せるので、method_missingが呼ばれずにKernel.#sleepが呼ばれます。

sendpublic_sendに変えるとmethod_missingを通るようになって、public_sendでも”ZZZ”が表示されるようになると思います。

回答者: Anonymous

Leave a Reply

Your email address will not be published. Required fields are marked *