Ruby Language
eccezioni
Ricerca…
Osservazioni
Un'eccezione è un oggetto che rappresenta il verificarsi di una condizione eccezionale. In altre parole, indica che qualcosa è andato storto.
In Ruby, le eccezioni sono spesso definite errori . Questo perché la classe Exception
base esiste come elemento di un oggetto di eccezione di livello superiore, ma le eccezioni di esecuzione definite dall'utente sono generalmente StandardError
o discendenti.
Alzare un'eccezione
Per generare un'eccezione usa Kernel#raise
passando la classe di eccezione e / o il messaggio:
raise StandardError # raises a StandardError.new
raise StandardError, "An error" # raises a StandardError.new("An error")
Puoi anche semplicemente passare un messaggio di errore. In questo caso, il messaggio è avvolto in un RuntimeError
:
raise "An error" # raises a RuntimeError.new("An error")
Ecco un esempio:
def hello(subject)
raise ArgumentError, "`subject` is missing" if subject.to_s.empty?
puts "Hello #{subject}"
end
hello # => ArgumentError: `subject` is missing
hello("Simone") # => "Hello Simone"
Creazione di un tipo di eccezione personalizzato
Un'eccezione personalizzata è qualsiasi classe che estende Exception
o una sottoclasse di Exception
.
In generale, si dovrebbe sempre estendere l'errore StandardError
o un discendente. La famiglia Exception
è in genere per errori di macchina virtuale o di sistema, il loro salvataggio può impedire un'interruzione forzata di funzionare come previsto.
# Defines a new custom exception called FileNotFound
class FileNotFound < StandardError
end
def read_file(path)
File.exist?(path) || raise(FileNotFound, "File #{path} not found")
File.read(path)
end
read_file("missing.txt") #=> raises FileNotFound.new("File `missing.txt` not found")
read_file("valid.txt") #=> reads and returns the content of the file
È comune denominare le eccezioni aggiungendo il suffisso Error
alla fine:
-
ConnectionError
-
DontPanicError
Tuttavia, quando l'errore è autoesplicativo, non è necessario aggiungere il suffisso Error
perché sarebbe ridondante:
-
FileNotFound
vsFileNotFoundError
-
DatabaseExploded
vsDatabaseExplodedError
Gestire un'eccezione
Usa il blocco begin/rescue
per rilevare (salvare) un'eccezione e gestirla:
begin
# an execution that may fail
rescue
# something to execute in case of failure
end
Una clausola di rescue
è analoga a un blocco catch
in un linguaggio di parentesi graffa come C # o Java.
Un semplice rescue
come questo salva StandardError
.
Nota: fare attenzione ad evitare l'intercettazione di Exception
anziché l'errore StandardError
predefinito. La classe Exception
include SystemExit
e NoMemoryError
e altre eccezioni serie che di solito non si desidera NoMemoryError
. Considerare sempre la cattura di StandardError
(il valore predefinito).
È inoltre possibile specificare la classe di eccezioni da salvare:
begin
# an excecution that may fail
rescue CustomError
# something to execute in case of CustomError
# or descendant
end
Questa clausola di salvataggio non catturerà alcuna eccezione che non sia un'eccezione CustomError
.
È inoltre possibile memorizzare l'eccezione in una variabile specifica:
begin
# an excecution that may fail
rescue CustomError => error
# error contains the exception
puts error.message # provide human-readable details about what went wrong.
puts error.backtrace.inspect # return an array of strings that represent the call stack
end
Se non si è riusciti a gestire un'eccezione, è possibile aumentarla in qualsiasi momento in un blocco di salvataggio.
begin
#here goes your code
rescue => e
#failed to handle
raise e
end
Se si desidera ripetere la begin
blocco, chiamare retry
:
begin
#here goes your code
rescue StandardError => e
#for some reason you want to retry you code
retry
end
Puoi essere bloccato in un ciclo se rilevi un'eccezione in ogni nuovo tentativo. Per evitare ciò, limita il tuo retry_count
a un certo numero di tentativi.
retry_count = 0
begin
# an excecution that may fail
rescue
if retry_count < 5
retry_count = retry_count + 1
retry
else
#retry limit exceeds, do something else
end
Puoi anche fornire un blocco else
o un blocco di ensure
. Un else
blocco sarà eseguito quando il begin
blocco completa senza eccezione generata. Un blocco di ensure
verrà sempre eseguito. Un ensure
blocco è analogo ad un finally
blocco in una lingua parentesi graffa come C # o Java.
begin
# an execution that may fail
rescue
# something to execute in case of failure
else
# something to execute in case of success
ensure
# something to always execute
end
Se si è all'interno di un def
, module
o blocco di class
, non è necessario utilizzare l'istruzione begin.
def foo
...
rescue
...
end
Gestire più eccezioni
È possibile gestire più errori nella stessa dichiarazione di rescue
:
begin
# an execution that may fail
rescue FirstError, SecondError => e
# do something if a FirstError or SecondError occurs
end
Puoi anche aggiungere più dichiarazioni di rescue
:
begin
# an execution that may fail
rescue FirstError => e
# do something if a FirstError occurs
rescue SecondError => e
# do something if a SecondError occurs
rescue => e
# do something if a StandardError occurs
end
L'ordine dei blocchi di rescue
è rilevante: la prima partita è quella eseguita. Pertanto, se si imposta StandardError
come prima condizione e tutte le eccezioni ereditate da StandardError
, le altre istruzioni di rescue
non verranno mai eseguite.
begin
# an execution that may fail
rescue => e
# this will swallow all the errors
rescue FirstError => e
# do something if a FirstError occurs
rescue SecondError => e
# do something if a SecondError occurs
end
Alcuni blocchi hanno una gestione delle eccezioni implicita come def
, class
e module
. Questi blocchi ti consentono di saltare l'istruzione di begin
.
def foo
...
rescue CustomError
...
ensure
...
end
Aggiunta di informazioni a eccezioni (personalizzate)
Potrebbe essere utile includere informazioni aggiuntive con un'eccezione, ad esempio per la registrazione o per consentire la gestione condizionale quando viene rilevata l'eccezione:
class CustomError < StandardError
attr_reader :safe_to_retry
def initialize(safe_to_retry = false, message = 'Something went wrong')
@safe_to_retry = safe_to_retry
super(message)
end
end
Sollevare l'eccezione:
raise CustomError.new(true)
Cattura l'eccezione e l'accesso alle informazioni aggiuntive fornite:
begin
# do stuff
rescue CustomError => e
retry if e.safe_to_retry
end