Ruby - Exceptions

L'exécution et l'exception vont toujours de pair. Si vous ouvrez un fichier qui n'existe pas, si vous n'avez pas géré correctement cette situation, votre programme est considéré comme de mauvaise qualité.

Le programme s'arrête si une exception se produit. Ainsi, les exceptions sont utilisées pour gérer divers types d'erreurs, qui peuvent survenir lors de l'exécution d'un programme et prendre les mesures appropriées au lieu d'arrêter complètement le programme.

Ruby fournit un mécanisme intéressant pour gérer les exceptions. Nous enfermons le code qui pourrait déclencher une exception dans un bloc de début / fin et utilisons des clauses de sauvetage pour indiquer à Ruby les types d'exceptions que nous voulons gérer.

Syntaxe

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

Tout du début au sauvetage est protégé. Si une exception se produit lors de l'exécution de ce bloc de code, le contrôle est passé au bloc entre le sauvetage et la fin .

Pour chaque clause de secours dans le bloc de début , Ruby compare l'exception levée à chacun des paramètres à son tour. La correspondance réussira si l'exception nommée dans la clause de sauvetage est identique au type de l'exception actuellement levée ou est une superclasse de cette exception.

Dans le cas où une exception ne correspond à aucun des types d'erreur spécifiés, nous sommes autorisés à utiliser une clause else après toutes les clauses de sauvetage .

Exemple

#!/usr/bin/ruby

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

Cela produira le résultat suivant. Vous pouvez voir que STDIN est remplacé par le fichier car l' ouverture a échoué.

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

Utilisation de l'instruction retry

Vous pouvez capturer une exception en utilisant le sauvetage bloc, puis utiliser la nouvelle tentative instruction à exécuter commencer bloc depuis le début.

Syntaxe

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

Exemple

#!/usr/bin/ruby

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

Voici le déroulement du processus -

  • Une exception s'est produite lors de l'ouverture.
  • Je suis allé à la rescousse. fname a été réaffecté.
  • Par réessayer est allé au début du début.
  • Ce fichier de temps s'ouvre avec succès.
  • Continué le processus essentiel.

NOTE- Notez que si le fichier de nom remplacé n'existe pas, cet exemple de code réessaye indéfiniment. Soyez prudent si vous utilisez une nouvelle tentative pour un processus d'exception.

Utilisation de l'instruction rise

Vous pouvez utiliser raise déclaration à soulever une exception. La méthode suivante lève une exception chaque fois qu'elle est appelée. Son deuxième message sera imprimé.

Syntaxe

raise 

OR

raise "Error Message" 

OR

raise ExceptionType, "Error Message"

OR

raise ExceptionType, "Error Message" condition

Le premier formulaire re-déclenche simplement l'exception actuelle (ou un RuntimeError s'il n'y a pas d'exception actuelle). Ceci est utilisé dans les gestionnaires d'exceptions qui doivent intercepter une exception avant de la transmettre.

Le deuxième formulaire crée une nouvelle exception RuntimeError , définissant son message sur la chaîne donnée. Cette exception est ensuite soulevée dans la pile d'appels.

Le troisième formulaire utilise le premier argument pour créer une exception, puis définit le message associé sur le deuxième argument.

Le quatrième formulaire est similaire au troisième formulaire, mais vous pouvez ajouter n'importe quelle instruction conditionnelle comme à moins de lever une exception.

Exemple

#!/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.'

Cela produira le résultat suivant -

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

Un autre exemple montrant l'utilisation de rais -

#!/usr/bin/ruby

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

Cela produira le résultat suivant -

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

Utilisation de la déclaration ensure

Parfois, vous devez garantir que certains traitements sont effectués à la fin d'un bloc de code, indépendamment du fait qu'une exception ait été déclenchée. Par exemple, vous pouvez avoir un fichier ouvert à l'entrée du bloc et vous devez vous assurer qu'il se ferme à la sortie du bloc.

La assurer clause ne vient cela. s'assure va après la dernière clause de sauvetage et contient un morceau de code qui sera toujours exécuté lorsque le bloc se termine. Peu importe si le bloc se termine normalement, s'il lève et sauve une exception, ou s'il est terminé par une exception non interceptée, le bloc s'assure sera exécuté.

Syntaxe

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

Exemple

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

Cela produira le résultat suivant -

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

Utilisation de l'instruction else

Si la clause else est présente, elle passe après les clauses de sauvetage et avant toute garantie .

Le corps d'une clause else n'est exécuté que si aucune exception n'est levée par le corps principal du code.

Syntaxe

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

Exemple

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

Cela produira le résultat suivant -

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

Le message d'erreur soulevé peut être capturé en utilisant $! variable.

Attraper et lancer

Bien que le mécanisme d'exception de relance et de sauvetage soit idéal pour abandonner l'exécution lorsque les choses tournent mal, il est parfois agréable de pouvoir sortir d'une construction profondément imbriquée pendant le traitement normal. C'est là que la capture et le lancer sont utiles.

Le catch définit un bloc étiqueté avec le nom donné (qui peut être un symbole ou une chaîne). Le bloc est exécuté normalement jusqu'à ce qu'un lancer soit rencontré.

Syntaxe

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

Exemple

L'exemple suivant utilise un throw pour mettre fin à l'interaction avec l'utilisateur si '!' est tapé en réponse à n'importe quelle invite.

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:")

Vous devriez essayer le programme ci-dessus sur votre machine car il nécessite une interaction manuelle. Cela produira le résultat suivant -

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

Exception de classe

Les classes et modules standard de Ruby lèvent des exceptions. Toutes les classes d'exception forment une hiérarchie, avec la classe Exception en haut. Le niveau suivant contient sept types différents -

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

Il existe une autre exception à ce niveau, Fatal, mais l'interpréteur Ruby l'utilise uniquement en interne.

ScriptError et StandardError ont un certain nombre de sous-classes, mais nous n'avons pas besoin d'entrer dans les détails ici. L'important est que si nous créons nos propres classes d'exceptions, elles doivent être des sous-classes de la classe Exception ou de l'un de ses descendants.

Regardons un exemple -

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

Maintenant, regardez l'exemple suivant, qui utilisera cette exception -

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

La ligne importante ici est augmenter FileSaveError.new ($!) . Nous appelons rais pour signaler qu'une exception s'est produite, en lui passant une nouvelle instance de FileSaveError, la raison étant que l'exception spécifique a provoqué l'échec de l'écriture des données.