I have a couple of modules that extend method missing:
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, *args, &block)
end
end
end
module SaysGoodbye
def respond_to?(method)
super.respond_to?(method) || !!(method.to_s =~ /^goodbye/)
end
def method_missing(method, *args, &block)
if (method.to_s =~ /^goodbye/)
puts "Goodbye, #{method}"
else
super.method_missing(method, *args, &block)
end
end
end
class ObjectA
include SaysHello
end
class ObjectB
include SaysGoodbye
end
This all works well, eg ObjectA.new.hello_there outputs "Hello, hello_there". Likewise, ObjectB.new.goodbye_xxx outputs "Goodbye, xxx". respond_to? also works, eg ObjectA.new.respond_to? :hello_there return true.
However, this doesn’t work very well when you want to use both SaysHello and SaysGoodbye:
class ObjectC
include SaysHello
include SaysGoodbye
end
While ObjectC.new.goodbye_aaa works correctly, ObjectC.new.hello_a acts strange:
> 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)
It outputs correctly, then throws an error. Also respond_to? doesn’t correctly, ObjectC.new.respond_to? :hello_a returns false.
Finally, adding this class:
class ObjectD
include SaysHello
include SaysGoodbye
def respond_to?(method)
super.respond_to?(method) || !!(method.to_s =~ /^lol/)
end
def method_missing(method, *args, &block)
if (method.to_s =~ /^lol/)
puts "Haha, #{method}"
else
super.method_missing(method, *args, &block)
end
end
end
Also acts strangely. ObjectD.new.lol_zzz works, however ObjectD.new.hello_aand ObjectD.new.goodbye_t both throw a name exception after outputting the correct string. respond_to? also fails for hello and goodbye methods.
Is there a way to get this all working correctly? An explanation of how method_missing, Modules and super are interacting would also be really useful.
EDIT: coreyward solved the problem, if I use super instead of super.<method-name>(args...) in all the methods I define, the program works correctly. I don’t understand why this is though, so I asked another question about this at What does super.<method-name> do in ruby?
When you redefine a method, you redefine a method; period.
What you’re doing when you include the second module with the
method_missingmethod define is overriding the previously definedmethod_missing. You can keep it around by aliasing it before you redefine it, but you might want to watch out with that.Also, I don’t know why you’re calling
super.method_missing. Once yourmethod_missingdefinition is out of tricks you should let Ruby know it can continue on up the chain looking for a way to handle the call, all just by callingsuper(no need to pass arguments or specify a method name).About Super (update)
When you call
superRuby continues on up the inheritance chain looking for the next definition of the method invoked, and if it finds one it calls it and returns the response. When you callsuper.method_missingyou call themethod_missingmethod on the response tosuper().Take this (somewhat silly) example:
You can see that super is a method just like any other, it just does some magic behind the scenes. If you call
super.classhere you’re going to seeString, since “Teriyaki” is a string.Make sense now?