Computer >> Máy Tính >  >> Lập trình >> Ruby

Làm việc với các ngoại lệ Ruby bên ngoài khối cứu hộ

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 $!$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 $ +