Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Begin, Rescue and Ensure in Ruby?

I've recently started programming in Ruby, and I am looking at exception handling.

I was wondering if ensure was the Ruby equivalent of finally in C#? Should I have:

file = File.open("myFile.txt", "w")  begin   file << "#{content} \n" rescue   #handle the error here ensure   file.close unless file.nil? end 

or should I do this?

#store the file file = File.open("myFile.txt", "w")  begin   file << "#{content} \n"   file.close rescue   #handle the error here ensure   file.close unless file.nil? end 

Does ensure get called no matter what, even if an exception isn't raised?

like image 338
Lloyd Powell Avatar asked Feb 03 '10 11:02

Lloyd Powell


People also ask

What is begin rescue in Ruby?

The code between “begin” and “rescue” is where a probable exception might occur. If an exception occurs, the rescue block will execute. You should try to be specific about what exception you're rescuing because it's considered a bad practice to capture all exceptions.

What is ensure in Ruby?

ensure goes after the last rescue clause and contains a chunk of code that will always be executed as the block terminates. It doesn't matter if the block exits normally, if it raises and rescues an exception, or if it is terminated by an uncaught exception, the ensure block will get run.

How do you handle exceptions in Ruby?

Ruby also provides a separate class for an exception that is known as an Exception class which contains different types of methods. The code in which an exception is raised, is enclosed between the begin/end block, so you can use a rescue clause to handle this type of exception.

How do you raise exceptions in Ruby?

Ruby actually gives you the power to manually raise exceptions yourself by calling Kernel#raise. This allows you to choose what type of exception to raise and even set your own error message. If you do not specify what type of exception to raise, Ruby will default to RuntimeError (a subclass of StandardError ).


1 Answers

Yes, ensure ensures that the code is always evaluated. That's why it's called ensure. So, it is equivalent to Java's and C#'s finally.

The general flow of begin/rescue/else/ensure/end looks like this:

begin   # something which might raise an exception rescue SomeExceptionClass => some_variable   # code that deals with some exception rescue SomeOtherException => some_other_variable   # code that deals with some other exception else   # code that runs only if *no* exception was raised ensure   # ensure that this code always runs, no matter what   # does not change the final value of the block end 

You can leave out rescue, ensure or else. You can also leave out the variables in which case you won't be able to inspect the exception in your exception handling code. (Well, you can always use the global exception variable to access the last exception that was raised, but that's a little bit hacky.) And you can leave out the exception class, in which case all exceptions that inherit from StandardError will be caught. (Please note that this does not mean that all exceptions are caught, because there are exceptions which are instances of Exception but not StandardError. Mostly very severe exceptions that compromise the integrity of the program such as SystemStackError, NoMemoryError, SecurityError, NotImplementedError, LoadError, SyntaxError, ScriptError, Interrupt, SignalException or SystemExit.)

Some blocks form implicit exception blocks. For example, method definitions are implicitly also exception blocks, so instead of writing

def foo   begin     # ...   rescue     # ...   end end 

you write just

def foo   # ... rescue   # ... end 

or

def foo   # ... ensure   # ... end 

The same applies to class definitions and module definitions.

However, in the specific case you are asking about, there is actually a much better idiom. In general, when you work with some resource which you need to clean up at the end, you do that by passing a block to a method which does all the cleanup for you. It's similar to a using block in C#, except that Ruby is actually powerful enough that you don't have to wait for the high priests of Microsoft to come down from the mountain and graciously change their compiler for you. In Ruby, you can just implement it yourself:

# This is what you want to do: File.open('myFile.txt', 'w') do |file|   file.puts content end  # And this is how you might implement it: def File.open(filename, mode='r', perm=nil, opt=nil)   yield filehandle = new(filename, mode, perm, opt) ensure   filehandle&.close end 

And what do you know: this is already available in the core library as File.open. But it is a general pattern that you can use in your own code as well, for implementing any kind of resource cleanup (à la using in C#) or transactions or whatever else you might think of.

The only case where this doesn't work, if acquiring and releasing the resource are distributed over different parts of the program. But if it is localized, as in your example, then you can easily use these resource blocks.


BTW: in modern C#, using is actually superfluous, because you can implement Ruby-style resource blocks yourself:

class File {     static T open<T>(string filename, string mode, Func<File, T> block)     {         var handle = new File(filename, mode);         try         {             return block(handle);         }         finally         {             handle.Dispose();         }     } }  // Usage:  File.open("myFile.txt", "w", (file) => {     file.WriteLine(contents); }); 
like image 173
Jörg W Mittag Avatar answered Oct 02 '22 15:10

Jörg W Mittag