Ruby Exceptions -- Why else?
The else
is for when the block completes without an exception thrown. The ensure
is run whether the block completes successfully or not. Example:
begin
puts "Hello, world!"
rescue
puts "rescue"
else
puts "else"
ensure
puts "ensure"
end
This will print Hello, world!
, then else
, then ensure
.
Ruby exception handling in else block
In the begin rescue
block else
is called only when no exception occurs, i.e. no error was thrown. Try this:
begin
# do work here
rescue SafeShutdown => e
# print e
rescue SystemExit => e
# print e
else
# this will only run when no exceptions are thrown
ensure
# this will always run
end
Ruby exception handling
You're thinking about exceptions incorrectly.
The whole point of exceptions is that the main body of your code proceeds as though no exceptions were thrown. All of the code inside your begin
block is already executing as though no exceptions were thrown. An exception can interrupt the normal flow of code, and prevent subsequent steps from executing.
You should put your file copy stuff inside the begin
block:
begin
#code...
# This will run if the above "code..." throws no exceptions
FileUtils.cp("Demo.rb", "C:/Ruby/Success")
rescue StandardError => e
puts("Exception #{e} occurred")
puts("Copying script to error folder.")
FileUtils.cp("Demo.rb", "C:/Ruby/Failure")
end
Ruby Exception or Error?
Looking at the list of Ruby exceptions, SignalException
is the only one that is named *Exception
; everything else is an XXXError
(except for SystemExit
and fatal
). If anything, the practice is to name your exception FooError
. I'm having trouble finding any specific reason why SignalException
isn't named SignalError
.
Ruby one-liner and warning: 'else without rescue is useless'
Ruby is interpreting your code as two separate statements.
puts "#{n} must be odd and positive" if n % 2 == 0 || n < 0
else (n + 1).times { |i| puts chars * i }
The else
is not associated with the if
. Ruby is, I guess in desperation, interpreting that as part of a begin/rescue/else
condition. Why it's not a syntax error, I have no idea, but it's interpreting it as having no begin
block which is technically "successful" so the else
always runs.
Conditional statements like do this if that
are intended only for simple conditions covering simple statements. Trying to wedge in an else condition is right out. Instead, use a normal condition.
def triangle(n, chars)
if n % 2 == 0 || n < 0
puts "#{n} must be odd and positive"
else
(n + 1).times { |i| puts chars * i }
end
end
In general, avoid the else
entirely by taking care of the error condition immediately. It avoids nesting the bulk of the function in an else
block. This is more relevant for longer functions, but it's a good habit to get into.
def triangle(n, chars)
if n % 2 == 0 || n < 0
puts "#{n} must be odd and positive"
return nil
end
(n + 1).times { |i| puts chars * i }
end
Finally, errors should be handled with exceptions, not printed error messages. Exceptions can be caught and dealt with by the caller, printed error messages are difficult to catch and bubble up to the user. Exceptions, if not dealt with, halt the program; if the user forgets to handle an error the program will stop and they'll know to fix it. Printed error messages simply let the program plow forward and can be ignored leading to further problems down the road.
Exceptions can also be categorized allowing the caller to figure out what sort of error happened and act appropriately.
def triangle(n, chars)
if n % 2 == 0 || n < 0
raise ArgumentError, "#{n} must be odd and positive"
end
(n + 1).times { |i| puts chars * i }
end
Why is it bad style to `rescue Exception = e` in Ruby?
TL;DR: Use StandardError
instead for general exception catching. When the original exception is re-raised (e.g. when rescuing to log the exception only), rescuing Exception
is probably okay.
Exception
is the root of Ruby's exception hierarchy, so when you rescue Exception
you rescue from everything, including subclasses such as SyntaxError
, LoadError
, and Interrupt
.
Rescuing Interrupt
prevents the user from using CTRLC to exit the program.
Rescuing SignalException
prevents the program from responding correctly to signals. It will be unkillable except by kill -9
.
Rescuing SyntaxError
means that eval
s that fail will do so silently.
All of these can be shown by running this program, and trying to CTRLC or kill
it:
loop do
begin
sleep 1
eval "djsakru3924r9eiuorwju3498 += 5u84fior8u8t4ruyf8ihiure"
rescue Exception
puts "I refuse to fail or be stopped!"
end
end
Rescuing from Exception
isn't even the default. Doing
begin
# iceberg!
rescue
# lifeboats
end
does not rescue from Exception
, it rescues from StandardError
. You should generally specify something more specific than the default StandardError
, but rescuing from Exception
broadens the scope rather than narrowing it, and can have catastrophic results and make bug-hunting extremely difficult.
If you have a situation where you do want to rescue from StandardError
and you need a variable with the exception, you can use this form:
begin
# iceberg!
rescue => e
# lifeboats
end
which is equivalent to:
begin
# iceberg!
rescue StandardError => e
# lifeboats
end
One of the few common cases where it’s sane to rescue from Exception
is for logging/reporting purposes, in which case you should immediately re-raise the exception:
begin
# iceberg?
rescue Exception => e
# do some logging
raise # not enough lifeboats ;)
end
Ruby - how to check for specific exception after rescue?
In your example ex
is an instance of on exception, and will not give expected results when comparing to the class itself with the ==
operator, which is a Class
object. You need to check the error's class against the actual class object.
# A NameError or an error that is a subclass of NameError
ex.is_a?(NameError)
ex.kind_of?(NameError)
# NameError ONLY, not even sub-classes of NameError
ex.instance_of?(NameError)
There are numerous other ways you could check, though the previous methods I outlined are the most commonly used, and typically "preferred" way. Although equality could still be used on the classes, it is typically not the suggested way to achieve it, as it is less flexible, and less clear on the underlying intention, where the above examples indicate exactly how you would like to handle subclasses of the specified error.
ex.class == NameError
I found this answer that explains the subtle differences pretty well.
Related Topics
How to Load a Spec_Helper.Rb Automatically in Rspec 2
How to Use Capybara in Pure Ruby (Without Rails)
Rails: How to Determine Controller/Action in View
How to Solve the Update Bundler Warning in Rails When Deploying to Heroku
Add Comment to User and Post Models (Ruby on Rails)
Restarting Unicorn with Usr2 Doesn't Seem to Reload Production.Rb Settings
Help Refactoring This Nasty Ruby If/Else Statement
What Is the Preferred Way (Better Style) to Name a Namespace in Ruby? Singular or Plural
Ruby Method Like 'Self' That Refers to Instance
Throttling Requests to a Ruby on Rails API
Rails Routing (Root :To => ...)
Rails 5.2 Active Storage Add Custom Attributes
Is Ruby a Scripting Language or an Interpreted Language
Calling/Applying Lambda VS. Function Call - the Syntax in Ruby Is Different. Why
Ruby: Insert Spaces Every X Number of Characters
What's the Most Efficient Way to Deep Copy an Object in Ruby