दूसरे दिन मैं शुरुआती लोगों के लिए लिखे गए Ruby एक्सेप्शन के परिचय की खोज कर रहा था - ऐसे लोग जो बेसिक Ruby सिंटैक्स जानते हैं लेकिन वास्तव में निश्चित नहीं हैं कि एक्सेप्शन क्या है या यह क्यों उपयोगी है। मुझे कोई नहीं मिला, इसलिए मैंने खुद इस पर एक प्रयास करने का फैसला किया। मुझे आशा है कि आप इसे उपयोगी पाएंगे। यदि कोई बिंदु भ्रमित करने वाले हैं, तो बेझिझक मुझे @StarrHorne पर ट्वीट करें। :)
एक्सेप्शन क्या है?
एक्सेप्शन अप्रत्याशित घटनाओं से निपटने का Ruby का तरीका है।
यदि आपने कभी अपने कोड में टाइपो किया है, जिससे आपका प्रोग्राम SyntaxError या NoMethodError जैसे संदेश के साथ क्रैश हो गया है, तो आपने एक्सेप्शन को एक्शन में देखा है।
जब आप Ruby में एक एक्सेप्शन उठाते हैं, तो दुनिया रुक जाती है और आपका प्रोग्राम बंद होना शुरू हो जाता है। यदि कुछ भी प्रक्रिया को नहीं रोकता, तो आपका प्रोग्राम अंततः एक त्रुटि संदेश के साथ बाहर निकल जाएगा।
यहां एक उदाहरण है। नीचे दिए गए कोड में, हम शून्य से विभाजित करने का प्रयास करते हैं। यह असंभव है, इसलिए Ruby ZeroDivisionError नामक एक एक्सेप्शन उठाता है। प्रोग्राम छोड़ देता है और एक त्रुटि संदेश प्रिंट करता है।
1 / 0 # प्रोग्राम क्रैश होता है और आउटपुट करता है: "ZeroDivisionError: divided by 0"
क्रैश होने वाले प्रोग्राम हमारे उपयोगकर्ताओं को नाराज करते हैं। इसलिए हम आमतौर पर इस शटडाउन प्रक्रिया को रोकना चाहते हैं, और त्रुटि पर बुद्धिमानी से प्रतिक्रिया करना चाहते हैं।
इसे "रेस्क्यू करना," "हैंडल करना," या "कैच करना" एक्सेप्शन कहा जाता है। उन सभी का मतलब एक ही है। Ruby में आप इसे इस तरह करते हैं:
begin # यहां कोई भी एक्सेप्शन...
1/0
rescue # ...इस कोड को चलाने का कारण बनेगा
puts "Got an exception, but I'm responding intelligently!"
do_something_intelligent()
end
# यह प्रोग्राम क्रैश नहीं करता।
# आउटपुट: "Got an exception, but I'm responding intelligently!"
एक्सेप्शन अभी भी होता है, लेकिन यह प्रोग्राम को क्रैश नहीं होने देता क्योंकि इसे "रेस्क्यू" किया गया था। बाहर निकलने के बजाय, Ruby रेस्क्यू ब्लॉक में कोड चलाता है, जो एक संदेश प्रिंट करता है।
यह अच्छा है, लेकिन इसकी एक बड़ी सीमा है। यह हमें बताता है "कुछ गलत हुआ," बिना हमें बताए कि क्या गलत हुआ।
जो कुछ गलत हुआ उसके बारे में सारी जानकारी एक एक्सेप्शन ऑब्जेक्ट में समाहित होगी।
एक्सेप्शन ऑब्जेक्ट्स
एक्सेप्शन ऑब्जेक्ट्स सामान्य Ruby ऑब्जेक्ट्स हैं। वे आपके द्वारा अभी रेस्क्यू किए गए एक्सेप्शन के लिए "क्या हुआ" के बारे में सभी डेटा रखते हैं।
एक्सेप्शन ऑब्जेक्ट प्राप्त करने के लिए, आप थोड़ा अलग रेस्क्यू सिंटैक्स का उपयोग करेंगे।
# सभी त्रुटियों को रेस्क्यू करता है, और एक्सेप्शन ऑब्जेक्ट को `e` में रखता है
rescue => e
# केवल ZeroDivisionError को रेस्क्यू करता है और एक्सेप्शन ऑब्जेक्ट को `e` में रखता है
rescue ZeroDivisionError => e
ऊपर के दूसरे उदाहरण में, ZeroDivisionError e में ऑब्जेक्ट की क्लास है। हमने जिन "प्रकारों" के एक्सेप्शन के बारे में बात की है वे वास्तव में सिर्फ क्लास नाम हैं।
एक्सेप्शन ऑब्जेक्ट उपयोगी डीबग डेटा भी रखते हैं। आइए हमारे ZeroDivisionError के लिए एक्सेप्शन ऑब्जेक्ट पर एक नज़र डालें।
begin # यहां कोई भी एक्सेप्शन...
1/0
rescue ZeroDivisionError => e
puts "Exception Class: #{ e.class.name }"
puts "Exception Message: #{ e.message }"
puts "Exception Backtrace: #{ e.backtrace }"
end
# आउटपुट:
# Exception Class: ZeroDivisionError
# Exception Message: divided by 0
# Exception Backtrace: ...बैकट्रेस एक ऐरे के रूप में...
अधिकांश Ruby एक्सेप्शन की तरह, इसमें इसकी क्लास नाम के साथ एक मैसेज और एक बैकट्रेस होता है।
अपने खुद के एक्सेप्शन उठाना
अब तक हमने केवल एक्सेप्शन रेस्क्यू करने के बारे में बात की है। आप अपने खुद के एक्सेप्शन भी ट्रिगर कर सकते हैं। इस प्रक्रिया को "उठाना" कहा जाता है। आप raise मेथड को कॉल करके ऐसा करते हैं।
जब आप अपने खुद के एक्सेप्शन उठाते हैं, तो आप चुन सकते हैं कि किस प्रकार के एक्सेप्शन का उपयोग करना है। आप त्रुटि संदेश भी सेट कर सकते हैं।
यहां एक उदाहरण है:
begin
# "you messed up!" संदेश के साथ एक ArgumentError उठाता है
raise ArgumentError.new("You messed up!")
rescue ArgumentError => e
puts e.message
end
# आउटपुट: You messed up!
जैसा कि आप देख सकते हैं, हम एक कस्टम मैसेज ("You messed up!") के साथ एक नया एरर ऑब्जेक्ट (एक ArgumentError) बना रहे हैं और इसे raise मेथड में पास कर रहे हैं।
यह Ruby होने के नाते, raise को कई तरीकों से कॉल किया जा सकता है:
# यह मेरा पसंदीदा है क्योंकि यह इतना स्पष्ट है
raise RuntimeError.new("You messed up!")
# ...समान परिणाम उत्पन्न करता है
raise RuntimeError, "You messed up!"
# ...समान परिणाम उत्पन्न करता है। लेकिन आप केवल इस तरह
# RuntimeErrors उठा सकते हैं
raise "You messed up!"
कस्टम एक्सेप्शन बनाना
Ruby के बिल्ट-इन एक्सेप्शन महान हैं, लेकिन वे हर संभावित उपयोग के मामले को कवर नहीं करते।
क्या होगा यदि आप एक यूजर सिस्टम बना रहे हैं और जब उपयोगकर्ता साइट के ऑफ-लिमिट्स हिस्से तक पहुंचने का प्रयास करता है तो एक एक्सेप्शन उठाना चाहते हैं? Ruby के मानक एक्सेप्शन में से कोई भी फिट नहीं बैठता, इसलिए आपका सबसे अच्छा दांव एक नए प्रकार का एक्सेप्शन बनाना है।
कस्टम एक्सेप्शन बनाने के लिए, बस StandardError से इनहेरिट करने वाली एक नई क्लास बनाएं।
class PermissionDeniedError < StandardError
end
raise PermissionDeniedError.new()
यह सिर्फ एक सामान्य Ruby क्लास है। इसका मतलब है कि आप इसमें किसी अन्य क्लास की तरह मेथड और डेटा जोड़ सकते हैं। आइए "action" नामक एक एट्रिब्यूट जोड़ें:
class PermissionDeniedError < StandardError
attr_reader :action
def initialize(message, action)
# मैसेज सेट करने के लिए पैरेंट के कंस्ट्रक्टर को कॉल करें
super(message)
# एक्शन को इंस्टेंस वेरिएबल में स्टोर करें
@action = action
end
end
# फिर, जब उपयोगकर्ता कुछ ऐसा डिलीट करने का प्रयास करता है जिसके लिए उनके पास
# अनुमति नहीं है, तो आप ऐसा कुछ कर सकते हैं:
raise PermissionDeniedError.new("Permission Denied", :delete)
क्लास हायरार्की
हमने StandardError को सबक्लास करके एक कस्टम एक्सेप्शन बनाया, जो स्वयं Exception को सबक्लास करता है।
वास्तव में, यदि आप Ruby में किसी भी एक्सेप्शन की क्लास हायरार्की को देखते हैं, तो आप अंततः इसे Exception पर वापस ले जाएंगे। यहां, मैं आपको साबित कर दूंगा। ये Ruby के अधिकांश बिल्ट-इन एक्सेप्शन हैं, जिन्हें हायरार्किकली प्रदर्शित किया गया है:
Exception
NoMemoryError
ScriptError
LoadError
NotImplementedError
SyntaxError
SignalException
Interrupt
StandardError
ArgumentError
IOError
EOFError
IndexError
LocalJumpError
NameError
NoMethodError
RangeError
FloatDomainError
RegexpError
RuntimeError
SecurityError
SystemCallError
SystemStackError
ThreadError
TypeError
ZeroDivisionError
SystemExit
आपको इन सभी को याद रखने की आवश्यकता नहीं है, बिल्कुल। मैं आपको ये दिखा रहा हूं क्योंकि हायरार्की का यह विचार एक विशिष्ट कारण से बहुत महत्वपूर्ण है।
किसी विशिष्ट क्लास के एरर को रेस्क्यू करना उसके चाइल्ड क्लास के एरर को भी रेस्क्यू करता है।
आइए इसे फिर से आजमाएं...
जब आप rescue StandardError करते हैं, तो आप न केवल StandardError क्लास के एक्सेप्शन को रेस्क्यू करते हैं बल्कि उसके चिल्ड्रन के भी। यदि आप चार्ट को देखते हैं, तो आप देखेंगे कि यह बहुत सारे हैं: ArgumentError, IOError, आदि।
यदि आप rescue Exception करते, तो आप हर एक एक्सेप्शन को रेस्क्यू कर लेते, जो एक बहुत बुरा विचार होगा।
सभी एक्सेप्शन को रेस्क्यू करना (खराब तरीका)
यदि आप चिल्लाए जाना चाहते हैं, तो स्टैक ओवरफ्लो पर जाएं और कुछ कोड पोस्ट करें जो ऐसा दिखता है:
// ऐसा न करें
begin
do_something()
rescue Exception => e
...
end
ऊपर दिया गया कोड हर एक्सेप्शन को रेस्क्यू करेगा। ऐसा न करें! यह आपके प्रोग्राम को अजीब तरीकों से तोड़ देगा।
ऐसा इसलिए है क्योंकि Ruby एक्सेप्शन का उपयोग केवल त्रुटियों के लिए नहीं करता। यह ऑपरेटिंग सिस्टम से "सिग्नल" नामक संदेशों को संभालने के लिए भी उनका उपयोग करता है। यदि आपने कभी किसी प्रोग्राम से बाहर निकलने के लिए "ctrl-c" दबाया है, तो आपने एक सिग्नल का उपयोग किया है। सभी एक्सेप्शन को दबाकर, आप उन सिग्नलों को भी दबा देते हैं।
कुछ प्रकार के एक्सेप्शन भी हैं - जैसे सिंटैक्स एरर - जो वास्तव में आपके प्रोग्राम को क्रैश करने का कारण बनने चाहिए। यदि आप उन्हें दबाते हैं, तो आपको कभी पता नहीं चलेगा कि आपने टाइपो या अन्य गलतियां कब कीं।
सभी एरर को रेस्क्यू करना (सही तरीका)
वापस जाकर क्लास हायरार्की चार्ट को देखें और आप देखेंगे कि आपके द्वारा रेस्क्यू किए जाने वाले सभी एरर StandardError के चिल्ड्रन हैं।
इसका मतलब है कि यदि आप "सभी एरर" को रेस्क्यू करना चाहते हैं तो आपको StandardError को रेस्क्यू करना चाहिए।
begin
do_something()
rescue StandardError => e
# केवल आपके ऐप के एक्सेप्शन निगले जाते हैं। SyntaxError जैसी चीजें अकेली छोड़ दी जाती हैं।
end
वास्तव में, यदि आप एक्सेप्शन क्लास निर्दिष्ट नहीं करते हैं, तो Ruby मान लेता है कि आप StandardError का मतलब है।
begin
do_something()
rescue => e
# यह StandardError को रेस्क्यू करने के समान है
end
विशिष्ट एरर को रेस्क्यू करना (सर्वोत्तम दृष्टिकोण)
अब जब आप जानते हैं कि सभी एरर को कैसे रेस्क्यू करना है, तो आपको पता होना चाहिए कि यह आमतौर पर एक बुरा विचार है, एक कोड स्मेल, हानिकारक माना जाता है, आदि।
वे आमतौर पर इस बात का संकेत हैं कि आप यह पता लगाने में बहुत आलसी थे कि किन विशिष्ट एक्सेप्शन को रेस्क्यू करने की आवश्यकता है। और वे लगभग हमेशा आपको परेशान करने के लिए वापस आएंगे।
इसलिए समय लें और इसे सही तरीके से करें। विशिष्ट एक्सेप्शन को रेस्क्यू करें।
begin
do_something()
rescue Errno::ETIMEDOUT => e
// यह केवल Errno::ETIMEDOUT एक्सेप्शन को रेस्क्यू करेगा
end
आप एक ही रेस्क्यू ब्लॉक में कई प्रकार के एक्सेप्शन को रेस्क्यू भी कर सकते हैं, इसलिए कोई बहाना नहीं। :)
begin
do_something()
rescue Errno::ETIMEDOUT, Errno::ECONNREFUSED => e
end