Help me design my exception classes

J

J. B. Rainsberger

Everyone:

Being a Java programmer learning Ruby, I don't want my Java-based
prejudices about design pollute my thinking as I make the transition to
being a fluent Ruby speaker. Accordingly, I'd like you to suggest to me
how I should design my exception classes.

I Googled around for about 10 minutes, but couldn't find anything on
point, and I found it difficult to rummage through everything. If
someone has written extensively about this already, please just point me
there.

My concrete example is this: I'm "spooling" documents to be printed by
sending them via FTP. There might be other ways to spool documents, such
as by sending them via e-mail, to a printer, to the screen. I would like
to represent an exception that occurs in the underlying transport
mechanism (FTP, SMTP, printing subsystem...), but of course, I don't
want my clients to know about those things directly.

In Java, I would do something like

//...
catch (FTPException wrapped) {
throw new RuntimeException("Unable to spool document", wrapped);
}

where the FTP library would throw me an FTPException to indicate that
something went wrong. I only throw a RuntimeException because I don't
necessarily want to create a custom exception class when I only report
generic, unrecoverable "I couldn't spool" exceptions. At that point,
there's nothing (yet) to gain from distinguishing exceptions by their type.

What's the corresponding exception class in Ruby to Java's generic
RuntimeException. Is it RuntimeError? StandardError? Exception? Does
anyone want to tell me I'm nuts and show me a more "standard" way of
designing exceptions in Ruby?

Thanks.
 
T

Tom Copeland

Being a Java programmer learning Ruby,

Welcome! Your JUnit book rocks.
What's the corresponding exception class in Ruby to Java's generic
RuntimeException. Is it RuntimeError? StandardError? Exception? Does
anyone want to tell me I'm nuts and show me a more "standard" way of
designing exceptions in Ruby?

I usually rescue a generic Exception:

===========
$ irb
irb(main):001:0> begin
irb(main):002:1* raise "hey"
irb(main):003:1> rescue Exception => e
irb(main):004:1> puts e.class
irb(main):005:1> end
RuntimeError
=> nil
===========

You can create your own exceptions, of course:

===========
irb(main):006:0> class MyException < Exception
irb(main):007:1> def initialize ; puts "hello" ; super ; end
irb(main):008:1> end
=> nil
irb(main):009:0> MyException.new
hello
=> #<MyException: MyException>
===========

Yours,

Tom
 
J

Joel VanderWerf

Tom said:
Welcome! Your JUnit book rocks.


I usually rescue a generic Exception:

===========
$ irb
irb(main):001:0> begin
irb(main):002:1* raise "hey"
irb(main):003:1> rescue Exception => e
irb(main):004:1> puts e.class
irb(main):005:1> end
RuntimeError
=> nil
===========

Careful... that's a wide net to cast. It will catch Interrupt and
SystemExit, for example. StandardError is often (but not always) the
right generic class to catch.

Note that the syntax

rescue => e

(with exception class omitted) is the same as

rescue StandardError => e

This suggests that StandardError was intended to be thought of as "generic".
 
T

Tom Copeland

Careful... that's a wide net to cast. It will catch Interrupt and
SystemExit, for example. StandardError is often (but not always) the
right generic class to catch.

Note that the syntax

rescue => e

(with exception class omitted) is the same as

rescue StandardError => e

This suggests that StandardError was intended to be thought of as "generic".

Hm, good point, StandardError probably is a better way to go. I've
always used Exception, but I think I'll go back and revisit some of
those usages now :)

Yours,

Tom
 
J

Jeremy Henty

DRb does this:

class DRbError < RuntimeError; end

and uses DRbError as a superclass of all DRb error classes. Is this a
standard convention for Ruby libraries?

Jeremy Henty
 
J

Jacob Fugal

My concrete example is this: I'm "spooling" documents to be printed by
sending them via FTP. There might be other ways to spool documents, such
as by sending them via e-mail, to a printer, to the screen. I would like
to represent an exception that occurs in the underlying transport
mechanism (FTP, SMTP, printing subsystem...), but of course, I don't
want my clients to know about those things directly.

In Java, I would do something like

//...
catch (FTPException wrapped) {
throw new RuntimeException("Unable to spool document", wrapped);
}

where the FTP library would throw me an FTPException to indicate that
something went wrong. I only throw a RuntimeException because I don't
necessarily want to create a custom exception class when I only report
generic, unrecoverable "I couldn't spool" exceptions. At that point,
there's nothing (yet) to gain from distinguishing exceptions by their type.

What's the corresponding exception class in Ruby to Java's generic
RuntimeException. Is it RuntimeError? StandardError? Exception?

You're on the right track. I'd use the same model you've got going in
your Java design. For a usage like this, RuntimeError is the one
you're looking for. Fortunately, Ruby uses RuntimeError as the default
when you just raise a string instead of a specific Exception object.

Also, in Ruby to raise specific exceptions you don't need to call new
on the specific exception class, just provide more arguments to raise.
And unless you do your own custom wrapping in a subclass, you can't
wrap a previous exception -- but you can subsume the backtrace as an
additional parameter to raise. Unfortunately, you need to specify the
exception class as the first parameter when you do so.

So your syntax could look like this:

begin
# ...
# code that might raise FTPException
# ...
rescue FTPException => wrapped
raise RuntimeError, "Unable to spool document", wrapped.backtrace
end

Alternately, if you decide you do want to create your own class of
exception for later discernment, you will inherit from RuntimeError:

class SpoolError < RuntimeError; end

begin
# ...
rescue FTPException => wrapped
raise SpoolError, "Unable to spool document", wrapped.backtrace
end

Jacob Fugal
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,968
Messages
2,570,149
Members
46,695
Latest member
StanleyDri

Latest Threads

Top