दूसरे दिन मैं शुरुआती लोगों के लिए लिखे गए रूबी अपवादों का परिचय खोज रहा था - वे लोग जो मूल रूबी सिंटैक्स को जानते हैं लेकिन वास्तव में यह सुनिश्चित नहीं हैं कि अपवाद क्या है या यह क्यों उपयोगी है। मुझे एक नहीं मिला, इसलिए मैंने खुद इस पर जाने का फैसला किया। मुझे उम्मीद है कि आप इसे उपयोगी पाएँ। यदि कोई ऐसा बिंदु है जो भ्रमित करने वाला है, तो बेझिझक मुझे @StarrHorne पर ट्वीट करें। :)
अपवाद क्या है?
अप्रत्याशित घटनाओं से निपटने के लिए रूबी के तरीके अपवाद हैं।
यदि आपने कभी अपने कोड में कोई टाइपो बनाया है, जिससे आपका प्रोग्राम SyntaxError
जैसे संदेश के साथ क्रैश हो जाता है या NoMethodError
, तो आपने कार्रवाई में अपवाद देखे हैं।
जब आप रूबी में अपवाद उठाते हैं, तो दुनिया रुक जाती है और आपका प्रोग्राम बंद होना शुरू हो जाता है। यदि कुछ भी प्रक्रिया को रोकता नहीं है, तो आपका प्रोग्राम अंततः एक त्रुटि संदेश के साथ बाहर निकल जाएगा।
यहाँ एक उदाहरण है। नीचे दिए गए कोड में, हम शून्य से विभाजित करने का प्रयास करते हैं। यह असंभव है, इसलिए रूबी एक अपवाद उठाती है जिसे ZeroDivisionError
. कहा जाता है . प्रोग्राम एक त्रुटि संदेश को छोड़ता है और प्रिंट करता है।
1 / 0
# Program crashes and outputs: "ZeroDivisionError: divided by 0"
क्रैशिंग प्रोग्राम हमारे उपयोगकर्ताओं को नाराज़ करते हैं। इसलिए हम आम तौर पर इस शटडाउन प्रक्रिया को रोकना चाहते हैं, और समझदारी से त्रुटि पर प्रतिक्रिया करना चाहते हैं।
इसे "बचाव," "हैंडलिंग," या "कैचिंग" एक अपवाद कहा जाता है। उन सबका मतलब एक ही है। रूबी में आप इसे इस तरह से करते हैं:
begin
# Any exceptions in here...
1/0
rescue
# ...will cause this code to run
puts "Got an exception, but I'm responding intelligently!"
do_something_intelligent()
end
# This program does not crash.
# Outputs: "Got an exception, but I'm responding intelligently!"
अपवाद अभी भी होता है, लेकिन यह प्रोग्राम को क्रैश नहीं करता है क्योंकि इसे "बचाया गया" था। रूबी बाहर निकलने के बजाय बचाव ब्लॉक में कोड चलाता है, जो एक संदेश को प्रिंट करता है।
यह अच्छा है, लेकिन इसकी एक बड़ी सीमा है। यह हमें बताए बिना "कुछ गलत हो गया" बताता है क्या गड़बड़ हो गया।
जो गलत हुआ उसके बारे में सारी जानकारी एक अपवाद वस्तु में समाहित होने वाली है।
अपवाद वस्तुएं
अपवाद वस्तुएं सामान्य रूबी वस्तुएं हैं। आपके द्वारा अभी-अभी बचाए गए अपवाद के लिए वे "क्या हुआ" के बारे में सभी डेटा रखते हैं।
अपवाद ऑब्जेक्ट प्राप्त करने के लिए, आप थोड़ा अलग बचाव सिंटैक्स का उपयोग करेंगे।
# Rescues all errors, an puts the exception object in `e`
rescue => e
# Rescues only ZeroDivisionError and puts the exception object in `e`
rescue ZeroDivisionError => e
ऊपर के दूसरे उदाहरण में, ZeroDivisionError
e
. में ऑब्जेक्ट का वर्ग है . हमने जिन सभी "प्रकारों" के अपवादों के बारे में बात की है, वे वास्तव में सिर्फ वर्ग के नाम हैं।
अपवाद ऑब्जेक्ट्स में उपयोगी डीबग डेटा भी होता है। आइए हमारे ZeroDivisionError
. के अपवाद ऑब्जेक्ट पर एक नज़र डालें ।
begin
# Any exceptions in here...
1/0
rescue ZeroDivisionError => e
puts "Exception Class: #{ e.class.name }"
puts "Exception Message: #{ e.message }"
puts "Exception Backtrace: #{ e.backtrace }"
end
# Outputs:
# Exception Class: ZeroDivisionError
# Exception Message: divided by 0
# Exception Backtrace: ...backtrace as an array...
अधिकांश रूबी अपवादों की तरह, इसमें अपने वर्ग के नाम के साथ एक संदेश और एक बैकट्रेस होता है।
अपना खुद का अपवाद उठाना
अभी तक हमने केवल अपवादों को बचाने के बारे में बात की है। आप अपने स्वयं के अपवादों को भी ट्रिगर कर सकते हैं। इस प्रक्रिया को "उठाना" कहा जाता है। आप इसे raise
. पर कॉल करके करते हैं तरीका।
जब आप अपने स्वयं के अपवादों को उठाते हैं, तो आपको यह चुनना होता है कि किस प्रकार के अपवाद का उपयोग करना है। आपको त्रुटि संदेश भी सेट करना होगा।
यहां एक उदाहरण दिया गया है:
begin
# raises an ArgumentError with the message "you messed up!"
raise ArgumentError.new("You messed up!")
rescue ArgumentError => e
puts e.message
end
# Outputs: You messed up!
जैसा कि आप देख सकते हैं, हम एक नया एरर ऑब्जेक्ट बना रहे हैं (एक ArgumentError
) एक कस्टम संदेश के साथ ("आपने गड़बड़ कर दी!") और इसे raise
. पर भेज दिया विधि।
यह रूबी है, raise
कई तरीकों से बुलाया जा सकता है:
# This is my favorite because it's so explicit
raise RuntimeError.new("You messed up!")
# ...produces the same result
raise RuntimeError, "You messed up!"
# ...produces the same result. But you can only raise
# RuntimeErrors this way
raise "You messed up!"
कस्टम अपवाद बनाना
रूबी के अंतर्निर्मित अपवाद बहुत अच्छे हैं, लेकिन वे हर संभव उपयोग के मामले को कवर नहीं करते हैं।
क्या होगा यदि आप एक उपयोगकर्ता प्रणाली का निर्माण कर रहे हैं और जब उपयोगकर्ता साइट के ऑफ-लिमिट भाग तक पहुंचने का प्रयास करता है तो अपवाद उठाना चाहते हैं? रूबी के मानक अपवादों में से कोई भी फिट नहीं है, इसलिए आपकी सबसे अच्छी शर्त एक नए प्रकार का अपवाद बनाना है।
एक कस्टम अपवाद बनाने के लिए, बस एक नया वर्ग बनाएं जो StandardError
. से विरासत में मिला हो .
class PermissionDeniedError < StandardError
end
raise PermissionDeniedError.new()
यह सिर्फ एक सामान्य रूबी वर्ग है। इसका मतलब है कि आप किसी भी अन्य वर्ग की तरह इसमें तरीके और डेटा जोड़ सकते हैं। आइए "कार्रवाई" नामक एक विशेषता जोड़ें:
class PermissionDeniedError < StandardError
attr_reader :action
def initialize(message, action)
# Call the parent's constructor to set the message
super(message)
# Store the action in an instance variable
@action = action
end
end
# Then, when the user tries to delete something they don't
# have permission to delete, you might do something like this:
raise PermissionDeniedError.new("Permission Denied", :delete)
कक्षा पदानुक्रम
हमने अभी-अभी StandardError
. को उपवर्गित करके एक कस्टम अपवाद बनाया है , जो स्वयं Exception
. को उपवर्गित करता है .
वास्तव में, यदि आप रूबी में किसी अपवाद के वर्ग पदानुक्रम को देखते हैं, तो आप पाएंगे कि यह अंततः Exception
पर वापस जाता है . यहाँ, मैं इसे आपको साबित करूँगा। ये रूबी के अधिकांश अंतर्निर्मित अपवाद हैं, जिन्हें पदानुक्रम में प्रदर्शित किया जाता है:
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
. थे , आप हर एक अपवाद को बचाएंगे, जो एक बहुत बुरा विचार होगा
सभी अपवादों का बचाव (खराब तरीका)
यदि आप चिल्लाना चाहते हैं, तो स्टैक ओवरफ्लो पर जाएं और कुछ ऐसा कोड पोस्ट करें जो इस तरह दिखता है:
// Don't do this
begin
do_something()
rescue Exception => e
...
end
उपरोक्त कोड हर अपवाद को बचाएगा। ऐसा मत करो! यह आपके प्रोग्राम को अजीब तरीके से तोड़ देगा।
ऐसा इसलिए है क्योंकि रूबी त्रुटियों के अलावा अन्य चीजों के लिए अपवादों का उपयोग करती है। यह उनका उपयोग "सिग्नल" नामक ऑपरेटिंग सिस्टम से संदेशों को संभालने के लिए भी करता है। यदि आपने कभी किसी प्रोग्राम से बाहर निकलने के लिए "ctrl-c" दबाया है, तो आपने सिग्नल का उपयोग किया है। सभी अपवादों को दबा कर आप उन संकेतों को भी दबा देते हैं।
कुछ प्रकार के अपवाद भी हैं - जैसे सिंटैक्स त्रुटियां - जो वास्तव में आपके प्रोग्राम को क्रैश कर सकती हैं। यदि आप उन्हें दबा देते हैं, तो आप कभी नहीं जान पाएंगे कि आप कब टाइपो या अन्य गलतियाँ करते हैं।
सभी त्रुटियों से बचाव (सही तरीका)
वापस जाएं और वर्ग पदानुक्रम चार्ट को देखें और आप देखेंगे कि सभी त्रुटियां जिन्हें आप बचाना चाहते हैं वे StandardError
के बच्चे हैं
इसका अर्थ है कि यदि आप "सभी त्रुटियों" से बचाव करना चाहते हैं तो आपको StandardError
. को बचाना चाहिए ।
begin
do_something()
rescue StandardError => e
# Only your app's exceptions are swallowed. Things like SyntaxErrror are left alone.
end
वास्तव में, यदि आप अपवाद वर्ग निर्दिष्ट नहीं करते हैं, तो रूबी मानती है कि आपका मतलब StandardError
है
begin
do_something()
rescue => e
# This is the same as rescuing StandardError
end
विशिष्ट त्रुटियों से बचाव (सर्वोत्तम तरीका)
अब जब आप जानते हैं कि सभी त्रुटियों को कैसे बचाया जाए, तो आपको पता होना चाहिए कि यह आमतौर पर एक बुरा विचार है, एक कोड गंध, जिसे हानिकारक माना जाता है, आदि।
वे आम तौर पर एक संकेत हैं कि आप यह पता लगाने के लिए बहुत आलसी थे कि किन विशिष्ट अपवादों को बचाया जाना चाहिए। और वे लगभग हमेशा आपको परेशान करने के लिए वापस आएंगे।
इसलिए समय निकालें और इसे सही करें। बचाव विशिष्ट अपवाद।
begin
do_something()
rescue Errno::ETIMEDOUT => e
// This will only rescue Errno::ETIMEDOUT exceptions
end
आप एक ही बचाव खंड में कई प्रकार के अपवादों को भी बचा सकते हैं, इसलिए कोई बहाना नहीं। :)पी>
begin
do_something()
rescue Errno::ETIMEDOUT, Errno::ECONNREFUSED => e
end