Always define respond_to_missing? when overriding method_missing
method_missing is still considered scary, but here's something more scary: forgetting to override respond_to?.
Whoa, right? Just gave you an anxiety attack there.
How about #method, though? Does that still work?
Go ahead, try it. Here, I'll try it with you:
require 'ostruct' class Order def user @_user ||= OpenStruct.new(name: 'Mike', age: 28, occupation: 'slacker') end def method_missing(method_name, *arguments, &block) if method_name.to_s =~ /user_(.*)/ user.send($1, *arguments, &block) else super end end def respond_to?(method_name, include_private = false) method_name.to_s.start_with?('user_') || super end end
OK now I'll load that into irb and play with it:
ruby-1.9.2-p290> order = Order.new => #<Order:0x00000001e40948> ruby-1.9.2-p290> order.user_name => "Mike" ruby-1.9.2-p290> order.respond_to?(:user_name) => true ruby-1.9.2-p290> order.method(:user_name) NameError: undefined method `user_name' for class `Order' from (irb):23:in `method' from (irb):23 from /home/mike/.rvm/rubies/ruby-1.9.2-p290/bin/irb:16:in `<main>'
Denied!
There is a solution! How would you like to solve your #method problem while also using a method with a better name? For the low price of Ruby 1.9 you, too, can have this:
require 'ostruct' class Order def user @_user ||= OpenStruct.new(name: 'Mike', age: 28, occupation: 'slacker') end def method_missing(method_name, *arguments, &block) if method_name.to_s =~ /user_(.*)/ user.send($1, *arguments, &block) else super end end def respond_to_missing?(method_name, include_private = false) method_name.to_s.start_with?('user_') || super end end
But you don't have to take my word for it:
ruby-1.9.2-p290> order = Order.new => #<Order:0x00000001c8d678> ruby-1.9.2-p290> order.user_name => "Mike" ruby-1.9.2-p290> order.respond_to?(:user_name) => true ruby-1.9.2-p290> order.method(:user_name) => #<Method: Order#user_name>
If I were to tweet about this, this is what I would say: always define respond_to_missing? when overriding method_missing.













