Reraise (gleiche Ausnahme) nach dem Abfangen einer Ausnahme in Ruby

84

Ich versuche meine Ruby-Fähigkeiten zu verbessern, indem ich Ausnahmen fange. Ich möchte wissen, ob es üblich ist, dieselbe Art von Ausnahme erneut auszulösen, wenn Sie mehrere Methodenaufrufe haben. Wäre der folgende Code also sinnvoll? Ist es in Ordnung, dieselbe Art von Ausnahme erneut auszulösen, oder sollte ich sie nicht bei der Prozessmethode abfangen?

class Logo
  def process
    begin
      @processed_logo = LogoProcessor::create_image(self.src)
    rescue CustomException
      raise CustomException
    end
  end
end

module LogoProcessor
  def self.create_image
    raise CustomException if some_condition
  end
end
Hommer Smith
quelle

Antworten:

170

Manchmal möchten wir nur wissen, dass ein Fehler aufgetreten ist , ohne den Fehler tatsächlich behandeln zu müssen .

Es ist häufig der Fall, dass derjenige, der für die Behandlung von Fehlern verantwortlich ist, der Benutzer des Objekts ist: der Anrufer. Was ist, wenn wir an dem Fehler interessiert sind, diese Verantwortung aber nicht übernehmen möchten? Wir beheben den Fehler, tun alles, was wir tun müssen, und verbreiten dann das Signal auf dem Stapel, als wäre nichts passiert.

Was wäre zum Beispiel, wenn wir die Fehlermeldung protokollieren und dann den Anrufer damit befassen wollten?

begin
  this_will_fail!
rescue Failure => error
  log.error error.message
  raise
end

Ein Aufruf raiseohne Argumente löst den letzten Fehler aus. In unserem Fall erhöhen wir erneut error.

In dem Beispiel, das Sie in Ihrer Frage vorgestellt haben, ist es einfach nicht erforderlich, den Fehler erneut auszulösen. Sie können es einfach den Stapel auf natürliche Weise ausbreiten lassen. Der einzige Unterschied in Ihrem Beispiel besteht darin, dass Sie ein neues Fehlerobjekt erstellen und es auslösen, anstatt das letzte erneut zu erhöhen.

Matheus Moreira
quelle
Interessant. Meine Frage ist, wenn ich den Fehler in der Definition des Prozesses nicht abfange, müsste ich ihn abfangen, wenn ich die Prozessmethode aufrufe, z. B.: begin @logo.process; rescue...Aber dann würde ich keine Ausnahme abfangen, die vom Prozess selbst gestartet wird. aber von etwas, das aus dem Prozess heraus aufgerufen wurde. Ist das richtig zu tun?
Hommer Smith
2
Dies würde stacktracedie ursprüngliche Ausnahme verlieren , Sie möchten wahrscheinlich eine Ausnahme einschließen, causedie in Ruby> 2.1
bjhaid
4
@bjhaid Wenn Sie raisein der Art dieser Antwort aufrufen , bleibt die ursprüngliche Ausnahme vollständig erhalten, einschließlich der backtrace. causegilt nicht für diesen Fall. Vielmehr wird es automatisch ausgefüllt, wenn ein rescueBlock eine neue Ausnahme auslöst.
Wiederholung
@ HommerSmith: Was ist, wenn die Zeile vor dem Erhöhen (in diesem Fall log.error, aber es könnte alles sein) fehlschlägt? Ich denke darüber nach, es "sicherzustellen", aber innerhalb der Sicherstellung müsste ich den Verweis auf den Fehler als Argument für "erhöhen" verwenden. Was denkst du darüber?
jgomo3
1
@ RafałCieślak Jedes Mal, wenn ein Fehler ausgelöst wird, wird er der $!globalen Variablen zugewiesen . Das Aufrufen raiseohne Argumente löst den darin enthaltenen Fehler aus und löst $!effektiv den letzten Fehler aus. Allerdings raise errorwird zu einem größeren Fehler in den enthaltenen errorlokalen Variablen, die nicht das gleiche Objekt in enthalten sein können $!. In meinem Beispiel $!ist das gleiche wie error. Es ist jedoch auch möglich, dies zu tun:error = Exception.new; raise error
Matheus Moreira
4

Dies löst den gleichen Fehlertyp wie das Original aus, Sie können die Nachricht jedoch anpassen.

rescue StandardError => e
  raise e.class, "Message: #{e.message}"
FreePender
quelle
Ich würde vorschlagen, dass es eine schlechte Idee ist, StandardError zu fangen, da dies alle Arten von Funktionen auf niedrigerer Ebene enthält und Ihr Programm hängen könnte.
Paul Whitehead
3
Ich glaube, dies ist eine "Ausnahme" von der Regel, da wir die Ausnahme sofort wieder auslösen.
FreePender
Ich sehe, was du dort gemacht hast, @FreePender;)
ilasno