Écrit par Martin C.
Les exceptions servent, comme leur nom l’indique, à gérer des erreurs au caractère rare et imprévu.
Elles n’ont pas vocation à gérer des erreurs prévisibles comme une erreur de saisie d’un utilisateur par exemple.
Venons-en rapidement aux faits :
begin
3 + '4'
rescue => e
puts e.class
puts e.message
puts e.trace
end
Nous renverra:
TypeError
String can't be coerced into Fixnum
./exceptions.rb:4:in `+'
./exceptions.rb:4:in `<main>'
On a donc une erreur de la classe TypeError. Si on regarde la classe parente de TypeError il s’agit de StandardError. Cette classe est en fait la classe par défaut utilisée dans notre rescue.
Notre code revient donc à :
begin
3 + '4'
rescue StandardError => e
puts e.class
puts e.message
puts e.trace
end
Il est fortement déconseillé d’attraper des erreurs en dehors des sous-classes de StandardError car elles sont utilisées par le système. Par exemple, on y retrouve NoMemoryError, LoadError ou encore SystemExit pour n’en citer que quelques-unes.
On comprend mieux pourquoi les attraper pourrait avoir des effets de bords. Quelques exemples :
begin
require 'foo'
rescue Exception
puts $!
end
Ici, on attrape l’exception de type LoadError, ce qui risque de masquer le problème de chargement de notre fichier.
À noter l’utilisation de la variable globale $! qui récupère automatiquement la dernière exception.
Autre exemple :
while true
puts "Say quit to esc"
begin
str = gets
exit if "quit" == str.chomp
rescue Exception => e
puts "Exception #{e.class}"
end
end
Bon courage pour quitter, le rescue attrapera l’exception SystemExit si vous tapez quit mais également Interrupt si vous faites un ^C.
À part pour faire une lib de debug ou autre type de wrapper ne faites pas de rescue Exception. Si vous avez besoin d’un wrapper, pensez à relancer l’exception :
begin
require 'foo'
rescue Exception => e
# j'attrape tout, mais je sais ce que je fais
puts $!
raise e
end
Lorsque vous avez besoin de vous assurer qu’un traitement sera réalisé même si une exception survient vous pouvez utiliser ensure :
require 'tempfile'
begin
file = Tempfile.new('a')
raise StandardError.new("ugh")
rescue => e
puts $!
raise e
ensure
file.close
file.unlink
end
Notre section ensure va s’assurer que le fichier est bien fermé et supprimé. J’ai volontairement re-lancé l’exception pour montrer que même si le rescue relance l’exception, le passage dans le ensure est garanti.
Bien entendu, vous pouvez créer votre propre exception comme une classe classique :
class MyOwnException < Exception; end
raise MyOwnException.new('foo')
Si vous souhaitez attraper plusieurs types d’exception sans distinction, vous pouvez parfaitement écrire :
require 'tempfile'
begin
file = Tempfile.new('a')
raise ArgumentError.new("ugh")
rescue ArgumentError, ZeroDivisionError => e
puts $!
raise e
ensure
file.close
file.unlink
end
Vous pouvez également gérer des comportements différents avec plusieurs instructions rescue :
require 'tempfile'
begin
file = Tempfile.new('a')
raise ArgumentError.new("ugh")
rescue ArgumentError => e
puts $!
# je renvoie l'erreur uniquement si c'est une exception de type ArgumentError
raise e
rescue ZeroDivisionError => e
puts $!
ensure
file.close
file.unlink
end
Pour connaître la liste des exceptions disponibles, je vous ai préparé quelques lignes pour parcourir les différentes classes existantes et les afficher sous forme d’arbre :
def subclasses_tree(klass, level = 0)
puts level.zero? ? klass.to_s : "#{' ' * (4 * (level - 1))}├── #{klass}"
descendants_of(klass).select { |k| k.superclass == klass }.each do |k|
subclasses_tree(k, level + 1)
end
end
def descendants_of(klass)
ObjectSpace.each_object(Class).select { |k| k < klass }
end
subclasses_tree(Exception)
Résultat :
Exception
├── MonitorMixin::ConditionVariable::Timeout
├── SystemStackError
├── NoMemoryError
├── SecurityError
├── ScriptError
├── NotImplementedError
├── LoadError
├── Gem::LoadError
├── Gem::ConflictError
├── SyntaxError
├── StandardError
├── FiberError
├── ThreadError
├── Math::DomainError
├── LocalJumpError
├── IOError
├── EOFError
├── RegexpError
├── ZeroDivisionError
├── SystemCallError
├── Errno::ERPCMISMATCH
├── Errno::EPROGUNAVAIL
├── Errno::EPROGMISMATCH
├── Errno::EPROCUNAVAIL
├── Errno::EPROCLIM
├── Errno::ENOTSUP
├── Errno::ENOATTR
├── Errno::ENEEDAUTH
├── Errno::EFTYPE
├── Errno::EBADRPC
├── Errno::EAUTH
├── Errno::EOWNERDEAD
├── Errno::ENOTRECOVERABLE
├── Errno::ECANCELED
├── Errno::EDQUOT
├── Errno::ESTALE
├── Errno::EINPROGRESS
├── IO::EINPROGRESSWaitWritable
├── IO::EINPROGRESSWaitReadable
├── Errno::EALREADY
├── Errno::EHOSTUNREACH
├── Errno::EHOSTDOWN
├── Errno::ECONNREFUSED
├── Errno::ETIMEDOUT
├── Errno::ETOOMANYREFS
├── Errno::ESHUTDOWN
├── Errno::ENOTCONN
├── Errno::EISCONN
├── Errno::ENOBUFS
├── Errno::ECONNRESET
├── Errno::ECONNABORTED
├── Errno::ENETRESET
├── Errno::ENETUNREACH
├── Errno::ENETDOWN
├── Errno::EADDRNOTAVAIL
├── Errno::EADDRINUSE
├── Errno::EAFNOSUPPORT
├── Errno::EPFNOSUPPORT
├── Errno::EOPNOTSUPP
├── Errno::ESOCKTNOSUPPORT
├── Errno::EPROTONOSUPPORT
├── Errno::ENOPROTOOPT
├── Errno::EPROTOTYPE
├── Errno::EMSGSIZE
├── Errno::EDESTADDRREQ
├── Errno::ENOTSOCK
├── Errno::EUSERS
├── Errno::EILSEQ
├── Errno::EOVERFLOW
├── Errno::EBADMSG
├── Errno::EMULTIHOP
├── Errno::EPROTO
├── Errno::ENOLINK
├── Errno::EREMOTE
├── Errno::ENOSR
├── Errno::ETIME
├── Errno::ENODATA
├── Errno::ENOSTR
├── Errno::EIDRM
├── Errno::ENOMSG
├── Errno::ELOOP
├── Errno::ENOTEMPTY
├── Errno::ENOSYS
├── Errno::ENOLCK
├── Errno::ENAMETOOLONG
├── Errno::EDEADLK
├── Errno::ERANGE
├── Errno::EDOM
├── Errno::EPIPE
├── Errno::EMLINK
├── Errno::EROFS
├── Errno::ESPIPE
├── Errno::ENOSPC
├── Errno::EFBIG
├── Errno::ETXTBSY
├── Errno::ENOTTY
├── Errno::EMFILE
├── Errno::ENFILE
├── Errno::EINVAL
├── Errno::EISDIR
├── Errno::ENOTDIR
├── Errno::ENODEV
├── Errno::EXDEV
├── Errno::EEXIST
├── Errno::EBUSY
├── Errno::ENOTBLK
├── Errno::EFAULT
├── Errno::EACCES
├── Errno::ENOMEM
├── Errno::EAGAIN
├── IO::EAGAINWaitWritable
├── IO::EAGAINWaitReadable
├── Errno::ECHILD
├── Errno::EBADF
├── Errno::ENOEXEC
├── Errno::E2BIG
├── Errno::ENXIO
├── Errno::EIO
├── Errno::EINTR
├── Errno::ESRCH
├── Errno::ENOENT
├── Errno::EPERM
├── Errno::NOERROR
├── EncodingError
├── Encoding::ConverterNotFoundError
├── Encoding::InvalidByteSequenceError
├── Encoding::UndefinedConversionError
├── Encoding::CompatibilityError
├── RuntimeError
├── Gem::Exception
├── Gem::VerificationError
├── Gem::RubyVersionMismatch
├── Gem::RemoteSourceException
├── Gem::RemoteInstallationSkipped
├── Gem::RemoteInstallationCancelled
├── Gem::RemoteError
├── Gem::OperationNotSupportedError
├── Gem::InvalidSpecificationException
├── Gem::InstallError
├── Gem::ImpossibleDependenciesError
├── Gem::GemNotFoundException
├── Gem::SpecificGemNotFoundException
├── Gem::FormatException
├── Gem::FilePermissionError
├── Gem::EndOfYAMLException
├── Gem::DocumentError
├── Gem::GemNotInHomeException
├── Gem::DependencyRemovalException
├── Gem::DependencyError
├── Gem::UnsatisfiableDependencyError
├── Gem::DependencyResolutionError
├── Gem::CommandLineError
├── NameError
├── NoMethodError
├── RangeError
├── FloatDomainError
├── IndexError
├── StopIteration
├── KeyError
├── ArgumentError
├── UncaughtThrowError
├── Gem::Requirement::BadRequirementError
├── TypeError
├── SignalException
├── Interrupt
├── fatal
├── SystemExit
├── Gem::SystemExitException
Vous savez tout, bonne gestion d’exceptions avec Ruby !
À lire aussi
Bitcoin, fonctionnement et technologies
Atlassian Team’25 Europe
Webinaire IA



