# [Ruby]配列に格納したメソッドをObject#sendで実行する

2019/11/05

諸事情あって複数のメソッドを一つずつ実行する必要がありました。いいやり方を見つけるのに時間がかかった & これがベストかわからないためメモしておきます。

# Object#send

obj.send(name, [arg, ...])
1

レシーバの持っているメソッドを呼び出すメソッド。 第一引数nameで呼び出したいメソッドをシンボルか文字列で定義します。

https://ref.xaio.jp/ruby/classes/object/send (opens new window)

このメソッドを利用することで、配列に格納した複数のメソッドを一つずつ取り出して実行することができます。

# 配列に格納したメソッドを一つずつ取りだして実行する例

配列にメソッド名をシンボルで格納します。それを一つずつ取り出し、hoge.sendに渡すことで、hogeオブジェクトのメソッドを呼び出せます。

  • hoge.rb
class Hoge
  def piyo
    puts "piyo"
  end

  def fuga
    puts "fuga"
  end
end

hoge = Hoge.new
[:piyo, :fuga].each do |method|
  hoge.send(method)
end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  • $ ruby hoge.rb
piyo
fuga
1
2

# 使用例

複数あるメソッドを必ず全て実行しつつ、どのメソッドにも同じ例外処理を行う場合に使いました。

  • foo.rb
class Foo
  def bar(x)
    puts 10 / x
  end

  def baz(y)
    puts 6 / y
  end
end

foo = Foo.new
[0, 2].each do |z|
  [:bar, :baz].each do |method|
    begin
      foo.send(method, z)
    rescue ZeroDivisionError
      puts "ゼロ除算"
    end
  end
end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  • $ ruby foo.rb
ゼロ除算
ゼロ除算
5
3
1
2
3
4

これを単純にfoo.barfoo.bazを順次実行するようにしてしまうと、foo.bar(0)で例外を起こした時にfoo.baz(0)が実行されなくなります。

  • bad_foo.rb
class Foo
  def bar(x)
    puts 10 / x
  end

  def baz(y)
    puts 6 / y
  end
end


foo = Foo.new
[0, 2].each do |z|
  begin
    foo.bar(z)
    foo.baz(z)
  rescue ZeroDivisionError
    puts "ゼロ除算"
  end
end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  • $ ruby bad_foo.rb
ゼロ除算
5
3
1
2
3

かといってbegin ~ rescur ~ endを、実行したいメソッドの数だけ書くのも面倒です。なのでObject#sendがいいのかなと。

# Object#sendの注意点

sendメソッドはObjectクラスのメソッドであり、ObjectクラスはKernelモジュールをincludeしているため、kernelモジュールで定義されたメソッドもsendメソッドで呼び出せてしまいます。evalメソッドも呼び出せ、任意のコードを実行できてしまうため、外部からメソッド名を受け取るのはやめましょうとのことです。

https://qiita.com/igrep/items/b2fed2d467f8a16f5eb0 (opens new window)

# メモ

ブロック使ったほうがうまく書けそう。