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

Nguyên nhân gây ra lỗi lồng nhau trong Ruby với Exception #

Đó 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.