Reraise (ten sam wyjątek) po przechwyceniu wyjątku w Rubim


86

Próbuję doskonalić swoje umiejętności w Rubim, łapiąc wyjątki. Chcę wiedzieć, czy często ponownie generuje się ten sam rodzaj wyjątku, gdy masz kilka wywołań metod. Czy więc następujący kod miałby sens? Czy można ponownie wywołać wyjątek tego samego rodzaju, czy też nie powinienem go przechwytywać w metodzie procesu?

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

Odpowiedzi:


175

Czasem po prostu chcemy wiedzieć błąd stało , bez konieczności rzeczywiście obsłużyć błędu.

Często zdarza się, że osobą odpowiedzialną za obsługę błędów jest użytkownik obiektu: wywołujący. A jeśli interesuje nas błąd, ale nie chcemy brać na siebie tej odpowiedzialności? Ratujemy błąd, robimy wszystko, co musimy, a następnie propagujemy sygnał w górę stosu, jakby nic się nie stało.

Na przykład, co by było, gdybyśmy chcieli zarejestrować komunikat o błędzie, a następnie pozwolić dzwoniącemu zająć się nim?

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

Wywołanie raisebez żadnych argumentów spowoduje ostatni błąd. W naszym przypadku ponownie przebijamy error.

W przykładzie, który przedstawiłeś w swoim pytaniu, ponowne podnoszenie błędu po prostu nie jest konieczne. Możesz po prostu pozwolić mu naturalnie rozprzestrzeniać się w górę stosu. Jedyna różnica w twoim przykładzie polega na tym, że tworzysz nowy obiekt błędu i podnosisz go, zamiast ponownie podnosić ostatni.


Ciekawy. Moje pytanie brzmi, jeśli nie wyłapię błędu w definicji procesu to musiałbym go wyłapać przy wywołaniu metody procesu, np .: begin @logo.process; rescue...ale wtedy nie łapałbym wyjątku uruchamianego przez sam proces, ale czegoś, co zostało wywołane z wewnątrz procesu. Czy to prawda?
Hommer Smith

2
Spowodowałoby to utratę stacktraceoryginalnego wyjątku, prawdopodobnie chcesz dołączyć wyjątek, causektóry jest dostępny w ruby> 2.1
bjhaid

4
@bjhaid Wywołanie raisew sposób podany w tej odpowiedzi całkowicie zachowuje oryginalny wyjątek, w tym rozszerzenie backtrace. causenie dotyczy tego przypadku. Raczej jest wypełniany automatycznie, gdy rescueblok zgłosi nowy wyjątek.
rep

@HommerSmith: Co się stanie, jeśli wiersz przed podniesieniem (w tym przypadku log.error, ale może to być wszystko) zawiedzie? Zastanawiam się nad „zapewnieniem” tego, ale wewnątrz upewnienia się musiałbym użyć odwołania do błędu jako argumentu „podnieś”. Co sądzisz o tym?
jgomo3

1
@ RafałCieślak Za każdym razem, gdy pojawia się błąd, jest on przypisywany do $!zmiennej globalnej. Wywołanie raisebez argumentów podnosi błąd zawarty w $!, skutecznie podnosząc ostatni błąd. Jednak raise errorspowoduje zgłoszenie błędu zawartego w errorzmiennej lokalnej, która może, ale nie musi być tym samym obiektem zawartym w $!. W moim przykładzie $!jest taki sam jak error. Jednak jest to również możliwe:error = Exception.new; raise error
Matheus Moreira,

4

Spowoduje to zgłoszenie tego samego rodzaju błędu co oryginał, ale możesz dostosować komunikat.

rescue StandardError => e
  raise e.class, "Message: #{e.message}"

Sugerowałbym, że wyłapywanie StandardError jest złym pomysłem, ponieważ zawiera on wszystkie rodzaje funkcji niższego poziomu i może zawiesić Twój program.
Paul Whitehead,

3
Uważam, że jest to „wyjątek” od reguły, ponieważ natychmiast ponownie podnosimy ten wyjątek.
FreePender

Widzę, co tam zrobiłeś, @FreePender;)
ilasno

1
Wiem, że minął rok, ale można bezpiecznie złapać StandardError, @PaulWhitehead - to wyjątek, którego nigdy, przenigdy nie możesz złapać. (Szybkie źródło: thinkbot.com/blog/rescue-standarderror-not-exception )
RonLugge
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.