Nó thường hữu ích để có thể nhận được ngoại lệ gần đây nhất, ngay cả khi mã của bạn không kiểm soát vòng đời của ngoại lệ đó. Hãy tưởng tượng rằng bạn muốn thêm tính năng phát hiện sự cố cơ bản vào ứng dụng của mình. Bạn muốn ghi lại thông tin bổ sung về bất kỳ sự cố nào xảy ra do một ngoại lệ không cần thiết.
Bước đầu tiên là thêm một trình xử lý chạy bất cứ khi nào ứng dụng của bạn thoát. Thật dễ dàng để thực hiện việc này thông qua phương thức at_exit của hạt nhân Ruby.
at_exit
puts "the app exited"
end
Nhưng làm thế nào chúng ta có thể biết liệu lệnh gọi lại thoát có được gọi là kết quả của một ngoại lệ hay không? Chà, Ruby cung cấp tên mã hóa là $!
biến toàn cục. Nó chứa ngoại lệ mới được nêu ra gần đây nhất đã xảy ra ở đâu đó trong ngăn xếp cuộc gọi hiện tại.
Thật tầm thường khi sử dụng $!
để phát hiện xem chương trình có bị thoát do ngoại lệ hay không. Nó trông giống như sau:
at_exit do
save_error_to_log($!) if $!
end
Các hạn chế của $!
Thật không may, $!
phương thức chỉ hoạt động nếu ngoại lệ xảy ra ở đâu đó trong ngăn xếp cuộc gọi hiện tại. Nếu bạn cứu một ngoại lệ, hãy thử truy cập $!
ngoài điều khoản giải cứu, bạn sẽ nhận được số không.
begin
raise "x"
rescue
puts $! # => RuntimeError
end
puts $! # => nil
Điều này có nghĩa là $!
khá vô dụng bên trong một shell như IRB. Thường thì trong IRB, tôi sẽ chạy một phương thức và nhận được một ngoại lệ. Đôi khi tôi muốn xem đối tượng ngoại lệ đó. Nhưng $!
không hoạt động cho điều này.
irb(main):001:0> 1/0
ZeroDivisionError: divided by 0
from (irb):1:in `/'
irb(main):002:0> $!
=> nil
Làm việc với $! với PRY
PRY vượt qua những hạn chế của $!
bằng cách thêm biến cục bộ của chính nó, _ex_
. Biến này chứa ngoại lệ chưa được ghi gần đây nhất.
[1] pry(main)> raise "hi"
RuntimeError: hi
from (pry):1:in `__pry__'
[2] pry(main)> _ex_
=> #<RuntimeError: hi>
Lý do mà PRY có thể làm được điều này là vì thực sự không có bất kỳ trường hợp ngoại lệ nào chưa được đề cập bên trong PRY hoặc IRB. Bản thân trình bao bắt các ngoại lệ và hiển thị chúng dưới dạng thông báo lỗi được định dạng độc đáo.
Tôi đã sao chép các bit liên quan của nguồn PRY bên dưới. Bạn có thể thấy rằng mã đánh giá các lệnh của bạn được bao bọc bên trong một khối bắt đầu / giải cứu / kết thúc. Khi một ngoại lệ có thể cứu được xảy ra, PRY sẽ lưu ngoại lệ vào self.last_exception và sau đó nó được gán cho _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
Yêu cầu tiếng Anh
Có lẽ bạn tìm thấy các tên biến như $!
một chút khó khăn trên mắt? May mắn thay, Ruby bao gồm một mô-đun có tên là "Tiếng Anh", cung cấp các phiên bản tiếng Anh của nhiều biến toàn cầu, nếu không thì trông giống như các từ khóa của rô bốt.
Từ đồng nghĩa với $!
là $ERROR_INFO
. Bạn có thể sử dụng nó ở bất cứ nơi nào bạn thường sử dụng $!
.
require "English"
begin
raise "x"
rescue
puts $ERROR_INFO # => RuntimeError
end
Và mặc dù hầu hết các từ tương đương tiếng Anh khác không liên quan gì đến chủ đề của bài đăng trên blog này, tôi đưa chúng vào các mục đích. Các biến tiếng Anh ở bên trái. Bản gốc ở bên phải.
$ 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 | $ & |
$ TRƯỚC | $ ` |
$ POSTMATCH | $ ‘ |
$ LAST_PAREN_MATCH | $ + |