There may be some circumstances in which you want to take some particular action whether or not an exception occurs. For example, whenever you are dealing with some kind of unpredictable input/output—say, when working with files and directories on disk—there is always the possibility that the location (the disk or directory) or the data source (the file) either may not be there at all or may provide some other kinds of problems—such as the disk being full when you attempt to write to it or the file containing the wrong kind of data when you attempt to read from it.
You may need to perform some final “cleanup” procedures whether or not you have encountered any problems, such as logging onto a specific working directory or closing a file that was previously opened. You can do this by following a begin..rescue
block of code with another block starting with the ensure
keyword. The code in the ensure
block will always execute, whether or not an exception has arisen beforehand.
Let’s look at two simple examples. In the first one, I try to log onto a disk and display the directory listing. At the end of this, I want to be sure that my working directory (given by Dir.getwd
) is always restored to its original location. I do this by saving the original directory in the startdir
variable and once again making this the working directory in the ensure
block:
ensure.rb
startdir = Dir.getwd begin Dir.chdir( "X:\" ) puts( `dir` ) rescue Exception => e puts e.class puts e ensure Dir.chdir( startdir ) end
When I run this, the following is displayed:
We start out here: C:/Huw/programming/bookofruby/ch9 Errno::ENOENT No such file or directory - X: We end up here: C:/Huw/programming/bookofruby/ch9
Let’s now see how to deal with the problem of reading the incorrect data from a file. This might happen if the data is corrupt, if you accidentally open the wrong file, or—quite simply—if your program code contains a bug.
Here I have a file, test.txt, containing six lines. The first five lines are numbers; the sixth line is a string, “six.” My code opens this file and reads in all six lines:
f = File.new( "test.txt" ) begin for i in (1..6) do puts("line number: #{f.lineno}") line = f.gets.chomp num = line.to_i puts( "Line '#{line}' is converted to #{num}" ) puts( 100 / num ) end rescue Exception => e puts( e.class ) puts( e ) ensure f.close puts( "File closed" ) end
The lines are read in as strings (using gets
), and the code attempts to convert them to integers (using to_i
). No error is produced when the conversion fails; instead, Ruby returns the value 0. The problem arises in the next line of code, which attempts a division by the converted number.
Having opened the data file at the outset, I want to ensure that the file is closed whether or not an error occurs. If, for example, I read in only the first five lines by editing the range in the for
loop to (1..5)
, then there would be no exception. I would still want to close the file. But it would be no good putting the file-closing code (f.close
) in the rescue
clause because it would not, in this case, be executed. By putting it in the ensure
clause, however, I can be certain that the file will be closed whether or not an exception occurs.
18.118.139.224