E-JAMBON bio photo

E-JAMBON

Ce qui se conçoit bien s'exprime clairement.

Email Twitter Github

Il n’y a aucune gestion d’erreur dans Embark. J’en ai donc rajouté une.

Ruby et la gestion des erreurs

Voyons voir comment on gère les erreurs dans un code en Ruby.

    def maMethode()
        begin
            # ... Ce que je veux faire dans ma méthode
        rescue MonExceptionATraiter => e
            # Ici j'attrape les erreurs, j'essaie de les traiter.
        ensure
            # Ici  je m'assure que je fais le nettoyage nécessaire quoi qu'il arrive. Comme de fermer les fichiers ouverts.
        end
    end

En ruby, on intercepte donc les erreurs par classe. Voici l’arborescence des erreurs en ruby :

Exception
 NoMemoryError
 ScriptError
   LoadError
   NotImplementedError
   SyntaxError
 SignalException
   Interrupt
 StandardError
   ArgumentError
   IOError
     EOFError
   IndexError
   LocalJumpError
   NameError
     NoMethodError
   RangeError
     FloatDomainError
   RegexpError
   RuntimeError
   SecurityError
   SystemCallError
   SystemStackError
   ThreadError
   TypeError
   ZeroDivisionError
 SystemExit
 fatal

Chouette, me direz-vous : pour que mon programme ne plante pas, je n’ai qu’à récuperer de toutes les erreurs en capturant Exception.

Certes mais en pratique, ça soulève beaucoup plus de problèmes que de ne traiter que les erreurs relatives à son propre code. C’est aussi l’avis de la plupart des développeurs qui se sont exprimés sur le sujet (en Ruby au moins).

Voici les raisons que je retiens :

  • Interrupt : Cette classe décrit les interruptions du programme. L’intercepter, c’est intercepter aussi les actions de l’utilisateur quand il fait un CTRL + C. On ne veut pas empêcher le traitement des interruptions : ça obligerait à faire dans notre propre code ce qui est déjà bien fait ailleurs.

  • SyntaxError : C’est la classe qui permet au débugger d’afficher les messages d’erreurs. En récupérant ces erreurs, on les rend silencieuses. A priori, ce n’est pas intéressant.

  • SignalException : en récupérant celle-ci, vous empêchez tous les traitements de signaux envoyés à Ruby (hormis kill -9). En voilà, une idée qu’elle est mauvaise.

Il est légitime de vouloir tenir un journal pour comprendre ce qui s’est passé dans un programme. C’est le B.A-BA pour l’entretien d’un logiciel. Il est légitime de récupérer toutes les exceptions pour pouvoir faire son propre traitement.

La solution pour que ce ne soit pas une gêne est simple : soulever l’erreur à nouveau une fois la journalisation réalisée.

    def writeContent(content)
        begin    
            embarkedHtmlFile = File.new("#{@cacheFolder}/#{filename}.html", "w:UTF-8")
            embarkedHtmlFile.puts content       
        rescue IOError => e
            puts " my message"
            # Fais ce que dois pour traiter ta propre erreur 
            # Ecrire dans le journal
        rescue Exceptions => a
            # Ecrire ce que dois dans le journal.
            raise a  # Ecriture dans le journal réalisée, nous renvoyons l'erreur.
        end
    end

Dans embark, la gestion d’erreur

Je pense qu’il n’est utile que de traiter les erreurs relatives au code que l’on écrit. On rajoutera le traitement des erreurs que l’on rencontre en l’utilisant / via les remontées de bug / via les tests.

Je n’ai pas, non plus, besoin d’avoir un journal d’erreur pour l’instant.

Nettoyer derrière soit

Donc j’ai vu comment récupérer et traiter une erreur. Mais je souhaite également faire un peu de nettoyage.

Par exemple, si j’ai déjà créé un fichier et qu’une erreur survient quand j’essaie d’écrire dedans, ce fichier est-il encore ouvert ? Si oui, j’aimerai le fermer.

Pour ça, il y a la clause ‘ensure’ :

    def writeContent(content)
        begin    
            embarkedHtmlFile = File.new("#{@cacheFolder}/#{filename}.html", "w:UTF-8")
            embarkedHtmlFile.puts content       
        rescue IOError => e
            puts " my message"
            # Fais ce que dois
        rescue Exceptions => a
            # Ecrire ce que dois dans le journal.
            raise a  # Ecriture dans le journal réalisée, nous renvoyons l'erreur.
        ensure
            embarkedHtmlFile && embarkedHtmlFile.close ## Toujours exécuté.
        end
    end

Les instructions dans “ensure” sont toujours exécutées, quoi qu’il survienne. En ruby, notez que cette précaution est inutile parce que ce nettoyage est déjà codé par la classe elle même.

Note de style

En ruby, soit A et B deux instructions, si on écrit :

A && B

alors B n’est évalué qu’à la condition que la valeur de A soit vraie. En ruby, toute instruction évaluée est vraie sauf si elle renvoie false ou nil.

Donc quand j’écris :

    embarkedHtmlFile && embarkedHtmlFile.close 

si embarkedHtmlFile n’est pas alloué, son évaluation vaut “nil” : l’instruction EmbarkedHtmlFile.close n’est pas évaluée. Dans tous les autres cas, l’instruction embarkedHtmlFile.close sera évaluée (et donc le fichier sera fermé).

Nouvelle version d’embarked.

Comme d’habitude, le code est accessible sur github github

Tests :

Je n’ai pas codé de tests. J’ai simplement fait un jeu de test, et je m’en contente. Ce sera peut-être l’objet d’un prochain billet.

Je fais ça surtout pour tester un peu Jekyll : c’est l’occasion de faire un billet / aide mémoire.

Todo :

  • Changer l’interface pour faire un block embark du style embark / endembark contenant un bloc.
    url: mon url
    titre: mon titre
    commentaire: mon commentaire
    style: nom du style à utiliser
  • Gérer les adresses pour lequelles le site renvoie une erreur.
  • Gérer les sites qui n’ont pas d’image. Mettre un logo par défaut s’il n’y en a aucun.
  • Gérer les sites qui n’ont pas de description.
  • Le rss généré est moche. A voir.
  • Ajouter des tests