เป็นรูปแบบทั่วไปใน 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.