Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

ข้อผิดพลาดที่ซ้อนกันใน Ruby ด้วย Exception#cause

เป็นรูปแบบทั่วไปใน Ruby เพื่อช่วยเหลือและยกเว้นและเรียกข้อยกเว้นประเภทอื่นขึ้นมาใหม่ ActionView เป็นตัวอย่างที่ชัดเจนอย่างหนึ่งของเรื่องนี้ ดังที่ฉันได้กล่าวไว้ในบล็อกโพสต์ก่อนหน้านี้เกี่ยวกับ TracePoint แล้ว ActionView จะกลืนข้อยกเว้นทุกอย่างที่เกิดขึ้นในเทมเพลตของคุณและยกขึ้นใหม่เป็น ActionView::TemplateError .

บางครั้งก็ไม่ดีพอ คุณต้องการข้อยกเว้นดั้งเดิมนั้นจริงๆ เพราะมีข้อมูลบางอย่างในนั้นที่จะช่วยคุณแก้ปัญหา โชคดีที่ Ruby 2.1 คุณสามารถใช้วิธี Exception#cause เพื่อทำเช่นนั้นได้

เรามาดูกันว่ามันทำงานอย่างไรในทางปฏิบัติ ที่นี่ เรายก NoMethodError จากนั้นกลืนมันทันทีและยก RuntimeError . จากนั้นเราก็จับ RuntimeError และใช้ #cause เพื่อรับ NoMethodError . ดั้งเดิม .

def fail_and_reraise
  raise NoMethodError
rescue
  raise RuntimeError
end

begin
  fail_and_reraise
rescue => e
  puts "#{ e } caused by #{ e.cause }"
end

backtraces ที่ซ้อนกันและแอตทริบิวต์ที่กำหนดเอง

วิธี #cause จะคืนค่าอ็อบเจ็กต์ข้อยกเว้นดั้งเดิม ซึ่งหมายความว่าคุณสามารถเข้าถึงข้อมูลเมตาที่เป็นส่วนหนึ่งของข้อยกเว้นเดิมได้ คุณสามารถรับการย้อนรอยเดิมได้เช่นกัน

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


สู่ความไม่มีที่สิ้นสุด!

แม้ว่าตัวอย่างข้างต้นจะมีความลึกเพียงระดับเดียวเท่านั้น แต่ข้อยกเว้นที่ซ้อนกันใน Ruby สามารถมีได้หลายระดับ ฉันจะแปลกใจถ้าคุณจำเป็นต้องลึกมากกว่าสามในสี่ระดับ

...แต่เพื่อความสนุก ฉันคิดว่าฉันจะลองสร้างข้อยกเว้นเชิงลึก 100 ระดับ มันเป็นโค้ดเล็กๆ ที่งี่เง่า และฉันหวังว่าคุณจะไม่เคยเห็นมันเหมือนตอนใช้งานจริง

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.