Are there any other reasons to handle an exception with rescue except for not letting my program to crash and continue working?
Nobody's forcing you to rescue exceptions. You can let it burn if you want, and in some situations this is a perfectly viable strategy. Sometimes it's better to crash than to muddle along and proceed as if nothing bad happened.
In systems where exceptions are logged and the process is automatically restarted this may be the way to go.
The last thing you want to do is jam in a rescue that does nothing other than obscure problems, or worse, leaves the system in an unstable or non-working state. This is the equivalent of disconnecting a fire alarm because it's annoying and beeps once in a while. One day you're going to need it because something serious is happening.
Most of the time exceptions communicate something useful, they're not a nuisance. Like this:
class ConfigDataError < StandardError
# Defines a custom exception class
end
def config_data(path)
YAML.load(File.open(path))
rescue Errno::ENOENT
# File is missing, no big deal.
{ }
rescue Psych::SyntaxError
# This is a problem!
raise ConfigDataError, "Invalid YAML in file #{path}"
end
Where you can target certain kinds of expected exceptions as well as being able to re-raise other exceptions as a new type.
Related
Ruby offers two possibilities to cause an exception programmatically: raise and fail, both being Kernel methods. According to the documents, they are absolutely equivalent.
Out of a habit, I used only raise so far. Now I found several recommendations (for example here), to use raise for exceptions to be caught, and fail for serious errors which are not meant to be handled.
But does it really make sense? When you are writing a class or module, and cause a problem deep inside, which you signal by fail, your programming colleagues who are reviewing the code, might happily understand your intentions, but the person who is using my code will most likely not look at my code and has no way of knowing, whether the exception was caused by a raise or by fail. Hence, my careful usage of raise or fail can't have any influence on his decision, whether she should or should not handle it.
Could someone see flaws in my arguments? Or are there other criteria, which might me want to use fail instead of raise?
use 'raise' for exceptions to be caught, and 'fail' for serious errors which are not meant to be handled
This is not what the official style guide or the link you provided say on the matter.
What is meant here is use raise only in rescue blocks. Aka use fail when you want to say something is failing and use raise when rethrowing an exception.
As for the "does it matter" part - it is not one of the most hardcore strictly followed rules, but you could make the same argument for any convention. You should follow in that order:
Your project style guide
Your company style guide
The community style guide
Ideally, the three should be the same.
Update: As of this PR (December 2015), the convention is to always use raise.
I once had a conversation with Jim Weirich about this very thing, I have since always used fail when my method is explicitly failing for some reason and raise to re-thrown exceptions.
Here is a post with a message from Jim (almost verbatim to what he said to me in person):
http://www.virtuouscode.com/2014/05/21/jim-weirich-on-exceptions/
Here is the relevant text from the post, a quote attributed to Jim:
Here’s my basic philosophy (and other random thoughts) on exceptions.
When you call a method, you have certain expectations about what the method will accomplish. Formally, these expectations are called post-conditions. A method should throw an exception whenever it fails to meet its postconditions.
To effectively use this strategy, it implies you must have a small understanding of Design by Contract and the meaning of pre- and post-conditions. I think that’s a good thing to know anyways.
Here’s some concrete examples. The Rails model save method:
model.save!
-- post-condition: The model object is saved.
If the model is not saved for some reason, then an exception must be raised because the post-condition is not met.
model.save
-- post-condition: (the model is saved && result == true) ||
(the model is not saved && result == false)
If save doesn’t actually save, then the returned result will be false , but the post-condition is still met, hence no exception.
I find it interesting that the save! method has a vastly simpler post-condition.
On the topic of rescuing exceptions, I think an application should have strategic points where exceptions are rescued. There is little need for rescue/rethrows for the most part. The only time you would want to rescue and rethrow is when you have a job half-way done and you want to undo something so avoid a partially complete state. Your strategic rescue points should be chosen carefully so that the program can continue with other work even if the current operation failed. Transaction processing programs should just move on to the next transaction. A Rails app should recover and be ready to handle the next http request.
Most exception handlers should be generic. Since exceptions indicate a failure of some type, then the handler needs only make a decision on what to do in case of failure. Detailed recovery operations for very specific exceptions are generally discouraged unless the handler is very close (call graph wise) to the point of the exception.
Exceptions should not be used for flow control, use throw/catch for that. This reserves exceptions for true failure conditions.
(An aside, because I use exceptions to indicate failures, I almost always use the fail keyword rather than the raise keyword in Ruby. Fail and raise are synonyms so there is no difference except that fail more clearly communcates that the method has failed. The only time I use raise is when I am catching an exception and re-raising it, because here I’m not failing, but explicitly and purposefully raising an exception. This is a stylistic issue I follow, but I doubt many other people do).
There you have it, a rather rambling memory dump on my thoughts on exceptions.
I know that there are many style guides that do not agree (the style guide used by RoboCop, for example). I don't care. Jim convinced me.
I read the blog post JRuby Performance: Exceptions are not flow control which advocated avoiding using Exceptions except for exceptional circumstances.
I realized that I'm guilty of using rescue to handle LoadErrors on a regular basis.
Is there an alternative to require that tries to load a file if it exists, but doesn't throw an exception if it doesn't?
Background: If you're wondering "why have you got requires that you don't absolutely require?", here's my story:
While I was programming for Ruby 1.8, I used require "rdoc/usage" so that I could give usage information if I didn't enter the correct number of parameters in my command-line application. This throws an exception on out-of-the-box 1.9.
Part of my application involves code to manipulate win32ole when it's running on my Windows desktop. This causes a LoadError if the files involved were run under the Linux server that does the heavy computational work. The files that use win32ole also has other code that is tested in my test suite, so when running my test suite under Linux, I have to require those files. I ought to split up such files, but that seems a bit like yak shaving.
Computer programs spend almost all their time in loops. If you are repeatedly raising and rescuing exceptions in an inner loop, which is executed millions of times during an execution of your program, you may genuinely have performance problems. But loading files is something which is usually done only during program initialization. If you raise and rescue a few exceptions during program startup, the impact on performance will be too close to zero for anybody to ever notice.
By the way, if you have a method which is truly performance-sensitive (ie. executed many times), but you do want a "goto" which allows you to jump out of multiple blocks and even up the call stack (like an exception), use throw and catch. They're similar to raise and rescue, but much faster. Most of the performance cost of raising an exception comes from filling in the stack trace, and throw doesn't do that.
IMHO, begin; require "..."; rescue LoadError is idiomatic Ruby and is not to be considered a bad practice in any way, regardless of what people say about "using exceptions for flow control". If your script is normally executed on Windows, running on Linux could rightly be considered an "exceptional condition", and worthy of using exceptions. In general, if a file which you want to load is not there, that's an "exceptional condition" -- which is why require raises exceptions in the first place!
Hold your head up high, man! Don't let the haters make you feel guilty for using simple, common-sense code!
Using an exception for your first case is probably fine and a lot less messy than trying to figure out if require would fail before calling it. If all you're doing is trying to load something optional (or, similarly, you need to support several different libraries that do the same thing), then trying to load it and handling the exception is fine, good, and morally upstanding behavior.
In your second case, it might make more sense to check RUBY_PLATFORM or sys-uname before trying to do platform specific things like OLE. Sometimes the yak does need shaving. In this, if you're on Windows then you really do want to have the require fail whereas if you're on Linux, you don't want to require at all; you're using side effects of the exception rather than the exception itself.
Sometimes people try to use exceptions as a trappable goto of sorts. Exceptions are intended for non-recoverable error conditions, not as a general event notification system. Using exceptions as a goto (i.e. flow control) is an abuse of the exception handling system and people that build systems that use exceptions for flow control usually end up in the hospital (for "falling", into a box of hammers, ten times in a row).
I'm not asking about how to use the begin..rescue statement in Ruby. Rather, I'm asking how do you figure out what's worth worrying about. I know some hardcore people probably go through the documentation/code to find every single possible error, and have code that handles all cases gracefully. But that might be going too far if you're not writing something "mission-critical".
So how do you know what errors to look out for? I generally just run my code, and if it stops due to an error, I add a begin..rescue statement. I'm sure there has to be a better way though. I just ran a script to import data into a database for some files, and it stopped after an hour (lots of files, don't ask) due to some error that I couldn't know was going to occur. Quite annoying, and I'm sure it's my fault for not writing more robust code. How do I do that?
I'm not sure it's always possible to always forsee exceptions without first experiencing them. Now that you know that databases sometimes fail, you might add better exception handling for that in the future, but experience is the only real teacher in this sense.
However, I think you might be a bit too quick to write your own error handlers. For production code, there are obviously certain scenarios you want to be able to catch easily and show a good error message for (e.g. too many database connections), but sometimes allowing the error to float up is, in fact, the most robust thing a script can do. If there's something wrong with my rubygems setup that causes your gem to spit up errors, I don't want you to hide them behind "something went wrong!"; I'd rather see the error itself so I can get down to work.
Catch the errors that really matter, and that you can do something about, like to retry a HTTP connection a few times before giving up. Beyond that, however, if there's nothing you can do about the error, there's no shame in letting it float up.
It seems like I have begin ... rescue ... end statements everywhere in my code! This doesn't seem like the correct thing to do.
Can anyone suggest how I can catch any exceptions without having to place everything inside begin ... rescue ... end? Any way to just tell Ruby to shut up and just keep going even if an exception is raised?
As in other languages, for any non-trivial program, you actually need a well-thought-out architecture for processing exceptions. One approach is to define exception-handling scopes within your project, and then you typically want to catch (rescue) exceptions at the scope boundaries. There is a trade-off. The closer you are in the stack to where the exception occurred, the more contextual information you have about the condition that triggered it. If you try to be too granular, you run into the problems that you have described. On the other hand, if you only catch exceptions at the top of the stack (in "main"), then there is no context. So defining the exception-handling scopes involves evaluating that tradeoff in relation to your particular program or system.
Ruby gives us the ability to "retry" -- not available in some other languages. This should be used sparingly! But where it makes sense (e.g. waiting for the network or a resource to be freed), such exceptions need to be handled very locally.
Otherwise, I tend to define exception scopes at a fairly coarse-grained level on a large project. It is often useful to capture some contextual information as the exception bubbles up from the point of origination through the various exception scope boundaries. To help with this, you can extend the Ruby exception class hierarchy by defining some of your own application-specific exception types, but again there are trade-offs. Your project should have clear standards about when to use custom exception types vs. capturing contextual data in the message field, what kind of information the message field should contain, etc., and a strategy for cataloguing the messages that your code can generate.
In most cases, exceptions can be allowed to propagate upward to a centralized handler, to be logged (for the technical team and support), to generate useful error messages for the user, and to determine whether the condition is serious enough to require your program to exit. Generally, all exceptions should be handled within your code or within the application framework you are using. No exceptions should be allowed to escape to the default exception handling of the language runtime or the OS.
Those are my thoughts based mostly on experience with other languages, but I think they apply pretty generally. Bottom line, on a large project you need to put quite a lot of effort into designing exception handling, vs. an ad hoc approach.
def action
yield
rescue
....
ensure
....
end
action { stuff_goes_here }
One thing that can make it look a bit cleaner is putting the rescue at the end of the method so you don't need a begin and another level of indentation:
def get_file_prompt
print "Enter file name: "
return File.open(read)
rescue StandardError
puts "File could not be opened"
return nil
end
Well, no. The whole point of exceptions is that they are conditions that must be handled.
Think of it this way: Exceptions are your friends! Without them you'd have to write lots of boring condition statements that would be hard to read and maintain.
If you are learning Ruby (or any language with an exception system), dealing with exceptions is one of the most important aspects, and you would do well to spend the time to figure out how to handle them, when to re-raise them, and when it is safe to ignore them.
I was going through the Programming Ruby book and I'm having problems understanding the following concepts:
The authors talk about "transient exceptions" that may occur in the code and then suggest a creation of your own exception object to handle them. I don't think I fully understand what a transient error is and when is it appropriate to make your own Exception object. He talks about it in Chapter 6 when he talks about defining exceptions:
For Example, certain types of network errors might be transient.Chapter 6.Page 97. Adding Information to exceptions.
I'm also having a hard time getting around the usage of Catch and Throw in ruby. When is this better than raise and rescue?
Can you give us a page reference to the "transient exceptions" line?
In any case, you can create a new exception any time, and it's commonly good to do so so that you can transfer more information about the fault. This is particularly good when you have alow level exception and want to make it into something more meaningful to the user.
Throw/Catch in Ruby are really a kind of nonlocal goto, like setjmp/longjmp in C, but better behaved. You'd use it anytime you want to tranfer execution a long ways.
Obviously, for the same reason you don't want to use goto much, you don't want to do this. A big reason you might use it is in a case of a program that needs to stay running, so if you catch certain kinds of errors, you might dump the whole piece of work you're doing and go back to the start.
Okay, that doesn't seem to be on page 97 of either of the editions of the pickaxe book I've got, but I see what it means. ... oh, here it is, page 157 in the third edition.
First of all, on the business about "transient", there are some network problems that can occur, and then resolve themselves, say when the BOFH unplugs the network cable and plugs it back in. So, under some conditions, you might want to give it a few seconds to settle down and try again before panicking. how would you do that?
In this case, they have you define a new kind of exception. This is done just with inheritance:
class RetryException < RuntimeError
# so this is a kind of RuntimeError which is a kind of Exception
attr: ok_to_retry
def initialize(ok_to_retry)
#ok_to_retry
end
end
so then if something goes wrong, you can raise on of these new retryable exceptions
raise RetryException.new(true), "transient read error"
which now sends something that is a kind of RuntimeError up the stack, but now has additional information attached to it, ie, a flag that says "yes, this can be retried."
NOw, here's a really nifty thing in Ruby: it has a built in capability of retrying some things. So, someplace up the stack, you have this code:
begin
# do something that raises this exception
do_something()
rescue RetryException => detail
# if the exception is one of these retryable ones,
# catch it here, but it in detail
if detail.ok_to_retry
retry
end
# this means exactly the same as 'retry if detail.ok_to_retry`
# from the book, btw
# if it STILL doesn't work, send the exception on
raise # just re-raises the last exception
end