ไวยากรณ์การเพิ่มของ Ruby มีตัวเลือกให้คุณสองสามตัวเลือกสำหรับการระบุประเภทของข้อผิดพลาดที่คุณต้องการให้ยกขึ้น ในโค้ดด้านล่าง ฉันได้แสดงวิธีเพิ่ม RuntimeError สามวิธี
raise "hello"
raise RuntimeError, "hello"
raise RuntimeError.new("hello")
# ...all of the above result in "RuntimeError: hello"
เป็นเรื่องที่ดี แต่จะเกิดอะไรขึ้นเมื่อฉันต้องการแจ้งเรื่องอื่นที่ไม่ใช่ข้อยกเว้น จะทำอย่างไรถ้าฉันต้องการเพิ่มจำนวน รูบี้ไม่ยอมให้ฉัน ฉันได้รับข้อความแสดงข้อผิดพลาดดังนี้:
raise 1
# TypeError: exception class/object expected
ตอนนี้ข้อความนี้อาจทำให้คุณเชื่อว่าการยกต้องการคลาส/อ็อบเจ็กต์ข้อยกเว้นเป็นพารามิเตอร์ แต่มันไม่ถูกต้อง!
แนะนำexception
วิธีการ
หากคุณ raise foo
วิธีการยกไม่คาดหวังว่า foo จะเป็นวัตถุข้อยกเว้น คาดว่าจะได้รับวัตถุยกเว้นเมื่อใดก็ตามที่เรียก foo.exception
.
สิ่งที่ต้องจำไว้คือคุณสามารถส่งผ่านอะไรก็ได้ที่จะเพิ่ม ตราบใดที่มีวิธีที่เรียกว่าข้อยกเว้นที่ส่งกลับข้อยกเว้น
ดังนั้น ถ้าคุณต้องการ คุณสามารถเรียนหมายเลขของ Monkeypatch ruby เพื่อให้คุณสามารถเพิ่มจำนวนได้ หน้าตาจะประมาณนี้:
class Fixnum
def exception
RuntimeError.new("I'm number: #{ self }")
end
end
raise 42
# ...results in raise_number.rb:7:in `<main>': I'm number: 42 (RuntimeError)
นี่เป็นกลอุบายของปาร์ตี้ที่ดี แต่จะมีประโยชน์ในชีวิตจริงหรือไม่? การใช้งานจริงหลักที่ฉันเห็นสำหรับเทคนิคนี้คือการแยกตรรกะที่จำเป็นในการสร้างข้อยกเว้นออกจากตรรกะที่ตัดสินใจยกข้อยกเว้น นี่เป็นกรณีขอบเล็กน้อย แต่มาดูกันว่าจะหน้าตาเป็นอย่างไร
ตัวอย่างที่นำไปใช้ได้จริง
สมมติว่าฉันต้องการอ่านบรรทัดข้อมูลจาก IO บางประเภท อาจเป็นเครือข่าย IO อาจเป็นไฟล์ มันไม่สำคัญจริงๆ ฉันแค่ต้องการอ่านข้อมูลและดูว่าข้อมูลถูกต้องหรือไม่
หากข้อมูลที่ฉันอ่านไม่ถูกต้อง ฉันต้องการยกข้อยกเว้น แต่ต้องปรับข้อยกเว้นให้เข้ากับอินพุต การเชื่อมต่อเครือข่ายต้องมีข้อมูลการดีบักที่แตกต่างจากไฟล์ในเครื่อง ฉันสามารถทำได้โดยจัดเตรียมวิธีการยกเว้นแบบกำหนดเองสำหรับคลาสอินพุตแต่ละประเภท นี่คือทับทิมเทียมที่แสดงให้เห็นว่าอาจมีหน้าตาเป็นอย่างไร
# These three classes represent different kinds of IO with different exceptions.
class NetworkConnection
...
def exception
NetworkConnectionError.new(url: url, ...)
end
end
class LocalFile
...
def exception
FileError.new(path: path, ...)
end
end
class UnixPipe
...
def exception
PipeError.new(...)
end
end
def read_all(*items)
items.each do |item|
if item.readline != "foo"
# We raise the item, which causes the appropriate exception class to be used.
raise item
end
end
end
read_all(NetworkConnection.new(url: "example.com"), LocalFile.new("/something"), UnixPipe.new)