जब आप रूबी में एक रेस्क्यू क्लॉज का उपयोग करते हैं, तो आप निर्दिष्ट कर सकते हैं कि आप किस प्रकार के एक्सेप्शन को रेस्क्यू करना चाहते हैं। आपको बस एक्सेप्शन क्लास की एक सूची प्रदान करने की आवश्यकता है जैसे:
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
लेकिन यह बहुत उबाऊ है! साथ ही, यह बहुत DRY दृष्टिकोण नहीं है। यह बहुत अधिक दिलचस्प होगा अगर हम किसी तरह रेस्क्यू क्लॉज को बता सकें कि केवल हमारी शर्तों से मेल खाने वाले एक्सेप्शन को रेस्क्यू करें। और चूंकि यह रूबी है, हम ऐसा कर सकते हैं!
रेस्क्यू एक्सेप्शन से कैसे मिलान करता है
जब एक रेस्क्यू ब्लॉक के अंदर एक एक्सेप्शन होता है, तो रूबी इंटरप्रेटर आपके द्वारा प्रदान की गई एक्सेप्शन क्लास की सूची के विरुद्ध एक्सेप्शन की क्लास की जांच करता है। यदि मेल खाता है, तो एक्सेप्शन रेस्क्यू हो जाता है।
मिलान कुछ इस तरह दिखता है:
exception_classes_to_rescue.any? do |c| c === raised_exception.class end
रूबी में हर दूसरे ऑपरेटर की तरह, === केवल एक मेथड है। इस मामले में यह 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
कस्टम एट्रिब्यूट के आधार पर एक्सेप्शन रेस्क्यू करना
चूंकि आपके पास एक्सेप्शन ऑब्जेक्ट तक पहुंच है, आपका मैचर उस ऑब्जेक्ट के अंदर मौजूद किसी भी डेटा का उपयोग कर सकता है।
एक पल के लिए कल्पना करें कि आपके पास एक एक्सेप्शन है जिसमें "severity" नामक एक कस्टम एट्रिब्यूट है। आप एक्सेप्शन की सभी "कम गंभीरता" वाली घटनाओं को निगलना चाहेंगे, लेकिन किसी भी "उच्च गंभीरता" वाली घटनाओं को पास होने देंगे। आप इसे इस तरह लागू कर सकते हैं:
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
इसे डायनामिक बनाना
यह सब बहुत अच्छा है, लेकिन इसमें बहुत सारा बॉयलरप्लेट कोड शामिल है। प्रत्येक मैचर के लिए मैन्युअल रूप से अलग-अलग क्लास को परिभाषित करना अत्यधिक लगता है। सौभाग्य से हम थोड़े मेटाप्रोग्रामिंग का उपयोग करके इसे काफी हद तक DRY कर सकते हैं।
नीचे दिए गए उदाहरण में, हम एक मेथड को परिभाषित कर रहे हैं जो हमारे लिए मैचर क्लास उत्पन्न करता है। आप एक ब्लॉक के माध्यम से मिलान तर्क प्रदान करते हैं, और मैचर जनरेटर एक नई क्लास बनाता है जो ब्लॉक को अपने === मेथड के अंदर उपयोग करता है।
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
एक दाना नमक का
रूबी में कई अच्छी तरकीबों की तरह, मैं यह तय नहीं कर पा रहा हूँ कि यह सब पागलपन है या एक बढ़िया विचार। शायद यह दोनों का थोड़ा सा है। जबकि मैं निश्चित रूप से सुझाव नहीं दूंगा कि आप इस तकनीक को पहली पसंद के रूप में अपनाएं, मैं देख सकता हूँ कि यह ऊपर वर्णित स्थिति में कैसे उपयोगी होगा जहां आप गंभीरता के आधार पर एक्सेप्शन को रेस्क्यू करना चाहते हैं। किसी भी मामले में, यह आपके टूलबेल्ट में एक और उपकरण है!