今天遇到一个很有意思的一段关于method_missing的代码:
class Roulette def method_missing(name, *args) person = name.to_s.capitalize 3.times do number = rand(10) + 1 puts "#{number}..." end "#{person} got a #{number}" end end r = Roulette.new puts r.bob
关于method_missing,就是在ancestors chain中没有找到改方法时,ruby就会去执行method_missing方法
这段代码的意思是,重写method_missing方法,当执行r.bob时,因为没有定义bob方法,程序会走到method_missing
方法中,将人名转换成大写,然后循环3次,得到一个随机数,那么期望的结果应该是:
4....
2....
7....
BOB got a 7
但是执行结果却是个死循环,最后程序会抛出异常:`method_missing': stack level too deep (SystemStackError)
问题的原因就在这个最后一句中得number变量:"#{person} got a #{number}"
这里的number变量是定义在blok中得,在blok外,number已经超出了作用范围,ruby不知道它是一个变量,会把它当成是一个不带括号的方法进行调用,此时就会重新调用method_missing方法,如此反复,就成了死循环了
所以在block外定义number = 0,程序即可正确执行
class Roulette def method_missing(name, *args) person = name.to_s.capitalize number = 0 3.times do number = rand(10) + 1 puts "#{number}..." end "#{person} got a #{number}" end end r = Roulette.new puts r.bob
另外,为了避免这样的麻烦,当执行某个方法时,你不知道如何处理,记得要退回到Kernel的method_missing方法。
class Roulette def method_missing(name, *args) person = name.to_s.capitalize super unless %w[Bob Frank Bill].include?(person) number = 0 3.times do number = rand(10) + 1 puts "#{number}..." end "#{person} got a #{number}" end end r = Roulette.new puts r.bob