অন্য দিন আমি নতুনদের জন্য লিখিত রুবি ব্যতিক্রমগুলির একটি ভূমিকার জন্য অনুসন্ধান করছিলাম - যারা মৌলিক রুবি সিনট্যাক্স জানেন কিন্তু সত্যিই নিশ্চিত নন যে একটি ব্যতিক্রম কি বা কেন এটি দরকারী। আমি একটি খুঁজে পাচ্ছি না, তাই আমি নিজেই এটি একটি যেতে হবে সিদ্ধান্ত নিয়েছে. আশা করি এটি আপনার কাজে লাগবে। বিভ্রান্তিকর কোনো বিষয় থাকলে, @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!"
কাস্টম ব্যতিক্রম তৈরি করা
রুবির অন্তর্নির্মিত ব্যতিক্রমগুলি দুর্দান্ত, তবে তারা প্রতিটি সম্ভাব্য ব্যবহারের ক্ষেত্রে কভার করে না।
আপনি যদি একটি ব্যবহারকারী সিস্টেম তৈরি করেন এবং ব্যবহারকারী যখন সাইটের একটি অফ-লিমিট অংশ অ্যাক্সেস করার চেষ্টা করেন তখন একটি ব্যতিক্রম উত্থাপন করতে চান? রুবির স্ট্যান্ডার্ড ব্যতিক্রমগুলির কোনওটিই উপযুক্ত নয়, তাই আপনার সেরা বাজি হল একটি নতুন ধরণের ব্যতিক্রম তৈরি করা।
একটি কাস্টম ব্যতিক্রম করতে, শুধু একটি নতুন ক্লাস তৈরি করুন যা rescue 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)
বর্গ শ্রেণিবিন্যাস
আমরা এইমাত্র rescue 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
উদ্ধার করেন , আপনি শুধুমাত্র ক্লাস rescue StandardError
দিয়ে ব্যতিক্রমগুলি উদ্ধার করেন না কিন্তু সেইসাথে তার সন্তানদের যারা. আপনি যদি চার্টটি দেখেন, আপনি দেখতে পাবেন যে এটি অনেক:ArgumentError
, IOError
, ইত্যাদি।
আপনি যদি rescue Exception
করতেন , আপনি প্রতিটি একক ব্যতিক্রম উদ্ধার করবেন, যা একটি খুব খারাপ ধারণা হবে
সকল ব্যতিক্রম উদ্ধার করা (খারাপ উপায়)
আপনি যদি চিৎকার করতে চান, স্ট্যাক ওভারফ্লোতে যান এবং কিছু কোড পোস্ট করুন যা এইরকম দেখাচ্ছে:
// Don't do this
begin
do_something()
rescue Exception => e
...
end
উপরের কোডটি প্রতিটি ব্যতিক্রম উদ্ধার করবে। এটা করবেন না! এটা অদ্ভুত উপায়ে আপনার প্রোগ্রাম বিরতি করব.
কারণ রুবি ত্রুটি ছাড়া অন্য কিছুর জন্য ব্যতিক্রম ব্যবহার করে। এটি "সিগন্যাল" নামক অপারেটিং সিস্টেম থেকে বার্তাগুলি পরিচালনা করতে তাদের ব্যবহার করে। আপনি যদি কখনও একটি প্রোগ্রাম থেকে প্রস্থান করার জন্য "ctrl-c" টিপে থাকেন তবে আপনি একটি সংকেত ব্যবহার করেছেন। সমস্ত ব্যতিক্রম দমন করে, আপনি সেই সংকেতগুলিকেও দমন করেন।
এছাড়াও কিছু ধরণের ব্যতিক্রম রয়েছে - যেমন সিনট্যাক্স ত্রুটি - যা সত্যিই আপনার প্রোগ্রামকে ক্র্যাশ করতে পারে। আপনি যদি তাদের দমন করেন, আপনি কখনই টাইপো বা অন্য ভুল করবেন তা আপনি কখনই জানতে পারবেন না।
সকল ত্রুটি উদ্ধার করা (সঠিক উপায়)
ফিরে যান এবং ক্লাস হায়ারার্কি চার্টটি দেখুন এবং আপনি দেখতে পাবেন যে আপনি যে সমস্ত ত্রুটিগুলি উদ্ধার করতে চান সেগুলি হল rescue StandardError
-এর সন্তান।
এর মানে হল যে আপনি যদি "সমস্ত ত্রুটি" উদ্ধার করতে চান তবে আপনাকে rescue StandardError
উদ্ধার করতে হবে .
begin
do_something()
rescue StandardError => e
# Only your app's exceptions are swallowed. Things like SyntaxErrror are left alone.
end
প্রকৃতপক্ষে, আপনি যদি একটি ব্যতিক্রম শ্রেণী নির্দিষ্ট না করেন, রুবি ধরে নেয় আপনি মানে rescue 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