module SaysHello def respond_to?(method) super.respond_to?(method) || !!(method.to_s =~ /^hello/) end def method_missing(method,*args,&block) if (method.to_s =~ /^hello/) puts "Hello,#{method}" else super.method_missing(method,&block) end end end module SaysGoodbye def respond_to?(method) super.respond_to?(method) || !!(method.to_s =~ /^goodbye/) end def method_missing(method,&block) if (method.to_s =~ /^goodbye/) puts "Goodbye,&block) end end end class ObjectA include SaysHello end class ObjectB include SaysGoodbye end
这一切都运行良好,例如ObjectA.new.hello_there输出“Hello,hello_there”.同样,ObjectB.new.goodbye_xxx输出“Goodbye,xxx”.回应?也有效,例如ObjectA.new.respond_to? :hello_there返回true.
但是,当您想同时使用SaysHello和SaysGoodbye时,这种方法效果不佳:
class ObjectC include SaysHello include SaysGoodbye end
当ObjectC.new.goodbye_aaa正常工作时,ObjectC.new.hello_a表现得很奇怪:
> ObjectC.new.hello_aaa Hello,hello_aaa NoMethodError: private method `method_missing' called for nil:NilClass from test.rb:22:in `method_missing' (line 22 was the super.method_missing line in the SaysGoodbye module)
它正确输出,然后抛出错误.还是respond_to?不正确,ObjectC.new.respond_to? :hello_a返回false.
最后,添加这个类:
class ObjectD include SaysHello include SaysGoodbye def respond_to?(method) super.respond_to?(method) || !!(method.to_s =~ /^lol/) end def method_missing(method,&block) if (method.to_s =~ /^lol/) puts "Haha,&block) end end end
也行为奇怪. ObjectD.new.lol_zzz工作,但ObjectD.new.hello_a和ObjectD.new.goodbye_t在输出正确的字符串后都抛出一个名称异常.回应?你好和再见的方法也失败了.
有没有办法让这一切正常工作?解释method_missing,Modules和super如何交互也非常有用.
编辑:coreyward解决了这个问题,如果我在我定义的所有方法中使用super而不是super.< method-name>(args …),程序可以正常工作.我不明白为什么会这样,所以我在What does super.<method-name> do in ruby?问了另一个问题
解决方法
当您使用method_missing方法定义第二个模块时,您正在执行的操作将覆盖先前定义的method_missing.您可以在重新定义它之前通过别名来保持它,但您可能需要注意它.
另外,我不知道你为什么叫super.method_missing.一旦你的method_missing定义没有技巧,你应该让Ruby知道它可以继续在链上寻找一种处理调用的方法,所有这些只需要调用super(不需要传递参数或指定方法名).
关于超级(更新)
当你调用超级Ruby时,继续在继承链上查找被调用方法的下一个定义,如果找到它,则调用它并返回响应.当你调用super.method_missing时,你会对super()的响应调用method_missing方法.
拿这个(有点傻)的例子:
class Sauce def flavor "Teriyaki" end end # yes,noodles inherit from sauce: # warmth,texture,flavor,and more! ;) class Noodle < Sauce def flavor sauce_flavor = super "Noodles with #{sauce_flavor} sauce" end end dinner = Noodle.new puts dinner.flavor #=> "Noodles with Teriyaki sauce"
你可以看到super就像其他任何一种方法一样,它只是在幕后做了一些魔术.如果你在这里调用super.class,你会看到String,因为“Teriyaki”是一个字符串.
现在有道理吗?