Augmenter une exception personnalisée avec des arguments

Je définis une exception personnalisée sur un modèle dans les rails comme une sorte de wrapper Exception: ( begin[code]rescue[raise custom exception]end )

Lorsque je soulève l’exception, j’aimerais lui transmettre quelques informations sur a) l’instance du modèle dont les fonctions internes génèrent l’erreur et b) l’erreur interceptée.

Cela se passe sur une méthode d’importation automatisée d’un modèle qui est alimenté par une requête POST à ​​partir d’une source de données étrangère.

tldr; Comment peut-on transmettre des arguments à une exception, étant donné que vous définissez vous-même l’exception? J’ai une méthode d’initialisation sur cette exception, mais la syntaxe raise semble accepter uniquement une classe et un message Exception, aucun paramètre facultatif transmis au processus d’instanciation.

créer une instance de votre exception avec new:

 class CustomException < StandardError def initialize(data) @data = data end end # => nil raise CustomException.new(bla: "blupp") # CustomException: CustomException 

Solution:

 class FooError < StandardError attr_reader :foo def initialize(foo) super @foo = foo end end 

C'est le meilleur moyen si vous suivez le Guide de style Rubocop et transmettez toujours votre message comme deuxième argument à raise :

 raise FooError.new('the foo'), 'bar baz' 

Vous pouvez obtenir comme ça:

 rescue FooError => error error.foo # => 'the foo' error.message # => 'bar baz' 

Si vous voulez définir le message d'erreur dans FooError alors écrivez:

 class FooError < StandardError attr_reader :foo def initialize(foo) super @foo = foo end def message "The foo is: #{foo}" end end 

Explication:

Passez votre message comme deuxième argument à raise

Comme le dit le Guide de style Rubocop , le message et la classe d’exception doivent être fournis sous forme d’arguments séparés, car si vous écrivez:

 raise FooError.new('bar baz') 

Et si vous voulez passer un backtrace à raise , il n'y a aucun moyen de le faire sans passer le message deux fois:

 raise FooError.new('bar baz'), 'bar baz', other_error.backtrace 

Comme le dit cette réponse , vous devrez transmettre une trace en arrière si vous souhaitez relancer une exception en tant que nouvelle instance avec le même chemin de retour et un message ou des données différents.

Dans l' initialize , appelez super , pas super(message)

Voici trois manières différentes d'implémenter FooError avec un code de test que vous pouvez exécuter dans Pry:

 class SuperError < StandardError attr_reader :foo def initialize(foo) super @foo = foo end end class SuperWithMessageError < StandardError attr_reader :foo def initialize(foo) super(message) @foo = foo end end class SuperWithMessageArgumentError < StandardError attr_reader :foo def initialize(message = nil, foo) super(message) @foo = foo end end # All three classes behave the same except in this case: raise SuperError, 'bar' _ex_.foo # => 'bar' _ex_.message # => 'bar' raise SuperWithMessageError, 'bar' _ex_.foo # => 'bar' _ex_.message # => 'SuperWithMessageError' raise SuperWithMessageArgumentError, 'bar' _ex_.foo # => 'bar' _ex_.message # => 'SuperWithMessageArgumentError' 

Dans SuperWithMessageError et SuperWithMessageArgumentError le message n'est pas défini correctement. SuperError est donc l'implémentation correcte.

Voici un exemple de code ajoutant un code à une erreur:

 class MyCustomError < StandardError attr_reader :code def initialize(code) @code = code end def to_s "[#{code}] #{super}" end end 

Et pour le soulever: raise MyCustomError.new(code), message

Vous pouvez créer une nouvelle instance de votre sous-classe Exception , puis augmentez-la. Par exemple:

 begin # do something rescue => e error = MyException.new(e, 'some info') raise error end