What happens if we get a fatal error halfway through the download? We
certainly don't want to store an incomplete song in the song list.
``I Did It My *click*''.

Let's add some exception handling code and see how it helps.
We
enclose the code that could raise an exception in a
begin/end block and use rescue clauses to tell Ruby the
types of exceptions we want to handle. In this case we're interested
in trapping SystemCallError exceptions (and, by implication, any
exceptions that are subclasses of SystemCallError), so that's what
appears on the rescue line. In the error handling block, we
report the error, close and delete the output file, and then reraise
the exception.

opFile = File.open(opName, "w")
begin
# Exceptions raised by this code will
# be caught by the following rescue clause
while data = socket.read(512)
opFile.write(data)
end

When an exception is raised, and independent of any subsequent
exception handling, Ruby places a reference to the Exception
object associated with the exception in the global variable $!
(the exclamation point presumably mirroring our surprise that any of
our code could cause errors). In the previous example, we used
this variable to format our error message.

After closing and deleting the file, we call raise with no
parameters, which reraises the exception in $!. This is a
useful technique, as it allows you to write code that filters
exceptions, passing on those you can't handle to higher levels. It's
almost like implementing an inheritance hierarchy for error
processing.

You can have multiple rescue clauses in a begin block, and
each rescue clause can specify multiple exceptions to catch. At
the end of each rescue clause you can give Ruby the name of a local
variable to receive the matched exception. Many people find this more
readable than using $! all over the place.

How does Ruby decide which rescue clause to execute? It turns out that
the processing is pretty similar to that used by the case
statement. For each rescue clause in the begin block, Ruby
compares the raised exception against each of the parameters in turn.
If the raised exception matches a parameter, Ruby executes the body of
the rescue and stops looking. The match is made using
$!.kind_of?(parameter), and so will succeed if the parameter
has the same class as the exception or is an ancestor of the
exception. If you write a rescue clause with no parameter list,
the parameter defaults to StandardError.

If no rescue clause matches, or if an exception is raised outside
a begin/end block, Ruby moves up the stack
and looks for an
exception handler in the caller, then in the caller's caller, and so on.

Although the parameters to the rescue clause are typically the
names of Exception classes, they can actually be arbitrary
expressions (including method calls) that return an Exception class.