On m'a transmis un héritage de longue durée Ruby program, qui a de nombreuses occurrences de
begin
#dosomething
rescue Exception => e
#halt the exception's progress
end
tout au long.
Sans traquer toutes les exceptions possibles, celles-ci pourraient être gérées (du moins pas immédiatement), je voudrais quand même être en mesure de les arrêter parfois avec CtrlC.
Et je voudrais le faire d'une manière qui ne fait qu'ajouter au code (donc je n'affecte pas le comportement existant, ou je manque une exception interceptée au milieu d'une course.)
[CtrlC est SIGINT, ou SystemExit, qui semble être équivalent à SignalException.new("INT")
dans le système de gestion des exceptions de Ruby. class SignalException < Exception
, C'est pourquoi ce problème se pose.]
Le code que j'aimerais avoir écrit serait:
begin
#dosomething
rescue SignalException => e
raise e
rescue Exception => e
#halt the exception's progress
end
EDIT: ce code fonctionne, tant que vous obtenez la classe de l'exception que vous souhaitez intercepter correctement. C'est soit SystemExit, Interrupt ou IRB :: Abort comme ci-dessous.
Le problème est que lorsqu'un programme Ruby se termine, il le fait en augmentant SystemExit. Lorsqu'un contrôle-C entre, il soulève Interruption =. Étant donné que SystemExit et Interrupt dérivent de Exception, votre gestion des exceptions arrête la sortie ou l'interruption de ses pistes. Voici le correctif:
Partout où vous le pouvez, changez
rescue Exception => e
# ...
end
à
rescue StandardError => e
# ...
end
pour ceux que vous ne pouvez pas changer en StandardError, relancez l'exception:
rescue Exception => e
# ...
raise
end
ou, à tout le moins, relancer SystemExit et Interrupt
rescue SystemExit, Interrupt
raise
rescue Exception => e
#...
end
Toutes les exceptions personnalisées que vous avez faites doivent dériver de StandardError, pas Exception.
Si vous pouvez envelopper votre programme entier, vous pouvez faire quelque chose comme ceci:
trap("SIGINT") { throw :ctrl_c }
catch :ctrl_c do
begin
sleep(10)
rescue Exception
puts "Not printed"
end
end
Cela a essentiellement CtrlC utilisez catch/throw au lieu de la gestion des exceptions, donc à moins que le code existant n'ait déjà un catch: ctrl_c, ça devrait aller.
Alternativement, vous pouvez faire une trap("SIGINT") { exit! }
. exit!
Se ferme immédiatement, il ne déclenche pas d'exception, le code ne peut donc pas l'attraper accidentellement.
Si vous ne pouvez pas envelopper votre demande dans un begin ... rescue
bloquer (par exemple, Thor) vous pouvez simplement intercepter SIGINT
:
trap "SIGINT" do
puts "Exiting"
exit 130
end
130 est un code de sortie standard.
J'utilise ensure
à grand effet! C'est pour les choses que vous voulez que vos choses se terminent, peu importe pourquoi elles se terminent.
Gérer proprement Ctrl-C dans Ruby de la manière ZeroMQ:
#!/usr/bin/env Ruby
# Shows how to handle Ctrl-C
require 'ffi-rzmq'
context = ZMQ::Context.new(1)
socket = context.socket(ZMQ::REP)
socket.bind("tcp://*:5558")
trap("INT") { puts "Shutting down."; socket.close; context.terminate; exit}
puts "Starting up"
while true do
message = socket.recv_string
puts "Message: #{message.inspect}"
socket.send_string("Message received")
end