Ruby / Exception handling
From WhyNotWiki
Exception handling edit (Category edit)
[edit] Exception hierarchy
- Exception
- fatal
- NoMemoryError
- ScriptError
- LoadError
- NotImplementedError
- SyntaxError
- SignalException
- Interrupt
- StandardError
- ArgumentError
- IOError
- EOFError
- IndexError
- LocalJumpError
- NameError
- NoMethodError
- RangeError
- FloatDomainError
- RegexpError
- RuntimeError
- SecurityError
- SystemCallError
- Errorno::__ (ENOENT, etc.) (system-dependent)
- ThreadError
- TypeError
- ZeroDivisionError
- SystemExit
- SystemStackError
[edit] The rescue statement modifier
[edit] Intro
rescue doesn't always have to be used with a begin block. You can do rescues even more concisely.
irb -> raise 'an error' rescue "Hey, there was an error!"
=> "Hey, there was an error!"
Or, most concisely of all:
irb -> raise 'an error' rescue nil
=> nil
It really doesn't get more concise than that for exception handling. If you just want to silence exceptions and you don't want to actually do any intelligent handling of them then rescue nil is your ticket.
[edit] Details
When you use rescue as a statement modifier, it rescues StandardError and its subclasses. You can't specify which class of exception(s) that you want to raise like you can with the normal version of rescue (rescue Exception => exception, for example).
Update: Perhaps it is possible after all. It looks like Rails is doing it right here (./vendor/rails/railties/lib/dispatcher.rb):
rescue Exception => exception # errors from CGI dispatch
failsafe_response(output, '500 Internal Server Error', exception) do
controller ||= ApplicationController rescue LoadError nil
controller ||= ActionController::Base
controller.process_with_exception(request, response, exception).out(output)
end
ensure
You also can't do such things as "ensure" when you use this style of rescue.
[edit] Caveat: The thing you pass to rescue is what it should do when there's a StandardError -- not the class of exceptions that it should rescue
That fact is a bit confusing, because it's different from the normal begin/rescue blocks.
irb -> begin
raise "A runtime error"
rescue RuntimeError
"The thing that I want to happen when there's a RuntimeError"
end
=> "The thing that I want to happen when there's a RuntimeError"
but:
irb -> raise "A runtime error" rescue "The thing that I want to happen when there's a RuntimeError"
=> "The thing that I want to happen when there's a RuntimeError"
If you forget this fact, you may be surprised when the error class itself is returned:
irb -> raise "A runtime error" rescue TypeError
=> TypeError
[edit] As soon as the error is rescued and you're no longer in the rescue block, $ERROR_INFO/$! resets to nil.
irb -> require 'English'
=> true
irb -> $ERROR_INFO
=> nil
irb -> raise "A runtime error" rescue puts $ERROR_INFO.inspect
#<RuntimeError: A runtime error>
=> nil
irb -> $ERROR_INFO
=> nil
irb -> $!
=> nil
[edit] Example: print errors without stopping
Use this trick when you want to see error messages that are raised but you want execution to continue:
irb -> NotARealClass.new() rescue $stderr.puts $!.inspect
#<NameError: uninitialized constant NotARealClass>
=> nil
or just:
irb -> NotARealClass.new() rescue $stderr.puts $!
uninitialized constant NotARealClass
=> nil
http://codeforpeople.com/lib/ruby/autorequire/autorequire-0.0.0/README
# rescues error and prints its message- support method for samples/*
#
def print_error
yield
rescue Exception => e
puts "#{ e } (#{ e.class })!"
end
[edit] Like all things in Ruby, rescue has a return value
This can be useful, for example, when you need to assign a default value to a variable if an error occurs.
irb -> an_important_variable = SomethingThatWillRaiseAnError.do_calculation rescue "a sane default value"
=> "a sane default value"
You may be thinking (if you come from a background in other, less orthogonal languages), how can you do things like rescue puts $!.inspect? You can do that because puts, like all methods and blocks, has a return value.
irb -> a = puts "whatever"
whatever
=> nil
irb -> puts a
nil
=> nil
irb -> a = raise 'arg!' rescue puts 'whatever'
whatever
=> nil
irb -> puts a
nil
=> nil
[edit] Can have an ensure without a begin block
def something_that_might_raise_an_error raise "an error" ensure puts "that we print this no matter what" end something_that_might_raise_an_error rescue puts $!
that we print this no matter what an error
[edit] Re-raising errors
[edit] Application thereof: Rescue in order to inspect the error more closely, but then "change your mind"
This application of exception re-raising has been made use of in a patch to RubyGems' require() method.
The problem is that Ruby's exception handling mechanism only lets you specify the class of the exception that you want to rescue:
rescue LoadError => exception
But sometimes just being able to specify a class causes too broad a category of errors to be rescued.
Fortunately, Ruby makes it very easy to inspect the details of the error after you've rescued it and to "change your mind" and re-raise the error if you decide you're not interested in rescuing it after all.
One use of this is with LoadErrors. If you only want to handle LoadErrors related to the failure to load a file named "A", then you can write a rescue block that only handles those specific errors, and causes all other errors to be re-raised as if they had not been rescued in the first place!
begin
...
rescue LoadError => exception
if exception.message == "no such file to load -- A"
# Handle it. Maybe dynamically *create* the 'A' file, for instance.
...
else
# We've "changed our mind". Don't know how to handle it after all. Re-raise it and let someone else worry about handling it.
raise
end
end
