2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[Ruby]method_missingを扱って魔術師見習いになろう!

Posted at

method_missingとは

method_missingとはRubyリファレンスマニュアルに記載の通り、呼びだされたメソッドが定義されていなかった時、Rubyインタプリタがこのメソッドを呼び出します。

また、本来存在しないメソッドを呼び出すので、これらの呼び出されたメソッドはゴーストメソッドとも呼ばれます。

魔術師としての使い方

呼び出しに失敗したメソッドの名前 (Symbol) が name にその時の引数が第二引数以降に渡されます。デフォルトではこのメソッドは例外NoMethodErrorを発生させます。

class Foo
end

foo = Foo.new

# 未定義のメソッドを呼び出すとNoMethodErrorとなる
foo.sample_method #=> NoMethodError!

しかし、method_missingをオーバーライドすることで未定義のメソッドが呼び出されても、意図的に未定義メソッドに対して任意の処理を加えることが出来る。

class Foo
  def method_missing(method, *args)
    # 任意の処理
    puts "#{method}が呼び出されましたが未定義です。"
  end
end

foo = Foo.new

# 未定義のメソッドを呼び出すしてもNoMethodErrorとならずに、任意の処理が出来る
foo.sample_method #=> "sample_methodが呼び出されましたが未定義です。"

method_missingという魔術の闇

以下のコードには無限ループするというエラーが隠れています。どこで発生しているか分かりますでしょうか??

class Roulette
  def method_missing(name, *args)
    person = name.to_s.upcase
    3.times do
      number = rand(10) + 1
      puts "#{number}..."
    end
    "#{person} got a #{number}"
  end
end

rand_num = Roulette.new
puts rand_num.tanaka
#=> 4...
#=> 2...
#=> 9...
#=> 7...
#=> …
#=> …
#=> SystemStackError: stack level too deep from..

ブロックローカル変数であるnumberが、ブロック外部で参照しようとしていることがエラーの原因となっています。
Rubyは「number」が見つからないので、method_missingメソッドを呼び出そうとするが、method_missingメソッド内で発生しているバグなので無限ループを起こしてしまいます。

注意点

method_missingメソッド自体にバグが潜んでいると、問題が特定しづらいため、必要のないゴーストメソッドは導入しないようにしましょう。

修正後コード

class Roulette
  def method_missing(name, *args)
    person = name.to_s.capitalize
    # 想定される名前以外は通常のmethod_missingを呼び出す
    super unless %w[tanaka sato suzuki].include?(person)
    # numberをブロック外から参照できるように事前に定義しておく
    number = 0
    3.times do
      number = rand(10) + 1
      puts "#{number}..."
    end
    "#{person} got a #{number}"
  end
end

rand_num = Roulette.new

# NoMethodError発生
puts rand_num.foo
=> NoMethodError: undefined method `foo' for #<Roulette:0x007fe4bb9ac7a0>

puts number_of.tanaka
#=> 3...
#=> 5...
#=> 5...
#=> tanaka got a 5

まとめ

method_missingは、魔術の一種として用いることができますが、リスクも伴っているので、利用するときは本当にこのやり方でいいか?を意識した上で利用しましょう!

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?