Đó là một mô hình phổ biến trong Ruby là giải cứu và ngoại lệ và nâng cao lại một loại ngoại lệ khác. ActionView là một ví dụ thực sự rõ ràng về điều này. Như tôi đã đề cập trong một bài đăng blog trước đó về TracePoint, ActionView nuốt chửng bất kỳ ngoại lệ nào xảy ra trong các mẫu của bạn và nâng chúng lên thành một ActionView::TemplateError
.
Đôi khi điều này không đủ tốt. Bạn THỰC SỰ cần ngoại lệ ban đầu đó vì nó có một số dữ liệu trong đó sẽ giúp bạn giải quyết vấn đề. May mắn thay, kể từ Ruby 2.1, bạn có thể sử dụng phương thức Exception # gây ra để làm điều đó.
Hãy xem nó hoạt động như thế nào trong thực tế. Ở đây, chúng tôi nêu ra một NoMethodError
, sau đó ngay lập tức nuốt nó và tạo ra một RuntimeError
. Sau đó, chúng tôi bắt được RuntimeError
và sử dụng #cause để lấy NoMethodError
gốc .
def fail_and_reraise
raise NoMethodError
rescue
raise RuntimeError
end
begin
fail_and_reraise
rescue => e
puts "#{ e } caused by #{ e.cause }"
end
Dấu vết lồng nhau và thuộc tính tùy chỉnh
Phương thức #cause thực sự trả về đối tượng ngoại lệ ban đầu. Điều đó có nghĩa là bạn có thể truy cập bất kỳ siêu dữ liệu nào nằm trong ngoại lệ ban đầu. Bạn cũng có thể lấy lại dấu vết ban đầu.
class EatingError < StandardError
attr_reader :food
def initialize(food)
@food = food
end
end
def fail_and_reraise
raise EatingError.new("soup")
rescue
raise RuntimeError
end
begin
fail_and_reraise
rescue => e
puts "#{ e } caused by #{ e.cause } while eating #{ e.cause.food }"
puts e.cause.backtrace.first
end
Đến vô cùng và xa hơn nữa!
Mặc dù các ví dụ trên chỉ sâu một cấp, các ngoại lệ lồng nhau trong Ruby có thể có bất kỳ số cấp nào. Tôi sẽ rất ngạc nhiên nếu bạn cần đi sâu hơn ba trong bốn cấp độ.
... nhưng chỉ để cho vui, tôi nghĩ tôi sẽ thử tạo một ngoại lệ lồng nhau sâu 100 cấp. Đó là một đoạn mã nhỏ ngớ ngẩn và tôi hy vọng bạn sẽ không bao giờ thấy nó giống như trong quá trình sản xuất.
def recursively_raise(c=0)
raise "Level #{ c }"
rescue => e
if c < 100
recursively_raise(c + 1)
else
recursively_print(e)
end
end
def recursively_print(e)
if e
puts e
recursively_print(e.cause)
end
end
recursively_raise()
# ... Prints the following:
# Level 100
# Level 99
# Level 98
# etc.