เมื่อคุณใช้คำสั่งช่วยเหลือใน Ruby คุณสามารถระบุประเภทของข้อยกเว้นที่คุณต้องการช่วยเหลือได้ สิ่งที่คุณต้องทำคือจัดเตรียมรายการคลาสข้อยกเว้นดังนี้:
begin
raise RuntimeError
rescue RuntimeError, NoMethodError
puts "rescued!"
end
แต่ถ้าคุณไม่ทราบว่าคลาสข้อยกเว้นจะเป็นอย่างไรในขณะที่คุณเขียนโค้ด คำตอบที่ชัดเจนที่สุดคือการช่วยเหลือข้อยกเว้นทั้งหมด ทำการทดสอบบางประเภท จากนั้นจึงเพิ่มข้อยกเว้นที่ไม่ผ่านขึ้นใหม่ บางสิ่งเช่นนี้:
begin
raise "FUBAR! The ship's going down!"
rescue => e
raise unless e.message =~ /^FUBAR/
... do something ...
end
แต่มันน่าเบื่อมาก! นอกจากนี้ยังไม่ใช่แนวทางที่แห้งมาก คงจะน่าสนใจกว่านี้มากถ้าเราสามารถบอกคำสั่งกู้ภัยเพื่อช่วยเหลือข้อยกเว้นที่ตรงกับเงื่อนไขของเราเท่านั้น และเนื่องจากนี่คือ Ruby เราทำได้!
กู้ภัยตรงกับข้อยกเว้นอย่างไร
เมื่อมีข้อยกเว้นเกิดขึ้นภายในบล็อกการกู้ชีพ ตัวแปล ruby จะตรวจสอบคลาสของข้อยกเว้นกับรายการคลาสข้อยกเว้นที่คุณระบุ หากมีการแข่งขัน ข้อยกเว้นจะได้รับการช่วยเหลือ
การจับคู่จะมีลักษณะดังนี้:
exception_classes_to_rescue.any? do |c|
c === raised_exception.class
end
เช่นเดียวกับโอเปอเรเตอร์อื่นๆ ใน Ruby ===
เป็นเพียงวิธีการ ในกรณีนี้เป็นวิธีการของ c
. แล้วเราจะทำอย่างไรถ้าเรากำหนด ===
. ของเราเอง วิธีการ?
ในตัวอย่างด้านล่าง ฉันกำลังสร้างคลาสชื่อ Anything
โดยที่ Anything === x
คืนค่า จริง สำหรับค่า x ใดๆ ถ้าฉันให้ชั้นเรียนนี้เป็นข้อโต้แย้งในการช่วยเหลือ มันจะทำให้ข้อยกเว้นทั้งหมดได้รับการช่วยเหลือ
class Anything
def self.===(exception)
true
end
end
begin
raise EOFError
rescue Anything
puts "It rescues ANYTHING!"
end
แม้ว่าจะมีวิธีที่ดีกว่ามากในการกู้คืนข้อยกเว้นทั้งหมด แต่โค้ดนี้น่าสนใจเพราะแสดงให้เราเห็นสองสิ่ง:
-
คุณสามารถให้คลาสของคำสั่งช่วยเหลือที่ไม่สืบทอดจาก
Exception
ตราบใดที่พวกเขาใช้===
-
หากคุณควบคุม
===
คุณสามารถควบคุมข้อยกเว้นที่จะได้รับการช่วยเหลือ
การช่วยเหลือข้อยกเว้นตามข้อความ
เมื่อรู้ว่าเรารู้อะไรในตอนนี้ การเขียนโค้ดก็ง่ายที่จะกู้คืนข้อยกเว้นได้ก็ต่อเมื่อข้อความของข้อยกเว้นตรงกับรูปแบบ
class AllFoobarErrors
def self.===(exception)
# rescue all exceptions with messages starting with FOOBAR
exception.message =~ /^FOOBAR/
end
end
begin
raise EOFError, "FOOBAR: there was an eof!"
rescue AllFoobarErrors
puts "rescued!"
end
การช่วยเหลือข้อยกเว้นตามแอตทริบิวต์ที่กำหนดเอง
เนื่องจากคุณมีสิทธิ์เข้าถึงอ็อบเจ็กต์ข้อยกเว้น ตัวจับคู่ของคุณจึงสามารถใช้ข้อมูลใดก็ได้ที่อยู่ในออบเจกต์นั้น
ลองนึกภาพสักครู่ว่าคุณมีข้อยกเว้นที่มีแอตทริบิวต์ที่กำหนดเองที่เรียกว่า "ความรุนแรง" คุณต้องการกลืน "ความรุนแรงต่ำ" ที่เกิดข้อยกเว้นทั้งหมด แต่ปล่อยให้ผ่านเหตุการณ์ "ความรุนแรงสูง" ใดๆ ไป คุณอาจนำไปใช้เช่นนั้น:
class Infraction < StandardError
attr_reader :severity
def initialize(severity)
@severity = severity
end
end
class LowSeverityInfractions
def self.===(exception)
exception.is_a?(Infraction) && exception.severity == :low
end
end
begin
raise Infraction.new(:low)
rescue LowSeverityInfractions
puts "rescued!"
end
ทำให้เป็นไดนามิก
ทั้งหมดนี้ค่อนข้างเจ๋ง แต่มันเกี่ยวข้องกับรหัสสำเร็จรูปจำนวนมาก ดูเหมือนว่ามากเกินไปที่จะต้องกำหนดคลาสแยกกันสำหรับตัวจับคู่แต่ละตัว โชคดีที่เราสามารถทำให้สิ่งนี้แห้งได้เล็กน้อยโดยใช้โปรแกรมเมตาดาต้าเพียงเล็กน้อย
ในตัวอย่างด้านล่าง เรากำลังกำหนดวิธีการที่สร้างคลาสที่จับคู่สำหรับเรา คุณระบุตรรกะการจับคู่ผ่านบล็อก และตัวสร้างการจับคู่จะสร้างคลาสใหม่ที่ใช้บล็อกภายใน ===
วิธีการ
def exceptions_matching(&block)
Class.new do
def self.===(other)
@block.call(other)
end
end.tap do |c|
c.instance_variable_set(:@block, block)
end
end
begin
raise "FOOBAR: We're all doomed!"
rescue exceptions_matching { |e| e.message =~ /^FOOBAR/ }
puts "rescued!"
end
เม็ดเกลือ
เช่นเดียวกับลูกเล่นเจ๋ง ๆ มากมายใน Ruby ฉันไม่สามารถตัดสินใจได้ทั้งหมดว่าทั้งหมดนี้เป็นเรื่องบ้าหรือเป็นความคิดที่ดี บางทีมันอาจจะเล็กน้อยของทั้งสอง แม้ว่าฉันจะไม่แนะนำให้คุณใช้เทคนิคนี้เป็นตัวเลือกแรก แต่ฉันสามารถเห็นได้ว่าเทคนิคนี้จะมีประโยชน์อย่างไรในสถานการณ์เช่นสถานการณ์ข้างต้นที่คุณต้องการช่วยเหลือข้อยกเว้นตามความรุนแรง เป็นอีกเครื่องมือหนึ่งในแถบเครื่องมือของคุณ!