Jak dodać informacje do wiadomości wyjątku w Ruby?
Jak dodać informacje do wiadomości wyjątku bez zmiany jego klasy w ruby?
Obecnie używam podejścia
strings.each_with_index do |string, i|
begin
do_risky_operation(string)
rescue
raise $!.class, "Problem with string number #{i}: #{$!}"
end
end
Idealnie, chciałbym również zachować backtrace.
Czy jest lepszy sposób?6 answers
Aby odtworzyć wyjątek i zmodyfikować wiadomość, zachowując klasę wyjątku i jej backtrace, po prostu wykonaj:
strings.each_with_index do |string, i|
begin
do_risky_operation(string)
rescue Exception => e
raise $!, "Problem with string number #{i}: #{$!}", $!.backtrace
end
end
Co daje:
# RuntimeError: Problem with string number 0: Original error message here
# backtrace...
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-31 17:24:47
Nie jest dużo lepiej, ale możesz po prostu przekierować wyjątek z nową wiadomością:
raise $!, "Problem with string number #{i}: #{$!}"
Możesz również samodzielnie uzyskać zmodyfikowany obiekt wyjątku za pomocą metody exception
:
new_exception = $!.exception "Problem with string number #{i}: #{$!}"
raise new_exception
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-13 00:48:31
Oto inny sposób:
class Exception
def with_extra_message extra
exception "#{message} - #{extra}"
end
end
begin
1/0
rescue => e
raise e.with_extra_message "you fool"
end
# raises an exception "ZeroDivisionError: divided by 0 - you fool" with original backtrace
(poprawione, aby użyć metody exception
wewnętrznie, dzięki @ Chuck)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-28 15:00:18
Moim podejściem byłoby extend
błąd rescue
d z anonimowym modułem rozszerzającym metodę message
błędu:
def make_extended_message(msg)
Module.new do
@@msg = msg
def message
super + @@msg
end
end
end
begin
begin
raise "this is a test"
rescue
raise($!.extend(make_extended_message(" that has been extended")))
end
rescue
puts $! # just says "this is a test"
puts $!.message # says extended message
end
W ten sposób nie zatrzaskujesz żadnych innych informacji w wyjątku(tj. jego backtrace
).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-13 00:51:26
Głosuję, że odpowiedź Ryana Heneise ' a powinna być zaakceptowana.
Jest to częsty problem w złożonych aplikacjach, a zachowanie oryginalnego backtrace jest często krytyczne tak bardzo, że w naszym module pomocniczym ErrorHandling
mamy do tego metodę użytkową.
Jednym z problemów, który odkryliśmy, było to, że czasami próba wygenerowania bardziej znaczących wiadomości, gdy system jest w poplątanym stanie, spowodowałaby wygenerowanie WYJĄTKÓW wewnątrz wyjątku sam handler, który doprowadził nas do utwardzenia naszej funkcji użytkowej w następujący sposób:
def raise_with_new_message(*args)
ex = args.first.kind_of?(Exception) ? args.shift : $!
msg = begin
sprintf args.shift, *args
rescue Exception => e
"internal error modifying exception message for #{ex}: #{e}"
end
raise ex, msg, ex.backtrace
end
When things go well
begin
1/0
rescue => e
raise_with_new_message "error dividing %d by %d: %s", 1, 0, e
end
Otrzymujesz ładnie zmodyfikowaną wiadomość
ZeroDivisionError: error dividing 1 by 0: divided by 0
from (irb):19:in `/'
from (irb):19
from /Users/sim/.rvm/rubies/ruby-2.0.0-p247/bin/irb:16:in `<main>'
When things go bad
begin
1/0
rescue => e
# Oops, not passing enough arguments here...
raise_with_new_message "error dividing %d by %d: %s", e
end
Nadal nie tracisz pojęcia o wielkim obrazie
ZeroDivisionError: internal error modifying exception message for divided by 0: can't convert ZeroDivisionError into Integer
from (irb):25:in `/'
from (irb):25
from /Users/sim/.rvm/rubies/ruby-2.0.0-p247/bin/irb:16:in `<main>'
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:03:02
Oto co zrobiłem:
Exception.class_eval do
def prepend_message(message)
mod = Module.new do
define_method :to_s do
message + super()
end
end
self.extend mod
end
def append_message(message)
mod = Module.new do
define_method :to_s do
super() + message
end
end
self.extend mod
end
end
Przykłady:
strings = %w[a b c]
strings.each_with_index do |string, i|
begin
do_risky_operation(string)
rescue
raise $!.prepend_message "Problem with string number #{i}:"
end
end
=> NoMethodError: Problem with string number 0:undefined method `do_risky_operation' for main:Object
I:
pry(main)> exception = 0/0 rescue $!
=> #<ZeroDivisionError: divided by 0>
pry(main)> exception = exception.append_message('. With additional info!')
=> #<ZeroDivisionError: divided by 0. With additional info!>
pry(main)> exception.message
=> "divided by 0. With additional info!"
pry(main)> exception.to_s
=> "divided by 0. With additional info!"
pry(main)> exception.inspect
=> "#<ZeroDivisionError: divided by 0. With additional info!>"
To jest podobne do Mark Rushakoff's odpowiedź, ale:
- nadpisuje
to_s
zamiastmessage
ponieważ domyślniemessage
jest zdefiniowane po prostuto_s
(przynajmniej w Rubim 2.0 i 2.2 gdzie go testowałem) - Połączenia
extend
dla ciebie, zamiast zmuszać rozmówcę do zrobienia tego dodatkowego kroku. - używa
define_method
i zamknięcia, aby można było odwoływać się do zmiennej lokalnejmessage
. Kiedy próbowałem użyć klasyvariable @@message
, ostrzegł, " warning: class variable access from toplevel "(Zobacz to pytanie: "ponieważ nie tworzysz klasy ze słowem kluczowym class, twoja zmienna klasy jest ustawiona naObject
, a nie na [Twój anonimowy Moduł]")
Cechy:
- łatwy w użyciu
- używa ponownie tego samego obiektu (zamiast tworzyć nową instancję klasy), więc rzeczy takie jak tożsamość obiektu, klasa i backtrace są zachowywane
-
to_s
,message
, iinspect
wszyscy odpowiadają odpowiednio - Może być używany z wyjątkiem, który jest już przechowywany w zmiennej; nie wymaga ponownego podnoszenia czegokolwiek (jak rozwiązanie, które wymagało przekazania backtrace do raise:
raise $!, …, $!.backtrace
). Było to dla mnie ważne, ponieważ wyjątek został przekazany do mojej metody logowania, a nie coś, co sam uratowałem.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:14