Vấn đề thường gặp Ruby là nhận error message, về mặt kỹ thuật gọi là "exceptions". Exception có thể dự đoán được, như file có sẵn lúc nhưng đôi khi thiếu, hoặc API chỉ tạm available do hạn chế, hoặc bất ngờ. Hôm nay bạn học quản lý lỗi dự đoán được. Làm sao? Giới thiệu "begin" & "rescue" Ruby, hai từ khóa quan trọng xử lý điều kiện lỗi. Cách hoạt động? Trước hết hiểu: chương trình Ruby có thể trigger error tại nhiều điểm khi chạy. Ví dụ: Đọc file không tồn tại. Chia số cho 0. Web server làm việc có SSL certificate cũ. Khi lỗi xảy ra... Ruby KHÔNG crash ngay! Bạn có cơ hội recover từ error. Gọi là "exception handling". Ruby cho từ khóa implement error recovery: begin & rescue. Khám phá cách dùng!
Cách Xử Lý Exception Ruby
Bọc code raise exception với block begin / rescue. Cách hoạt động... Phần đầu (begin), có code bạn chạy & có thể raise exception. Ví dụ:
begin
IO.sysopen('/dev/null')
rescue
# ...
end
Ở đây thử mở file với sysopen. Exception raise nếu không mở được. Thời điểm hoàn hảo dùng rescue! Dùng từ khóa này nói điều gì xảy ra khi exception raise. Vậy failure mode dưới kiểm soát. Ví dụ:
begin
IO.sysopen('/dev/null')
rescue
puts "Can't open IO device."
end
Bạn muốn log error & cung cấp default value. Đừng. Bỏ qua. Lỗi.
Rescue Nhiều Exception
Cần biết rescue nhận đối số tùy chọn. Đối số này là lớp exception bạn muốn rescue. Phụ thuộc code bạn chạy. Với IO: Errno::ENOENT cho file thiếu, hoặc Errno::EACCES cho lỗi permission. Phần tốt: Xử lý nhiều exception trong cùng block begin/rescue. Như sau:
begin
IO.sysopen('/dev/null')
rescue Errno::ENOENT
puts "File not found."
rescue Errno::EACCES
puts "Insufficient permissions, not allowed to open file."
end
Muốn cùng hành động cho nhiều exception... Làm:
begin
IO.sysopen('/dev/null')
rescue Errno::ENOENT, Errno::EACCES
puts "There was an error opening the file."
end
Cách Rescue Exception Bên Trong Block & Method
Không luôn cần begin. Có trường hợp bỏ qua. Ở đâu? Bên trong method & block. Ví dụ:
def get_null_device
IO.sysopen('/dev/null')
rescue Errno::ENOENT
puts "Can't open IO device."
end
Định nghĩa method tự làm việc của begin, nên bỏ qua được. Cũng làm với block. Ví dụ:
["a.txt", "b.txt", "c.txt"].map do |f|
IO.sysopen(f)
rescue Errno::ENOENT
puts "Can't open IO device: #{f}."
end
Còn một cách dùng rescue không begin. Xem cách hoạt động.
Hiểu Inline Rescue & Tại Sao Nguy Hiểm
Dùng rescue inline. Trong vài trường hợp hiếm, dạng này hữu ích. Ví dụ:
["a.txt", "b.txt", "c.txt"].select { |f| File.open(f) rescue nil }.map(&:size)
Cho mở chỉ file tồn tại & bỏ qua file không. Kết quả nhận size file tồn tại. Không exception raise. Tại sao làm vậy? Giữ code một dòng. Đó là tất cả. Có "nguy hiểm ẩn" khi dùng dạng rescue này vì bắt tất cả exception kế thừa StandardError. Đó là hầu hết exception. Tại sao KHÔNG tốt? Vì tốt nhất chỉ handle exception cụ thể, thay vì chọn rộng. Tránh giấu lỗi từ chính mình! Lỗi ẩn dẫn đến hành vi kỳ lạ & khó debug.
Tóm Tắt
Bạn đã học lỗi Ruby, exception handling cơ bản & từ khóa rescue / begin. Hãy chia sẻ nếu hữu ích 🙂 Cảm ơn bạn đã đọc!