Search code examples
rubyrescue

Refactoring nested rescue statements


Rescue statement serves well when it comes to successive filtering of different errors:

o = Object.new
begin
  o.foo
rescue ArgumentError
  puts "Object o does define #foo, but you got the argument number wrong!"
rescue NoMethodError
  puts "Object o does not respond to #foo!"
else
  puts "That's right!"
end

But when it comes to rescuing the same error with different parameters, this is what I use in my code:

o = Object.new
begin
  o.foo
rescue NoMethodError
  begin
    o.bar
  rescue NoMethodError
    begin
      o.quux
    rescue NoMethodError
      warn "Object o responds not to basic methods!"
    end
  end
end

Needless to say, I don't like it. Isn't there a more clever way to do this?


Solution

  • def send_messages_maybe(object, messages, *parameters)
      object.send(messages.first, *parameters)
    rescue NoMethodError
      messages = messages[1..-1]
      if messages.empty?
        warn "Object does not respond to basic methods!"
      else
        retry
      end
    end
    
    module Quux
      def quux(*args)
        puts "quux method called with #{args.inspect}"
      end
    end
    
    messages = %i{foo bar quux}
    send_messages_maybe(Object.new, messages)
    send_messages_maybe(Object.new.extend(Quux), messages, 10, :hello, 'world')
    

    Output:

    Object does not respond to basic methods!
    quux method called with [10, :hello, "world"]
    

    This works with objects that don't have a #respond_to_missing? method defined, which is very common --- most of the code I've seen that uses #method_missing falls into this category.