Ruby - Eccezioni

L'esecuzione e l'eccezione vanno sempre insieme. Se stai aprendo un file, che non esiste, se non hai gestito correttamente questa situazione, il tuo programma è considerato di cattiva qualità.

Il programma si interrompe se si verifica un'eccezione. Quindi le eccezioni vengono utilizzate per gestire vari tipi di errori, che possono verificarsi durante l'esecuzione di un programma e intraprendere l'azione appropriata invece di arrestare completamente il programma.

Ruby fornisce un bel meccanismo per gestire le eccezioni. Racchiudiamo il codice che potrebbe sollevare un'eccezione in un blocco di inizio / fine e usiamo clausole di salvataggio per dire a Ruby i tipi di eccezioni che vogliamo gestire.

Sintassi

begin  
# -  
rescue OneTypeOfException  
# -  
rescue AnotherTypeOfException  
# -  
else  
# Other exceptions
ensure
# Always will be executed
end

Tutto, dall'inizio al salvataggio, è protetto. Se si verifica un'eccezione durante l'esecuzione di questo blocco di codice, il controllo viene passato al blocco tra rescue e end .

Per ogni salvataggio clausola nel cominciare blocco, Ruby confronta l'Eccezione sollevata contro ciascuno dei parametri a loro volta. La corrispondenza avrà esito positivo se l'eccezione denominata nella clausola di salvataggio è la stessa del tipo dell'eccezione attualmente generata o è una superclasse di tale eccezione.

Nel caso in cui un'eccezione non corrisponda a nessuno dei tipi di errore specificati, siamo autorizzati a utilizzare una clausola else dopo tutte le clausole di salvataggio .

Esempio

#!/usr/bin/ruby

begin
   file = open("/unexistant_file")
   if file
      puts "File opened successfully"
   end
rescue
      file = STDIN
end
print file, "==", STDIN, "\n"

Questo produrrà il seguente risultato. Puoi vedere che STDIN è stato sostituito al file perché l' apertura non è riuscita.

#<IO:0xb7d16f84>==#<IO:0xb7d16f84>

Utilizzo dell'istruzione retry

È possibile catturare un'eccezione utilizzando salvataggio del blocco e quindi l'uso di tentativi istruzione da eseguire iniziare blocco fin dall'inizio.

Sintassi

begin
   # Exceptions raised by this code will 
   # be caught by the following rescue clause
rescue
   # This block will capture all types of exceptions
   retry  # This will move control to the beginning of begin
end

Esempio

#!/usr/bin/ruby

begin
   file = open("/unexistant_file")
   if file
      puts "File opened successfully"
   end
rescue
   fname = "existant_file"
   retry
end

Quello che segue è il flusso del processo:

  • Si è verificata un'eccezione all'apertura.
  • Sono andato a salvare. fname è stato riassegnato.
  • Riprovando è andato all'inizio dell'inizio.
  • Questo file ora si apre correttamente.
  • Ha continuato il processo essenziale.

NOTE- Notare che se il file di nome ricostituito non esiste, questo codice di esempio riprova all'infinito. Fai attenzione se utilizzi Riprova per un processo di eccezione.

Utilizzo dell'istruzione raise

È possibile utilizzare rilancio dichiarazione per sollevare un'eccezione. Il metodo seguente solleva un'eccezione ogni volta che viene chiamato. Il secondo messaggio verrà stampato.

Sintassi

raise 

OR

raise "Error Message" 

OR

raise ExceptionType, "Error Message"

OR

raise ExceptionType, "Error Message" condition

La prima forma solleva semplicemente nuovamente l'eccezione corrente (o un RuntimeError se non c'è eccezione corrente). Viene utilizzato nei gestori di eccezioni che devono intercettare un'eccezione prima di trasmetterla.

La seconda forma crea una nuova eccezione RuntimeError , impostando il suo messaggio sulla stringa data. Questa eccezione viene quindi sollevata nello stack di chiamate.

La terza forma utilizza il primo argomento per creare un'eccezione e quindi imposta il messaggio associato al secondo argomento.

La quarta forma è simile alla terza ma puoi aggiungere qualsiasi istruzione condizionale come a meno che non sollevi un'eccezione.

Esempio

#!/usr/bin/ruby

begin  
   puts 'I am before the raise.'  
   raise 'An error has occurred.'  
   puts 'I am after the raise.'  
rescue  
   puts 'I am rescued.'  
end  
puts 'I am after the begin block.'

Questo produrrà il seguente risultato:

I am before the raise.  
I am rescued.  
I am after the begin block.

Un altro esempio che mostra l'uso del rilancio -

#!/usr/bin/ruby

begin  
   raise 'A test exception.'  
rescue Exception => e  
   puts e.message  
   puts e.backtrace.inspect  
end

Questo produrrà il seguente risultato:

A test exception.
["main.rb:4"]

Utilizzando la dichiarazione di sicurezza

A volte, è necessario garantire che alcune elaborazioni vengano eseguite alla fine di un blocco di codice, indipendentemente dal fatto che sia stata sollevata un'eccezione. Ad esempio, potresti avere un file aperto all'ingresso del blocco e devi assicurarti che venga chiuso quando il blocco esce.

La clausola di garanzia fa proprio questo. garantire va dopo l'ultima clausola di salvataggio e contiene una porzione di codice che verrà sempre eseguita al termine del blocco. Non importa se il blocco esce normalmente, se solleva e salva un'eccezione o se viene terminato da un'eccezione non rilevata, il blocco di sicurezza verrà eseguito.

Sintassi

begin 
   #.. process 
   #..raise exception
rescue 
   #.. handle error 
ensure 
   #.. finally ensure execution
   #.. This will always execute.
end

Esempio

begin
   raise 'A test exception.'
rescue Exception => e
   puts e.message
   puts e.backtrace.inspect
ensure
   puts "Ensuring execution"
end

Questo produrrà il seguente risultato:

A test exception.
["main.rb:4"]
Ensuring execution

Utilizzo dell'istruzione else

Se la clausola else è presente, va dopo le clausole di salvataggio e prima di qualsiasi garanzia .

Il corpo di una clausola else viene eseguito solo se non vengono sollevate eccezioni dal corpo principale del codice.

Sintassi

begin 
   #.. process 
   #..raise exception
rescue 
   # .. handle error
else
   #.. executes if there is no exception
ensure 
   #.. finally ensure execution
   #.. This will always execute.
end

Esempio

begin
   # raise 'A test exception.'
   puts "I'm not raising exception"
rescue Exception => e
   puts e.message
   puts e.backtrace.inspect
else
   puts "Congratulations-- no errors!"
ensure
   puts "Ensuring execution"
end

Questo produrrà il seguente risultato:

I'm not raising exception
Congratulations-- no errors!
Ensuring execution

Il messaggio di errore generato può essere catturato usando $! variabile.

Prendi e lancia

Mentre il meccanismo di eccezione di rilancio e salvataggio è ottimo per abbandonare l'esecuzione quando le cose vanno male, a volte è bello essere in grado di saltare fuori da qualche costrutto profondamente annidato durante la normale elaborazione. Qui è dove prendere e lanciare torna utile.

Il fermo definisce un blocco che è etichettato con il nome dato (che può essere un simbolo o una stringa). Il blocco viene eseguito normalmente fino a quando non si incontra un lancio.

Sintassi

throw :lablename
#.. this will not be executed
catch :lablename do
#.. matching catch will be executed after a throw is encountered.
end

OR

throw :lablename condition
#.. this will not be executed
catch :lablename do
#.. matching catch will be executed after a throw is encountered.
end

Esempio

L'esempio seguente utilizza un lancio per terminare l'interazione con l'utente se "!" viene digitato in risposta a qualsiasi richiesta.

def promptAndGet(prompt)
   print prompt
   res = readline.chomp
   throw :quitRequested if res == "!"
   return res
end

catch :quitRequested do
   name = promptAndGet("Name: ")
   age = promptAndGet("Age: ")
   sex = promptAndGet("Sex: ")
   # ..
   # process information
end
promptAndGet("Name:")

Dovresti provare il programma sopra sulla tua macchina perché necessita di interazione manuale. Questo produrrà il seguente risultato:

Name: Ruby on Rails
Age: 3
Sex: !
Name:Just Ruby

Eccezione di classe

Le classi ei moduli standard di Ruby sollevano eccezioni. Tutte le classi di eccezione formano una gerarchia, con la classe Eccezione in alto. Il livello successivo contiene sette diversi tipi:

  • Interrupt
  • NoMemoryError
  • SignalException
  • ScriptError
  • StandardError
  • SystemExit

C'è un'altra eccezione a questo livello, Fatal, ma l'interprete Ruby lo utilizza solo internamente.

Sia ScriptError che StandardError hanno un numero di sottoclassi, ma non è necessario entrare nei dettagli qui. La cosa importante è che se creiamo le nostre classi di eccezione, devono essere sottoclassi della classe Exception o di uno dei suoi discendenti.

Diamo un'occhiata a un esempio:

class FileSaveError < StandardError
   attr_reader :reason
   def initialize(reason)
      @reason = reason
   end
end

Ora, guarda il seguente esempio, che utilizzerà questa eccezione:

File.open(path, "w") do |file|
begin
   # Write out the data ...
rescue
   # Something went wrong!
   raise FileSaveError.new($!)
end
end

La riga importante qui è raise FileSaveError.new ($!) . Chiamiamo raise per segnalare che si è verificata un'eccezione, passandogli una nuova istanza di FileSaveError, con il motivo che l'eccezione specifica ha causato il fallimento della scrittura dei dati.