[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

exception extensions

Joel VanderWerf

11/7/2004 3:38:00 AM


These are some extensions to Exception that I've found useful recently.
Maybe someone else will too.

The #details method outputs something like what ruby itself does when
you see an exception in stderr. However, it returns a string, and you
can control the threshold for removing informative lines with '...'.
Also, the output is formatted more nicely, IMO.

The #reraise_as method is useful when you want to catch something that's
fairly opaque, like NameError, or NoMethodErr, and, using a more
descriptive and app-specific exception class, raise it again, but with
the same backtrace.

Some example code is included.

class Exception
# More or less reproduces ruby's default exception reporting in
# the returned string, except truncation can be controlled with
# +maxlines+: +nil+ means no limit, an integer value means at
# most <tt>maxlines/2</tt> frames from each of the top and bottom
# of the stack are reported.
def details(maxlines=nil)
bt = backtrace.dup
bt0 = bt.shift
if maxlines and maxlines < bt.size
ml, r = maxlines.divmod(2)
bt = bt[0...ml+r] + ["..."] + bt[-(ml==0 ? 1 : ml)..-1]
end
[
"#{bt0}: #{self.class}:",
message,
" from " + bt.join("\n from ")
].join("\n")
end

# Raise the exception, preserving the backtrace, but with as an
# instance of class +cl+. The message is preserved by default,
# but may be set to +msg+.
def reraise_as(cl, msg = message)
e = cl.new(msg)
e.set_backtrace backtrace
raise e
end
end


class FooError < StandardError; end

def foo
bar(10)
rescue StandardError => e
puts e.details(5)
puts "*"*40
e.reraise_as(FooError)
end

def bar(try)
if try <= 0
raise " bar problem:\n can't find a bar\n anywhere around here"
else
bar(try-1)
end
end

foo

EXAMPLE OUTPUT:

reraise.rb:43:in `bar': RuntimeError:
bar problem:
can't find a bar
anywhere around here
from reraise.rb:45:in `bar'
from reraise.rb:45:in `bar'
from reraise.rb:45:in `bar'
from ...
from reraise.rb:34:in `foo'
from reraise.rb:49
****************************************
reraise.rb:43:in `bar': bar problem: (FooError)
can't find a bar
anywhere around here from reraise.rb:45:in `bar'
from reraise.rb:45:in `bar'
from reraise.rb:45:in `bar'
from reraise.rb:45:in `bar'
from reraise.rb:45:in `bar'
from reraise.rb:45:in `bar'
from reraise.rb:45:in `bar'
from reraise.rb:45:in `bar'
from reraise.rb:45:in `bar'
from reraise.rb:45:in `bar'
from reraise.rb:34:in `foo'
from reraise.rb:49



2 Answers

Robert Klemme

11/7/2004 12:14:00 PM

0


"Joel VanderWerf" <vjoel@PATH.Berkeley.EDU> schrieb im Newsbeitrag
news:418D989C.1010303@path.berkeley.edu...
>
> These are some extensions to Exception that I've found useful recently.
> Maybe someone else will too.
>
> The #details method outputs something like what ruby itself does when you
> see an exception in stderr. However, it returns a string, and you can
> control the threshold for removing informative lines with '...'. Also, the
> output is formatted more nicely, IMO.
>
> The #reraise_as method is useful when you want to catch something that's
> fairly opaque, like NameError, or NoMethodErr, and, using a more
> descriptive and app-specific exception class, raise it again, but with the
> same backtrace.

This both is quite nice. But does the reraise_as really work as expected?
As far as I can see the set_backtrace is rendered useless because when the
secondary exception is raise the stack trace is overwritten (which is quite
logical btw). With your code in x.rb I get

$ ruby x.rb
x.rb:44:in `bar': RuntimeError:
bar problem:
can't find a bar
anywhere around here
from x.rb:46:in `bar'
from x.rb:46:in `bar'
from x.rb:46:in `bar'
from ...
from x.rb:35:in `foo'
from x.rb:50
****************************************
x.rb:27:in `reraise_as': bar problem: (FooError)
can't find a bar
anywhere around here from x.rb:39:in `foo'
from x.rb:50

Line 27 is the one with "raise e". The output is the same if I comment line
26 ("e.set_backtrace backtrace").

Probably an approach similar to Java 1.4 exception chaining is better: there
each exception has an optional "cause" (another exception) and printing of
the stack trace is modified that it prints this stack trace and then the
cause's trace. The nice thing about this feature is that no stack frame is
printed twice, i.e. the exceptions trace is shortened because the cause will
contain similar frames. Also this works recursive, i.e., if the cause has a
cause itself etc.

Kind regards

robert

Pit Capitain

11/7/2004 1:15:00 PM

0

Robert Klemme schrieb:
> (...) But does the reraise_as really work as
> expected? As far as I can see the set_backtrace is rendered useless
> because when the secondary exception is raise the stack trace is
> overwritten (which is quite logical btw).
> (...)

Kernel#raise can take a backtrace as the third parameter. So reraise_as could be
implemented as:

class Exception
# Raise the exception, preserving the backtrace, but as an
# instance of class +cl+. The message is preserved by default,
# but may be set to +msg+.
def reraise_as(cl, msg = message)
raise cl, msg, backtrace
end
end

Regards,
Pit