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.
Related
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've always followed the logic: if assert fails, then there is a bug. Root cause could either be:
Assert itself is invalid (bug)
There is a programming error (bug)
(no other options)
I.E. Are there any other conclusions one could come to? Are there cases where an assert would fail and there is no bug?
If assert fails there is a bug in either the caller or callee. Why else would there be an assertion?
Yes, there is a bug in the code.
Code Complete
Assertions check for conditions that
should never occur. [...]
If an
assertion is fired for an anomalous
condition, the corrective action is
not merely to handle an error
gracefully- the corrective action is
to change the program's source code,
recompile, and release a new version
of the software.
A good way to
think of assertions is as executable
documentation - you can't rely on them
to make the code work, but they can
document assumptions more actively
than program-language comments can.
That's a good question.
My feeling is, if the assert fails due to your code, then it is a bug. The assertion is an expected behaviour/result of your code, so an assertion failure will be a failure of your code.
Only if the assert was meant to show a warning condition - in which case a special class of assert should have been used.
So, any assert should show a bug as you suggest.
If you are using assertions you're following Bertrand Meyer's Design by Contract philosophy. It's a programming error - the contract (assertion) you have specified is not being followed by the client (caller).
If you are trying to be logically inclusive about all the possibilities, remember that electronic circuitry is known to be affected by radiation from space. If the right photon/particle hits in just the right place at just the right time, it can cause an otherwise logically impossible state transition.
The probability is vanishingly small but still non-zero.
I can think of one case that wouldn't really class as a bug:
An assert placed to check for something external that normally should be there. You're hunting something nutty that occurs on one machine and you want to know if a certain factor is responsible.
A real world example (although from before the era of asserts): If a certain directory was hidden on a certain machine the program would barf. I never found any piece of code that should have cared if the directory was hidden. I had only very limited access to the offending machine (it had a bunch of accounting stuff on it) so I couldn't hunt it properly on the machine and I couldn't reproduce it elsewhere. Something that was done with that machine (the culprit was never identified) occasionally turned that directory hidden.
I finally resorted to putting a test in the startup to see if the directory was hidden and stopping with an error if it was.
No. An assertion failure means something happened that the original programmer did not intend or expect to occur.
This can indicate:
A bug in your code (you are simply calling the method incorrectly)
A bug in the Assertion (the original programmer has been too zealous and is complaining about you doing something that is quite reasonable and the method will actually handle perfectly well.
A bug in the called code (a design flaw). That is, the called code provides a contract that does not allow you to do what you need to do. The assertion warns you that you can't do things that way, but the solution is to extend the called method to handle your input.
A known but unimplemented feature. Imagine I implement a method that could process positive and negative integers, but I only need it (for now) to handle positive ones. I know that the "perfect" implementation would handle both, but until I actually need it to handle negatives, it is a waste of effort to implement support (and it would add code bloat and possibly slow down my application). So I have considered the case but I decide not to implement it until the need is proven. I therefore add an assert to mark this unimplemented code. When I later trigger the assert by passing a negative value in, I know that the additional functionality is now needed, so I must augment the implementation. Deferring writing the code until it is actually required thus saves me a lot of time (in most cases I never imeplement the additiona feature), but the assert makes sure that I don't get any bugs when I try to use the unimplemented feature.
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 have a procedure that returns an OUT parameter.
procedure foo (in_v IN INTEGER, out_v OUT integer)
BEGIN
...
EXCEPTION
WHEN OTHERS THEN
--sh*t happend
out_v := SQLCODE;
END
That parameter will be 0 if everything goes OK, and <> 0 if something ugly happened.
Now, if sh*t happens along the way, an exception will be thrown.
Is it ok to assing the SQLCODE value to the OUT parameter ? Or is this consideres a code smell, and I will be expelled from the programming community ?
Thanks in advance.
If there is no additional handling of the error, I would probably advise against it. This approach just makes it necessary for each caller to examine the value of the out parameter anyway. And if the caller forgets it, a serious problem may pass unnoticed at first and create a hard to debug problem elsewhere.
If you simply don't catch OTHERS here, you ensure that the caller has to explicitly catch it, which is a lot cleaner an easier to debug.
Whether it's OK or not depends on what you're trying to achieve with it, I suppose. What problem are you trying to solve, or what requirement are you trying to meet?
the usual behaviour with errors is to gracefully handle the ones that you expect might happen during normal functioning and to allow those that you do not expect to be raised, so this does look odd.
Its ok, but I don't recommend it. By doing it this way you're forcing the calling code to do non-standard error handling. This is fine if you are the only person ever calling the code and you remember to check the error code. However, if you're coding in a large system with multiple programmers I think you should be kind to your fellow programmers and follow the standard way of exception handling supported by the language.
If you do decide to go down that route, also pass back SQLERRM as without the error text you only have the error code to go by. After years of catching "-20001" for the hundreds of application errors in 3rd party software the SQLERRM is often important.
This coding style is not a good idea for a number of reasons.
First, it is bad practice to mix errors and exceptions, even worse practice to mix return values and exceptions. Exceptions are meant to track exceptional situations - things completely unexpected by normal programming such as out of memory issues, conversion problems and so on that you normally would not want to write handlers at every call site for but need to deal with at some level.
The second worrying aspect of the coding style is that your code is effectively saying when an exception is encountered, the code will signal to its caller that something bad has happened but will happily throw away ALL exception information except for the SQLCODE.
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