Shameless Plug: Don’t make any exceptions. Protect your privacy online with a VPN Service when browsing the web.
update:
p $!, *$@ is a little more optimal in that you will see the class of the exception also.
I’ll put the meat of this post first so you don’t have to waste your time if you already know this. The simplest way to catch and print out an exception along with it’s backtrace:
# catch most exceptions (anything that derives from StandardError) begin ... rescue puts $!, $@ end # catch all exceptions (anything that derives from Exception) begin ... rescue Exception puts $!, $@ end
At some point we all want to catch an exception, print it out, and then move on to something else. I just wanted to point out a very simple way to do this. I want to point this out because I have seen many people spend far too much typing on this (myself included). Here’s a combined example of the overtyping I’ve seen used.
begin
...
rescue Exception => ex
puts ex.message
puts ex.backtrace.join("\n")
end
First off, Ruby stores the last exception caught in the special (thread local) variable $! and the backtrace of the last exception in $@. So there is no need to explicitly specify a variable to store it in. Secondly, Exception#message in most cases is redundant because it’s just aliased to Exception#to_s which puts will call for you. Thirdly calling join("\n") is redundant when passing an array to puts. By default puts will output an array with a newline between each item. Also you might not have known that you can pass multiple arguments to puts and it will be just as if you had called puts for each one.
Usually the message of the exception is clear enough so you don’t need to see its class. But in the cases where you want to, we can easily show this too without much more work:
rescue p $! puts $@ end # or rescue puts $!.inspect, $@ end
p will call inspect on the exception which will show it’s class and message.
I know this is simple and trivial stuff but I still see a lot of people doing it a more verbose way than necessary. Some people will probably argue that it’s less understandable my way or we should be more explicit or something and that’s a matter of opinion but I personally disagree with it. Anyway this post is mainly for the people who would like doing it the shorter way but just didn’t know about it yet.
That’s helpful, thanks! :)
Comment by Noam B — November 7, 2008 @ 10:50 am
Don’t get me wrong but I think it’s bullshit. Give me a person which will remember all this $#@!%#@! crypto named variables. Yep, Matz when created Ruby liked also Perl. He thought that all these magic variables are great. But 15 years later most people will agree: they don’t and they don’t match with Ruby. Fortunately most people don’t use them.
Try it with your colleges, show your code and ask them what $! and $@ means. Show them also e.message and e.backtrace and I’m sure that (even when don’t know Ruby at all) they will know.
Programming isn’t about writing the shortest code as you can. Programming is about balancing between readability, verbosity and quantity of source code. Without good proportions your end up with java or perl.
Comment by Radarek — November 7, 2008 @ 2:07 pm
I agree with Radarek. I’ll take readable ‘verbose’ code over a splattering of $@$! any day. I actually like the idea of dropping the join and combining the puts, resulting in:
rescue Exception => e
puts e.message, e.backtrace
end
That’s very succinct, yet completely understandable.
Comment by Dean Strelau — November 7, 2008 @ 2:15 pm
This really comes to down to who you’re programming for and personal style. If either one dictates you should use a more verbose syntax then by all means please do.
In regards to the “=> e” I just think it’s the most redundant thing ever. What ELSE are you going to do in a rescue block other than access the exception object. I think if you saw a $! inside of a rescue block for the first time it’d be pretty easy to deduce what it is.
Now one thing I don’t and haven’t recommended is using $! in some method which you call from the rescue block. In that case I would definitely recommend passing it to the method.
Comment by coderrr — November 7, 2008 @ 2:34 pm
perl has english version of these variables ($! is also $ERRNO and $OS_ERROR, $@ is also $EVAL_ERROR, etc… see man perlvar for all the others).
Know if ruby also has readable name for these variables ?
Comment by Thierry — November 7, 2008 @ 7:47 pm
$! == $ERROR_INFO
$@ == $ERROR_POSITION
There – no need to use meaningless punctuation.
But note the $. Global variables are bad. I’m not going to argue about it, since other people have done a far better job than I can over the last 30 or 40 years. If someone disagrees that globals should only be used when absolutely necessary, fine.
However, I do sympathize with the discomfort of declaring a variable to hold the exception, when it’s not really serving any purpose to do so. Perhaps the best solution would be an automatically defined variable (like $ERROR_INFO) that would be local to the current exception handler.
Either way, I think ‘=> e’ is far to be preferred over cryptic, Perly, global variables. Even if someone could intuit what $! means, what about $@? C’mon.
Comment by Mark Wilden — November 11, 2008 @ 12:02 am
Do you really mean that Exception#message is _always_ redundant because it’s _always_ aliased to puts? If not, what are the situations where it’s NOT simply aliased to #to_s?
Is any such discrepancy between ruby versions? Subclasses of Exception? I ask because I initialized a Hash by passing in an array (see Hash[]) and it differed between ruby 1.8.4 and 1.8.7. I’ve come to be aware of such caveats.
Comment by Joshua Go — November 20, 2008 @ 1:09 am
1.8:
rb_define_method(rb_eException, “message”, exc_to_str, 0);
exc_to_str of course calls to_s…
Any subclass could override #message to do something different. I haven’t done an investigation of this but I have a feeling you won’t find many cases where they do that.
In 1.9 it’s the same.
Comment by coderrr — November 20, 2008 @ 9:40 am
Don’t forget that you can just raise the error again, and it will be printed if it is not caught. This is good if your goal is just to print the error, and you don’t care about recovering from the error; the only reason you caught the error was to log it in a special way. So your code might be like this:
begin
results = calculate_values(data)
upload_results_with_api(connector, results)
alert_script_success(true)
rescue Exception
alert_script_success(false)
raise
end
Comment by Rory O’Kane — June 19, 2012 @ 5:44 pm