Retour

Gérer les erreurs en Ruby : comprendre, déboguer, ne plus paniquer

Enregistrer
Temps de lecture: 8 min Vues: 28 Niveau: Intermédiaire à Confirmé
environ 1 mois par Franci-lobbie LALANE (Modifié)
Gérer les erreurs en Ruby : comprendre, déboguer, ne plus paniquer
Tags:
#rails #ruby #exceptions #erreurs #goodpractices


 “There are two ways to write error-free programs; only the third one works.” — Alan J. Perlis 



Introduction ⚙️

Quand on code en Ruby, on finit toujours par croiser des erreurs. Pas parce qu'on est mauvais, mais parce que le code qu'on écrit est vivant, souvent complexe, et parfois un peu capricieux. Et heureusement : les erreurs sont nos meilleurs signaux pour corriger, améliorer et comprendre notre code.
Ce guide est là pour t'apprendre à apprivoiser les exceptions Ruby :
  • Comment elles fonctionnent ?
  • Quelles sont les plus courantes ?
  • Comment les lire intelligemment ?
  • Comment les gérer proprement ?
  • Comment créer ses propres erreurs utiles pour ton app ?
Avec des exemples concrets, des bonnes pratiques, des nouveautés de Ruby 3.4.1, et même quelques retours de devs glanés sur Reddit. Bref, tout ce qu’il faut pour que les erreurs ne te fassent plus peur.






I. C’est quoi une exception en Ruby ? 🧩

Une exception est une situation anormale qui interrompt l’exécution normale d’un programme. Par exemple : diviser par zéro, appeler une méthode qui n'existe pas, ouvrir un fichier absent…
Ruby utilise des objets pour représenter ces erreurs. Chaque type d'erreur est une classe, souvent héritée de StandardError.

Structure de base
begin
  # code potentiellement problématique
rescue
  # code exécuté en cas d’erreur
end

Quand une erreur est levée, Ruby saute immédiatement au bloc rescue correspondant. Si aucun bloc ne correspond, l’exception se propage dans la pile jusqu’à ce que quelque chose la capture… ou que le programme plante.
Il existe donc deux grands types d'erreurs :
  • Silencieuses : capturées et gérées
  • Fatales : non capturées, le programme s’arrête





II. Lire un message d’erreur : le stack trace expliqué 🐛

Un message d'erreur Ruby, aussi appelé stack trace, est ton meilleur ami. Il te dit exactement :
  • le fichier concerné
  • la ligne où ça a planté
  • le type d’erreur
  • le message explicite





Exemple :
main.rb:12:in `calculate': divided by 0 (ZeroDivisionError)

Tu sais que dans main.rb, à la ligne 12, dans la méthode calculate, un ZeroDivisionError a été levé avec le message "divided by 0".




Bonus sympa : Did you mean?




Depuis Ruby 2.3, Ruby te suggère souvent des corrections de nom :
NameError: undefined local variable or method `usr' for main:Object
Did you mean?  user

Une petite pépite pour corriger les fautes de frappe 😄




III. Les erreurs les plus fréquentes 🧱

Voici les erreurs que tu rencontreras le plus souvent. Pour chaque type : une explication, un exemple, une astuce.




NameError




Variable ou constante non définie.
puts total_price
# => NameError: undefined local variable or method 'total_price'

Solution : vérifier la déclaration, la casse, ou la portée de ta variable.




NoMethodError
Méthode appelée sur un objet qui ne la connaît pas.
user = nil
user.name
# => NoMethodError: undefined method `name` for nil:NilClass


Solution : bien vérifier que ton objet n’est pas nil, ou utiliser &. (safe navigation operator).




ArgumentError
Le nombre ou type d’arguments ne colle pas.
def greet(name, age); end
greet("Alice")
# => ArgumentError: wrong number of arguments (given 1, expected 2)

Solution : toujours relire la signature de la méthode.




SyntaxError
Code invalide au parsing.
if true
  puts "ok"
# => SyntaxError: unexpected end-of-input, expecting keyword_end

Solution : fermer toutes tes structures (end) et valider ton code dans un éditeur.




TypeError
Type d’objet inapproprié.
[1, 2, 3] + "salut"
# => TypeError: no implicit conversion of String into Array

Solution : toujours s'assurer que les objets sont compatibles.




ZeroDivisionError
La classique division par zéro 😬
10 / 0
# => ZeroDivisionError: divided by 0

Solution : toujours vérifier les dénominateurs avant de diviser.




LoadError / Errno::ENOENT / IOError
Fichier ou ressource manquante.
File.read("/path/to/missing/file")
# => Errno::ENOENT: No such file or directory

Solution : vérifier les chemins, les permissions, et tester leur existence avant appel.




IV. Comment gérer les erreurs proprement 🛠️

Ruby nous donne plusieurs outils pour intercepter, traiter, relancer ou ignorer les erreurs. Mais comme tout outil puissant, ça peut aussi mal tourner si c’est mal utilisé.




rescue avec variable
C’est la base : capturer l’exception dans une variable pour l’analyser.
begin
  risky_code
rescue => e
  puts e.class       # Type d'erreur
  puts e.message     # Message associé
  puts e.backtrace   # Détail des appels
end

Tu peux ensuite logger, alerter, ou relancer l’erreur après traitement si besoin avec raise e.

else et ensure
  • else s’exécute si aucune erreur n’est levée
  • ensure s’exécute dans tous les cas, erreur ou pas
begin
  puts "On tente un truc"
rescue => e
  puts "Erreur capturée : #{e.message}"
else
  puts "Tout s'est bien passé !"
ensure
  puts "Nettoyage systématique"
end

C’est super utile pour fermer un fichier, libérer une ressource, ou logger une info de fin de traitement.

retry et raise

Tu peux parfois vouloir réessayer après une erreur ponctuelle (réseau, base de données, etc.). Ruby permet ça avec retry.
tries = 0
begin
  fetch_remote_data
rescue NetworkTimeout => e
  tries += 1
  retry if tries < 3
  raise "Échec après 3 tentatives"
end

Mais attention à ne pas créer de boucle infinie. Mets des limites claires.




V. Lever ses propres exceptions 🚨

Lever une exception, c’est dire « stop, cette situation est invalide, je préfère interrompre l’exécution ici ».
Avec raise simple
raise "Ce nom est invalide"

C’est pratique pour signaler un problème métier ou un cas limite mal géré.




Avec une classe d’exception personnalisée
class PermissionDenied < StandardError; end

def delete_user(user)
  raise PermissionDenied, "Vous n'avez pas le droit de faire ça" unless user.admin?
end

Créer tes propres classes rend les erreurs plus explicites et facilite leur gestion (tu peux rescue PermissionDenied uniquement).




En ajoutant des attributs
class ApiError < StandardError
  attr_reader :code

  def initialize(message, code)
    super(message)
    @code = code
  end
end

Tu peux ainsi accéder à plus d’infos quand tu traites l’erreur (e.code). Très utile pour les appels HTTP ou les erreurs de validation métier.




VI. L’arbre des exceptions Ruby 🌳

Ruby organise toutes ses erreurs en arbre. Tu peux en voir un extrait ici :




Exception
├── NoMemoryError
├── ScriptError
│   ├── LoadError
│   ├── SyntaxError
├── SignalException
│   └── Interrupt
├── StandardError
│   ├── ArgumentError
│   ├── ZeroDivisionError
│   ├── RuntimeError
│   ├── NoMethodError
│   ├── etc.

Pourquoi c’est important ? Parce que Ruby ne capture que StandardError par défaut.




Donc si tu fais rescue, ça ne prendra pas une SyntaxError ou un SystemExit. C’est voulu : ça évite de masquer les plantages critiques.




Moralité : hérite toujours de StandardError pour tes classes d’erreur perso.




VII. Nouveautés et changements dans Ruby 3.4.1 🧪

La gestion des exceptions a évolué avec les versions récentes.




full_message




Depuis Ruby 2.6, tu peux utiliser e.full_message pour une sortie lisible et colorée :
rescue => e
  puts e.full_message(highlight: true)

Pratique pour le debug sans avoir à parser backtrace manuellement.




cause




Si tu relèves une erreur dans un rescue, Ruby garde une trace de l’originale :
begin
  raise "Erreur A"
rescue => e
  raise "Erreur B"
end

e.cause permet de suivre la chaîne d’erreurs. Top pour les logs ou les outils d’observabilité.




Améliorations internes




Ruby 3.4 a aussi clarifié les messages dans les lambdas, les blocs, et les appels indirects. Tu devrais voir moins de "undefined method" bizarres et plus d’infos utiles.




VIII. Bonnes pratiques ✅


Voici quelques règles simples pour éviter les pièges les plus courants :
  • Ne fais jamais rescue nil : tu masques l’erreur, tu ne la règles pas.
  • Ne rescue pas Exception sauf cas ultra spécifiques (comme un moteur de sandbox).
  • Loggue toujours ce que tu interceptes (au moins e.message).
  • Utilise ensure pour nettoyer après une opération critique (fichier, DB, etc.).
  • Crée des erreurs personnalisées avec des noms clairs : InvalidCredentials, RateLimitExceeded, etc.
Ces petites habitudes font une énorme différence dans la stabilité et la lisibilité de ton code.




IX. Bonus : outils de debug Ruby 🧰


Quelques pépites pour t'aider à mieux comprendre et corriger tes erreurs Ruby :
  • pry-rescue : ouvre un REPL automatiquement dès qu'une exception se produit.
  • byebug : très utile pour mettre un breakpoint dans un rescue.
  • binding.irb ou binding.pry : explore l'état du programme en live.
  • exception.full_message(highlight: true) : affiche le message d’erreur avec couleurs et contexte.
  • Sentry / Rollbar / Bugsnag : pour suivre les exceptions en production.





Conclusion 🎬


Les erreurs Ruby ne sont pas là pour t'embêter. Elles sont là pour te parler.
Apprendre à les lire, les comprendre et les utiliser fait de toi un meilleur développeur. Et Ruby a l’avantage de rendre ce processus vraiment agréable, comparé à d’autres langages plus obscurs.
Prends le temps d’écouter ce que l’exception essaie de te dire.
Et surtout, continue à casser ton code. C’est comme ça qu’on apprend.




Happy coding ! 🚀


Laissez un commentaire

Se connecterpour laisser un commentaire.