type - throw exception in ruby




Pourquoi est-ce mauvais style de `sauver Exception=> e` dans Ruby? (4)

C'est un cas spécifique de la règle selon laquelle vous ne devez pas attraper une exception que vous ne savez pas gérer. Si vous ne savez pas comment le gérer, il est toujours préférable de laisser une autre partie du système attraper et gérer.

Ruby QuickRef de Ryan Davis dit (sans explication):

Ne pas sauver l'exception. DÉJÀ. ou je vais te poignarder.

Pourquoi pas? Quelle est la bonne chose à faire?


Cela vous cachera également des bogues, par exemple si vous avez mal tapé un nom de méthode:

def my_fun
  "my_fun"
end

begin
 # you mistypped my_fun to my_func
 my_func # my_func()
rescue Exception
  # rescued NameError (or NoMethodError if you called method with parenthesis)
end

La vraie règle est: Ne jetez pas les exceptions. L'objectivité de l'auteur de votre citation est discutable, comme en témoigne le fait qu'il se termine par

ou je vais te poignarder

Bien sûr, sachez que les signaux (par défaut) émettent des exceptions, et que les processus normalement longs sont terminés par un signal, donc attraper Exception et ne pas terminer sur les exceptions de signal rendra votre programme très difficile à arrêter. Donc, ne faites pas ceci:

#! /usr/bin/ruby

while true do
  begin
    line = STDIN.gets
    # heavy processing
  rescue Exception => e
    puts "caught exception #{e}! ohnoes!"
  end
end

Non, vraiment, ne le fais pas. Ne lance même pas ça pour voir si ça marche.

Cependant, disons que vous avez un serveur threadé et que vous voulez que toutes les exceptions ne le soient pas:

  1. être ignoré (par défaut)
  2. Arrêtez le serveur (ce qui arrive si vous dites thread.abort_on_exception = true ).

Ensuite, cela est parfaitement acceptable dans votre thread de gestion de connexion:

begin
  # do stuff
rescue Exception => e
  myLogger.error("uncaught #{e} exception while handling connection: #{e.message}")
    myLogger.error("Stack trace: #{backtrace.map {|l| "  #{l}\n"}.join}")
end

Ce qui précède est une variante du gestionnaire d'exception par défaut de Ruby, avec l'avantage de ne pas tuer votre programme. Rails fait cela dans son gestionnaire de requêtes.

Les exceptions de signal sont déclenchées dans le thread principal. Les threads d'arrière-plan ne les obtiendront pas, donc il ne sert à rien d'essayer de les attraper là.

Ceci est particulièrement utile dans un environnement de production, où vous ne voulez pas que votre programme s'arrête simplement quand quelque chose ne va pas. Ensuite, vous pouvez prendre les décharges de la pile dans vos journaux et les ajouter à votre code pour traiter des exceptions spécifiques plus loin dans la chaîne d'appel et de manière plus gracieuse.

Notez aussi qu'il existe un autre idiome de Ruby qui a à peu près le même effet:

a = do_something rescue "something else"

Dans cette ligne, si do_something déclenche une exception, elle est interceptée par Ruby, rejetée et assignée à "something else" .

Généralement, ne faites pas cela, sauf dans des cas spéciaux où vous savez que vous n'avez pas besoin de vous inquiéter. Un exemple:

debugger rescue nil

La fonction de debugger est une façon plutôt sympa de définir un point d'arrêt dans votre code, mais si elle s'exécute en dehors d'un débogueur, et Rails, elle déclenche une exception. Maintenant, en théorie, vous ne devriez pas laisser de code de débogage dans votre programme (pff! Nobody ne le fait pas!) Mais vous pourriez vouloir le garder pendant un certain temps pour une raison quelconque, mais pas continuellement votre débogueur.

Remarque:

  1. Si vous avez exécuté le programme de quelqu'un d'autre qui capture les exceptions de signal et les ignore, (disons le code ci-dessus) alors:

    • pgrep ruby Linux, dans un shell, tapez pgrep ruby ou ps | grep ruby ps | grep ruby , recherchez le PID de votre programme incriminé, puis exécutez kill -9 <PID> .
    • dans Windows, utilisez le Gestionnaire des tâches ( CTRL - SHIFT - ESC ), allez dans l'onglet "processus", trouvez votre processus, faites un clic droit dessus et sélectionnez "Terminer le processus".
  2. Si vous travaillez avec le programme de quelqu'un d'autre qui est, pour une raison quelconque, parsemé de ces blocs d'exception-exception, alors mettre ceci en haut de la ligne principale est une possibilité de dérobade:

    %W/INT QUIT TERM/.each { |sig| trap sig,"SYSTEM_DEFAULT" }
    

    Cela provoque le programme à répondre aux signaux de terminaison normaux en terminant immédiatement, en contournant les gestionnaires d'exception, sans nettoyage . Cela peut donc provoquer une perte de données ou similaire. Faites attention!

  3. Si vous devez faire ceci:

    begin
      do_something
    rescue Exception => e
      critical_cleanup
      raise
    end
    

    vous pouvez réellement faire ceci:

    begin
      do_something
    ensure
      critical_cleanup
    end
    

    Dans le second cas, le critical cleanup sera appelé à chaque fois, qu'une exception soit levée ou non.


Parce que cela capture toutes les exceptions. Il est peu probable que votre programme puisse récupérer de l' un d'eux.

Vous ne devez gérer que les exceptions dont vous savez comment récupérer. Si vous n'anticipez pas un certain type d'exception, ne le gérez pas, plantez fort (notez les détails dans le journal), puis diagnostiquez les journaux et corrigez le code.

Avaler des exceptions est mauvais, ne le faites pas.





exception-handling