มีประโยชน์มากที่ได้ most recent exception แม้ code ไม่ control lifecycle ของ exception สมมติอยากเพิ่ม basic crash detection ใน application อยาก log extra info เกี่ยวกับ crash จาก uncaught exception
ขั้นแรก add handler รันทุกครั้ง app exit ทำง่ายด้วย Ruby kernel's at_exit method
at_exit puts "the app exited" end
แต่มันรู้ยังไงว่า exit callback invoked เพราะ exception? Ruby มี $! global variable แปลกชื่อ มี most recently raised exception ใน current call stack
用 $! detect ว่า program exit เพราะ exception ง่าย:
at_exit do save_error_to_log($!) if $! end
Limitations ของ $!
เสียดาย $! ใช้ได้ก็ต่อเมื่อ exception occurred ใน current call stack ถ้า rescue exception แล้วพยายาม access $! นอก rescue clause ได้ nil
begin raise "x" rescue puts $! # => RuntimeError end puts $! # => nil
หมายถึง $! ไม่ค่อย useful ใน shell เหมือน IRB บ่อยๆ ใน IRB รัน method ได้ exception อยากได้ exception object แต่ $! ไม่ work
irb(main):001:0> 1/0 ZeroDivisionError: divided by 0 from (irb):1:in `/' irb(main):002:0> $! => nil
Working Around $! with PRY
PRY แก้ limitations ของ $! ด้วย local variable ของตัวเอง _ex_ มี most recent uncaught exception
[1] pry(main)> raise "hi" RuntimeError: hi from (pry):1:in `__pry__' [2] pry(main)> _ex_ => #<RuntimeError: hi>
เหตุผล PRY ทำได้เพราะไม่มี uncaught exceptions จริงๆ ใน PRY หรือ IRB Shell เอง catch exceptions แสดงเป็น nicely-formatted error messages
Copy relevant bits ของ PRY source ดู code evaluate commands wrapped ใน begin/rescue/end block เวลา rescuable exception เกิด PRY save exception ไป self.last_exception ต่อมา assigned ให้ _ex_
# Excerpted from the PRY source at https://github.com/pry/pry/blob/623306966bfa86890ac182bc8375ec9699abe90d/lib/pry/pry_instance.rb#L273 begin if !process_command_safely(line) @eval_string << "#{line.chomp}\n" if !line.empty? || !@eval_string.empty? end rescue RescuableException => e self.last_exception = e result = e Pry.critical_section do show_result(result) end return end
Require English
อาจรู้สึก variable names เหมือน $! แย่สำหรับตา? Ruby มี module "English" ให้ english-language versions ของ global variables ที่ดู像 robot cusswords
Synonym สำหรับ $! คือ $ERROR_INFO ใช้ได้ทุกที่ที่ใช้ $! ปกติ
require "English" begin raise "x" rescue puts $ERROR_INFO # => RuntimeError end
แม้ english equivalents อื่นๆ ไม่เกี่ยวกับ blog post นี้ ก็รวมไว้เพื่อสนุก English variables ซ้าย ต้นฉบับ ขวา
| $ERROR_INFO | $! |
| $ERROR_POSITION | $@ |
| $FS | $; |
| $FIELD_SEPARATOR | $; |
| $OFS | $, |
| $OUTPUT_FIELD_SEPARATOR | $, |
| $RS | $/ |
| $INPUT_RECORD_SEPARATOR | $/ |
| $ORS | $\ |
| $OUTPUT_RECORD_SEPARATOR | $\ |
| $INPUT_LINE_NUMBER | $. |
| $NR | $. |
| $LAST_READ_LINE | $_ |
| $DEFAULT_OUTPUT | $> |
| $DEFAULT_INPUT | $< |
| $PID | $$ |
| $PROCESS_ID | $$ |
| $CHILD_STATUS | $? |
| $LAST_MATCH_INFO | $~ |
| $IGNORECASE | $= |
| $ARGV | $* |
| $MATCH | $& |
| $PREMATCH | $` |
| $POSTMATCH | $' |
| $LAST_PAREN_MATCH | $+ |