Le eccezioni sono un tipo speciale di oggetti, un’istanza della classe
Exception o un discendente di essa. Sollevare un’eccezione significa interrompere la normale esecuzione del programma, trattare il problema che si è verificato o arrestare il programma. Tutto dipende dalla creazione della clausola
rescue, poiché se questa operazione non è stata eseguita, il programma verrà terminato.
Ruby ha delle classi predefinite —
Exception e le sue sottoclassi — che permettono di gestire gli errori che possono verificarsi nei programmi. La figura che segue, tratta dal libro
Programming Ruby, mostra la gerarchia delle eccezioni di Ruby:
<center>

</center>
Il metodo che segue solleva una eccezione ogni volta che viene chiamato; il suo secondo messaggio non verrà mai stampato. Segue un programma di esempio,
p043raise.rb:
<span style="font-size:1.0em">
def raise_exception
puts 'Prima dell'eccezione.'
raise 'Verifica dell'errore'
puts 'Dopo l'eccezione'
end
raise_exception</span>
L'output è:
<span style="font-size:1.0em">
>ruby p043raise.rb
Prima dell'eccezione.
raise.rb:3:in `raise_exception': An error has occured (RuntimeError)
from raise.rb:6
>Exit code: 1</span>
Il metodo
raise fa parte del modulo
Kernel. Per default,
raise crea un’eccezione della classe
RuntimeError. Per sollevare un’eccezione di una specifica classe, è possibile passare il nome della classe come argomento di
raise. Ecco il programma
p044inverse.rb:
<span style="font-size:1.0em">
def inverse(x)
raise ArgumentError, 'Argument is not numeric' unless x.is_a? Numeric
1.0 / x
end
puts inverse(2)
puts inverse('not a number')</span>
L'output è:
<span style="font-size:1.0em">
>ruby p044inverse.rb
0.5
inverse.rb:2:in `inverse': Argument is not numeric (ArgumentError)
from inverse.rb:6
>Exit code: 1</span>
Da tenere presente, i metodi che agiscono come interrogazioni prendono nomi con un
? che segue
s_a?; è un metodo nella classe
Object e restituisce
vero o
falso. Il modificatore
unless, quando agganciato alla fine di un normale comando, significa “esegui l'espressione precedente a meno che la condizione sia vera”. Per essere più specifici circa il prodursi di un errore, è possibile definire le proprie sottoclassi di
Exception:
<span style="font-size:1.0em">
class NotInvertibleError < StandardError
end</span>
Per gestire le eccezioni, racchiudiamo il codice che potrebbe sollevare un’eccezione in un blocco
begin/end e usiamo una o più clausole
rescue per dire a
Ruby il tipo di eccezione che vogliamo trattare. Il programma
0045handexcp.rb mostra questa procedura:
<span style="font-size:1.0em">
def raise_and_rescue
begin
puts 'Prima dell'eccezione'
raise 'Verifica dell'errore'
puts 'Dopo l'eccezione'
rescue
puts 'Problema risolto.'
end
puts 'Dopo l'inizio del blocco.'
end
raise_and_rescue</span>
L'output è:
<span style="font-size:1.0em">
>ruby p045handexcp.rb
Prima dell'eccezione.
Problema risolto.
Dopo l'inizio del blocco.
>Exit code: 0</span>
Da osservare che il codice interrotto dall'eccezione non viene mai eseguito. Una volta che l'eccezione è gestita, l'esecuzione continua immediatamente dopo il blocco
begin che l'ha generata.
Se si scrive una clausola
rescue senza lista di parametri, i parametri vengono per default da
StandardError. Ciascuna clausola
rescue può specificare più eccezioni da intercettare. Alla fine di ciascuna clausola
rescue si può dare a
Ruby il nome di una variabile locale nella quale viene ricevuta l'espressione corrispondente. I parametri della clausola
rescue possono essere espressioni arbitrarie (comprese chiamate ai metodi), che restituiscono una classe
Exception. Se utilizziamo
raise senza parametri, esso risolleverà l'eccezione. Si possono impilare clausole
rescue in un blocco
begin/rescue. Le eccezioni non gestite da una clausola
rescue vengono passate alla successiva.
<span style="font-size:1.0em">
begin
# -
rescue OneTypeOfException
# -
rescue AnotherTypeOfException
# -
end</span>
Per ogni clausola
rescue nel blocco
begin,
Ruby confronta le
Exception sollevate con ciascuno dei parametri a rotazione. Il confronto ha successo se l'eccezione nominata nella clausola
rescue ha lo stesso tipo di quella sollevata, o è una superclasse di quella eccezione. Se si vuole interrogare un’eccezione recuperata, è possibile mappare l'oggetto
Exception in una variabile all'interno della clausola
rescue, come mostrato nel programma
p046excpvar.rb:
<span style="font-size:1.0em">
begin
raise 'A test exception.'
rescue Exception => e
puts e.message
puts e.backtrace.inspect
end</span>
L'output è:
<span style="font-size:1.0em">
>ruby p046excpvar.rb
A test exception.
["excpvar.rb:2"]
>Exit code: 0</span>
Se si ha bisogno della garanzia che qualche elaborazione sia eseguita alla fine di un blocco di codice che si siano o meno verificate eccezioni, allora è necessaria una clausola
ensure,che deve essere messa alla fine dell'ultima clausola
rescue e contiene il pezzo di codice che deve essere sempre eseguito alla fine del blocco.
Ensure garantisce che il blocco verrà sempre eseguito. Alcune comuni eccezioni sono mostrate nella seguente tabella, tratta da
Ruby For Rails:
<center>

</center>
"Traduzione e adattamento a cura di Francesca Beatrice Cice. La versione originale del tutorial di
Satish Talim può essere trovata su
rubylearning.com"