Best practises for exception handling

I'm write a wrapper for a web service and am wondering about the best
way to handle exceptions.

The main method in the wrapper sends either a HEAD or GET request to a
web server using Net::HTTP.

Should I catch any exceptions thrown by Net::HTTP and then throw back
my own exception, or should I leave the exceptions totally un-handled?

Alex

I would raise a new error that's on the same level of abstraction as
the calling code.

Pat

···

On Nov 8, 2007 2:04 PM, Alex Dunae <alex@dunae.ca> wrote:

I'm write a wrapper for a web service and am wondering about the best
way to handle exceptions.

The main method in the wrapper sends either a HEAD or GET request to a
web server using Net::HTTP.

Should I catch any exceptions thrown by Net::HTTP and then throw back
my own exception, or should I leave the exceptions totally un-handled?

Alex

i generally do one of two things

a) let the exception pass through. this is ok if, and only if, there is nothing reasonable your code could do to retry. if you do retry you have you have a configurable numbmer of retries, of course.

b) wrap the errors. my preferred approach is something like

   module Wrapper
     class Error < ::StandardError
       attr_accessor :exception

       def self.wrapping exception, *a, &b
         e = new *a, &b
         e.exception = exception
         e
       end
     end
     class Specific < Error; end
     class EvenMoreSpecific < Specific; end
   end

this allows client code respond appropriately to specific errors or simply to rescue the entire class of errors your wrapper might throw and, when you want to wrap another error you can

   rescue => e
     raise Error.wrapping(e)
   end

i would say that you have to have a good reason to pick b over a, but they do exist.

cheers.

a @ http://codeforpeople.com/

···

On Nov 8, 2007, at 3:04 PM, Alex Dunae wrote:

I'm write a wrapper for a web service and am wondering about the best
way to handle exceptions.

The main method in the wrapper sends either a HEAD or GET request to a
web server using Net::HTTP.

Should I catch any exceptions thrown by Net::HTTP and then throw back
my own exception, or should I leave the exceptions totally un-handled?

--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama

ara.t.howard wrote:

a) let the exception pass through. this is ok if, and only if, there is nothing reasonable your code could do to retry. if you do retry you have you have a configurable numbmer of retries, of course.

b) wrap the errors. my preferred approach is something like

Another possibility, somewhere between a and b: rescue the exception and re-raise, but add some more information to the message. That extra info might be, for example, IP address and port during network operations. This doesn't change the structure of exception handling, since the Exception subclass is the same and it will be handled at the same point, eventually, but it can be very useful for logging/debugging and for presenting an informative message to the user, if your program is interactive.

···

--
       vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

good point joel, i recently added this to main:

...

                 rescue Exception => e
                   handle_exception e
                 end
...

     def handle_exception e
       if e.respond_to?(:error_handler_before)
         fcall(e, :error_handler_before, self)
       end

       if e.respond_to?(:error_handler_instead)
         fcall(e, :error_handler_instead, self)
       else
         if e.respond_to? :status
           exit_status(( e.status ))
         end

         if Softspoken === e or SystemExit === e
           stderr.puts e.message unless(SystemExit === e and e.message.to_s == 'exit') ### avoids double message for abort('message')
         else
           fatal{ e }
         end
       end

       if e.respond_to?(:error_handler_after)
         fcall(e, :error_handler_after, self)
       end

       exit_status(( exit_failure )) if exit_status == exit_success
       exit_status(( Integer(exit_status) rescue(exit_status ? 0 : 1) ))
       exit exit_status
     end

...

which basically allows exceptions to be extended with modules or have methods defined on them which affect how they are handled. it seems like a useful pattern but i've only just started using it.

cheers.

a @ http://codeforpeople.com/

···

On Nov 8, 2007, at 7:20 PM, Joel VanderWerf wrote:

Another possibility, somewhere between a and b: rescue the exception and re-raise, but add some more information to the message. That extra info might be, for example, IP address and port during network operations. This doesn't change the structure of exception handling, since the Exception subclass is the same and it will be handled at the same point, eventually, but it can be very useful for logging/debugging and for presenting an informative message to the user, if your program is interactive.

--
share your knowledge. it's a way to achieve immortality.
h.h. the 14th dalai lama

Joel, A,

Thank you both for your responses - they made the gap in my knowledge quite plain, so I'm off to do some more homework on exceptions.

Alex

ara.t.howard wrote:

···

On Nov 8, 2007, at 7:20 PM, Joel VanderWerf wrote:

Another possibility, somewhere between a and b: rescue the exception and re-raise, but add some more information to the message. That extra info might be, for example, IP address and port during network operations. This doesn't change the structure of exception handling, since the Exception subclass is the same and it will be handled at the same point, eventually, but it can be very useful for logging/debugging and for presenting an informative message to the user, if your program is interactive.

good point joel, i recently added this to main:

...

                rescue Exception => e
                  handle_exception e
                end
...

    def handle_exception e
      if e.respond_to?(:error_handler_before)
        fcall(e, :error_handler_before, self)
      end

      if e.respond_to?(:error_handler_instead)
        fcall(e, :error_handler_instead, self)
      else
        if e.respond_to? :status
          exit_status(( e.status ))
        end

        if Softspoken === e or SystemExit === e
          stderr.puts e.message unless(SystemExit === e and e.message.to_s == 'exit') ### avoids double message for abort('message')
        else
          fatal{ e }
        end
      end

      if e.respond_to?(:error_handler_after)
        fcall(e, :error_handler_after, self)
      end

      exit_status(( exit_failure )) if exit_status == exit_success
      exit_status(( Integer(exit_status) rescue(exit_status ? 0 : 1) ))
      exit exit_status
    end

...

which basically allows exceptions to be extended with modules or have methods defined on them which affect how they are handled. it seems like a useful pattern but i've only just started using it.

cheers.

a @ http://codeforpeople.com/
--
share your knowledge. it's a way to achieve immortality.
h.h. the 14th dalai lama