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


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

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

[code]

    class A
      def method_missing(method, *args)
        puts "ZZZ" if method == :sleep
      end
    end
    
    a = A.new
    a.sleep
    a.send(:sleep)
    
[/code]

`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`メソッドを使うと確かめられます。

[code]

    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>'
    
    
[/code]

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

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

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




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

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

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

[code]

    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>'
    
    
[/code]

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

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

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

