sendメソッドについて

RSpecを用いたServiceクラスのテストにて、 __send__ メソッドが使われていました。

知らないメソッドだったので、調査してみました。

Ruby 2.5.0リファレンスマニュアルには、send__send__ について下記のように記載されています。

オブジェクトのメソッド name を args を引数に して呼び出し、メソッドの実行結果を返します。

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

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

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

[PARAM] name:

 文字列かSymbol で指定するメソッド名です。

[PARAM] args:

 呼び出すメソッドに渡す引数です。

利用方法

まずは、基本的な利用方法をみていきましょう

class User
  def name1
    "Taro"
  end

  def name2
    "Hanako"
  end
end

user1 = User.new.send("name1")
user2 = User.new.send(:name2)
p user1
p user2

Userクラスのオブジェクトについて send メソッドを呼び出すと、指定したインスタンスメソッドが実行されることがわかります。

次に、クラスメソッドを定義するとどうなるのでしょうか?

class User
  def self.name3
    "Tom"
  end
end

p User.methods.grep /name/
user3 = User.send(:name3)
p user3

クラスメソッドに対しても呼び出すことができますね。

引数を渡すことができるかを確認しましょう。

class User

  def self.name4 name4=nil
    if name4.nil?
      p "You pass class method but value is nil"
    else
      p name4
    end
  end
end

user4 = User.send(:name4)
user4 = User.send(:name4, "Emma")

メソッドの呼び出し制限 にかかわらず任意のメソッドを呼び出せます

Privateメソッドも呼び出すことができるのでしょうか?

class User
  private
  def private_name
    "殿下"
  end
end

p User.new.send(:private_name)

無事、秘密の名前を出力できたようです。

利用場面

さて、send メソッドの利用場面について考えてみましょう。

一つは、先ほど確認したPrivateメソッドも呼び出せることです。これは、テストで重宝するでしょう。

次に、メソッドを動的に決定できる点ではないでしょうか?

例えば、以下のように例があります

class User
  def dynamic_dispatch(dyName)
    send(dyName)
  end

  def user_name
    "クッパ"
  end

  def house_name
    "クッパ城"
  end

  def enemy_name
    "マリオ"
  end
end

user = User.new
["user", "house", "enemy"].each do |dyName|
    p user.dynamic_dispatch("#{dyName}_name")
end

このように、動的にメソッドを呼び出すことを 動的ディスパッチ と呼びます。

以上で、send の調査は終了です。

ご静聴ありがとうございました。