rubyのsendメソッド

2022.07.15

sendメソッドとは

オブジェクトのメソッド name を args を引数にして呼び出し、メソッドの実行結果を返します。ブロック付きで呼ばれたときはブロックもそのまま引き渡します

Ruby 3.1 リファレンスマニュアル

メソッドを引数を持って呼び出せる。似たものにpublic_methodがあるが違いはprivateメソッドを呼び出せるかの違い。

特徴

Animalsコントローラーにあるプライベートメソッドpri_aをテストしたい時

# コントローラー
class AnimalsController < ApplicationController
  private
  def pri_a(x)
    p "プライベート#{x}"
  end
end

# テスト
test "pri_aの返り値がプライベート3であること" do
  animal = AnimalsController.new
  result = animal.send(:pri_a, 3)
  assert_equal "プライベート3", result 
end

カラムがnilであればエラーが出ることを確認したい時

# モデル
class Animal
  with_options presence: true do
    validates :name
    validates :age
    validates :from
  end
end

# テスト
test "カラムがnilであれば保存できないこと" do
  animal = Animal.new
  %w[name age from].each do |colmun|
    animal.send("#{column}=", nil)
    assert_not animal.valid?
  end 
end

#以下と同じ意味
test "nameがnilであれば保存できないこと" do
  animal = Animal.new
  animal.name = nil
  assert_not animal.valid?
end

test "ageがnilであれば保存できないこと" do
  animal = Animal.new
  animal.age = nil
  assert_not animal.valid?
end

test "fromがnilであれば保存できないこと" do
  animal = Animal.new
  animal.from = nil
  assert_not animal.valid?
end

条件によって呼び出すメソッドを変更したい時。asiaレコードやeuropeレコードを追加しても呼び出し方は変わらない。

class Record
  attr_accessor :region

  def world_record
    # 処理
  end

  def japan_record
    #処理
  end
end

record = Record.new
record.region = "world"
if record.region == "world"
  record.send("#{record.region}_record")
elsif record.region == "japan"
  record.send("#{record.region}_record")
else
  raise "not correct region"
end
  

まとめ

プライベートメソッドを直接叩いてテストできるのは便利だと感じた。また、条件によって呼び出すメソッドを変えたり(動的)したい場合も使えそう。

ただし、ユーザーが入力した(外部から来た)値等をsendメソッドで使うことは脆弱性の面から避けた方が良いと思いました。