Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are there alternatives to rescue LoadError for Ruby?

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:

  1. 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.
  2. 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.
like image 395
Andrew Grimm Avatar asked Jun 20 '11 00:06

Andrew Grimm


2 Answers

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!

like image 122
Alex D Avatar answered Oct 21 '22 22:10

Alex D


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).

like image 40
mu is too short Avatar answered Oct 21 '22 22:10

mu is too short