কম্পিউটার টিউটোরিয়াল

রুবিতে এক্সেপশন হ্যান্ডলিং 습득: একটি প্র্যাক্টিক্যাল গাইড

AppSignal-এ আমরা রুবি অ্যাপ্লিকেশনের জন্য এরর ট্র্যাকিং প্রদান করি। সেটির জন্য, আমরা অ্যাপ্লিকেশনগুলো যা সব এক্সেপশন নেওয়া ফেলে সেগুলো ক্যাপচার করি এবং ডেভেলপারদের তাদের হোনেই নোটিফাই করি।

এক্সেপশন হ্যান্ডলিং সঠিকভাবে করা কঠিন হতে পারে। এই আর্টিকেলে আমরা ব্যাখ্যা করব কীভাবে এটি কাজ করে, খারাপ হ্যান্ডলিং কী কী সমস্যা তৈরি করতে পারে এবং কীভাবে এক্সেপশনগুলো প্রপারলি রেসকিউ করতে হয়।

এক্সেপশন রেসকিউ করা

রুবিতে এক্সেপশন রেসকিউ করে আপনি আপনার অ্যাপ্লিকেশন ক্র্যাশ হওয়া থেকে বাঁচাতে পারেন যkenland কিছু ভুল হয়। একটি begin .. rescue ব্লক দিয়ে আপনি আপনার অ্যাপ্লিকেশনের জন্য একটি অ্যাল্টারনেটিভ পথ স্পেসিফাই করতে পারেন যখন একটি এরর ঘটে।

begin File.read "config.yml" rescue puts "No config file found. Using defaults." end

এটি সম্ভব কি স্পেসিফাই করা যাবে কন এক্সেপশনগুলো রেসকিউ করা উচিত। যখন একটি এক্সেপশন ক্লাস স্পেসিফাই করা হয়, তার সব সাবক্লাস এক্সেপশনও ক্যাপচার হয়।

begin File.read "config.yml" rescue SystemCallError => e puts e.class # => Errno::ENOENT puts e.class.superclass # => SystemCallError puts e.class.superclass.superclass # => StandardError end

উপরের উদাহরণে আপনি দেখতে পাচ্ছেন Errno::ENOENT এক্সেপশন ক্যাপচার হয়েছে যখন এর পেরেন্ট SystemCallError রেসকিউ করা হয়েছে।

এক্সেপশন চেইনে খুব উপরে রেসকিউ করা

এক্সেপশন চেইনে খুব উপরে রেসকিউ করা গুরুত্বপূর্ণ নয়। আপনি যখন এটা করেন, তখন সকল সাবক্লাসড এক্সেপশনও ক্যাচ হয়ে যায়, রেসকিউ ব্লকের ক্যাপচার খুব জেনেরিক হয়ে যায়।

এখানে একটি প্রোগ্রাম যা আর্গুমেন্ট হিসেবে পাস করা কনফিগ ফাইল পড়ে।

# $ ruby example.rb config.yml def config_file ARGV.firs # নোট করুন এখানে টাইপো, আমরা `ARGV.first` বোঝাচ্ছিলাম। end begin File.read config_file rescue puts "Couldn't read the config file" end

এরর মেসেজ বলে কনফিগ ফাইল পড়া যায়নি, কিন্তু আসল সমস্যাটি ছিল কোডে টাইপো।

begin File.read config_file rescue => e puts e.inspect end #

এক begin .. rescue ব্লক দ্বারা ডিফল্ট এক্সেপশন ক্লাস ক্যাচ করা হয় StandardError। আমরা যদি কোনো স্পেসিফিক ক্লাস পাস না করি, রুবি StandardError এবং সকল সাবক্লাসড এরর রেসকিউ করবে। NoMethodError এর মধ্যে একটি।

এক নির্দিষ্ট এক্সেপশন ক্লাস রেসকিউ করলে অরিলেটেড এররগুলো অকস্মাত ফেইলিয়ার স্টেট প্রম্পট করার থেকে বাঁচায়। এটিও আরও স্পেসিফিক কাস্টম এরর মেসেজ দিতে দেয় যা এন্ড ইউজারের জন্য বেশি হেল্পফুল।

config_file = "config.yml" begin File.read config_file rescue Errno::ENOENT => e puts "File or directory #{config_file} doesn't exist." rescue Errno::EACCES => e puts "Can't read from #{config_file}. No permission." end

Exception রেসকিউ করা

এক্সেপশন চেইনে উপরে রেসকিউ করা এখনও লোভনীয় হতে পারে। একটি অ্যাপ্লিকেশন যা সকল এক্সেপশন আটকে দিতে পারে সেটি রেসকিউ করলে এটা ক্র্যাশ হতে পারবে না। (১০০% আপটাইম হেরে আছি!) তবে, এতে অনেক সমস্যা হতে পারে।

Exception ক্লাস হল রুবিতে প্রধান এক্সেপশন ক্লাস। সকল অন্যান্য এক্সেপশন এই ক্লাসের সাবক্লাস; যদি Exception রেসকিউ করা হয়, সব এরর ক্যাচ হবে।

দুটি এক্সেপশন আছে যা অধিকাংশ অ্যাপ্লিকেশন রেসকিউ করতে চায় না: SignalException এবং SystemExit।

SignalException ব্যবহার হয় যখন একটি বাহ্যিক সোর্স অ্যাপ্লিকেশনকে স্টপ করতে বলে। এটি হতে পারে অপারেটিং সিস্টেম যখন শাট ডাউন করতে চায়, অথবা একটি সিস্টেম অ্যাডমিন যখন অ্যাপ্লিকেশন বন্ধ করতে চায়। উদাহরণ

SystemExit ব্যবহার হয় যখন রুবি অ্যাপ্লিকেশন থেকে exit কল করা হয়। যখন এটি রেইজ করা হয়, ডেভেলপার অ্যাপ্লিকেশন বন্ধ করতে চান। উদাহরণ

যদি আমরা Exception রেসকিউ করি এবং এই এক্সেপশনগুলো রেইজ হয় যখন অ্যাপ্লিকেশন বর্তমানে begin ... rescue ... end ব্লক চলছে, তবে এটি এক্সিট করতে পারবে না।

সাধারণ পরিস্থিতিতে Exception রেসকিউ করা একটি খারাপ আইডিয়া। Exception রেসকিউ করলে, আপনি SignalException এবং SystemExit কে ফাংশন করার থেকে বাঁচাবেন, কিন্তু LoadError, SyntaxError এবং NoMemoryError-এর মতো অন্যদেরও, কয়েকটি নাম দিতে হলে। বেটার হল আরও স্পেসিফিক এক্সেপশন রেসকিউ করা।

টেস্টে ফেইলিয়ার

যখন Exception রেসকিউ করা হয়, rescue Exception => e ব্যবহার করে, আপনার অ্যাপ্লিকেশনের বাইরের জিনিসও ভাঙতে পারে। টেস্ট স্যুট আসলে কিছু এরর হাইড করতে পারে।

Minitest এবং RSpec-এ ফেইল হওয়া অ্যাসারশন একটি এক্সেপশন রেইজ করে আপনাকে ফেইলড অ্যাসারশন সম্পর্কে জানাতে, টেস্ট ফেইল করে। সেগুলো وقتی করে, তারা তাদের নিজস্ব কাস্টম এক্সেপশন রেইজ করে, Exception থেকে সাবক্লাসড।

যদি Exception একটি টেস্টে বা অ্যাপ্লিকেশন কোডে রেসকিউ করা হয়, তবে এটি একটি অ্যাসারশন ফেইলিয়ার সাইলেন্স করতে পারে।

# RSpec example def foo(bar) bar.baz rescue Exception => e puts "This test should actually fail" # Failure/Error: bar.baz #  received unexpected message :baz with (no args) end describe "#foo" do it "hides an 'unexpected message' exception" do bar = double(to_s: "") foo(bar) end end

এক্সপেক্টিং এক্সেপশন (Expecting exceptions)

কিছু কোড এক্সেপশন রেইজ করার জন্য তৈরি থাকে। একটি টেস্ট স্যুটে এক্সেপশন সাইলেন্স করা সম্ভব যাতে সেগুলো রেইজ হলে টেস্ট ফেইল না করে।

def foo raise RuntimeError, "something went wrong" end foo rescue RuntimeError

তবে, এটি টেস্ট করে না যে এক্সেপশন রেইজ হয়েছে কিনা। যখন এক্সেপশন রেইজ হয় না, আপনার টেস্ট বোঝাতে পারবে না বিহেভিয়র এখনো সঠিক কিনা।

এক্সেপশন রেইজ হয়েছে কিনা অ্যাসার্ট করা সম্ভব, এবং না হলে কোন এক্সেপশনটি ছিল।

# expecting_exceptions_spec.rb # RSpec example def foo raise NotImplementedError, "foo method not implemented" end describe "#foo" do it "raises a RuntimeError" do expect { foo }.to raise_error(RuntimeError) end end
1) #foo raises a RuntimeError Failure/Error: expect { foo }.to raise_error(RuntimeError) expected RuntimeError, got # with backtrace: # ./expecting_exceptions_spec.rb:4:in `foo' # ./expecting_exceptions_spec.rb:9:in `block (3 levels) in ' # ./expecting_exceptions_spec.rb:9:in `block (2 levels) in ' # ./expecting_exceptions_spec.rb:9:in `block (2 levels) in ' 

Exception Re-raise করা

এক অ্যাপ্লিকেশন শুধু এক্সেপশন চেইনে Exception ক্লাস পর্যন্ত ক্যাপচার করবে যখন খুব ভালো কারণ থাকে। উদাহরণস্বরূপ, যখন একটি ব্লক অফ কোড এক্সিট করার আগে কিছু ক্লিনআপ ইনভল্ভড থাকে, যেমন টেম্পোরারি ফাইল রিমুভ করা যেগুলো রিমুভ করা জরুরি।

যদি আপনি অবশ্যই Exception রেসকিউ করতে হয়, এক রিকমেন্ডেশন হল আপনি এরর হ্যান্ডেল করার পর সেটি re-raise করুন। এইভাবে রুবি এক্সেপশন হ্যান্ডলিং প্রসেসের ভাগ্য ডিসাইড করতে পারবে।

File.open("/tmp/my_app.status", "w") { |f| "running" } begin foo rescue Exception => e Appsignal.add_error e File.open("/tmp/my_app.status", "w") { |f| "stopped" } raise e end

নিশ্চিত নয় কী রেসকিউ করবেন?

আগে বলা হয়েছে, যা রেসকিউ করবেন সেটি স্পেসিফিক হওয়া ভালো।

যখন আপনি নিশ্চিত নন কোন অপারেশন কী এক্সেপশন রেইজ করতে পারে, StandardError রেসকিউ করা একটি ভালো স্টার্টিং পয়েন্ট হতে পারে। আপনার কোড বিভিন্ন সিনারিওতে রান করুন এবং দেখুন কী এক্সেপশন রেইজ করে।

begin File.open('/tmp/appsignal.log', 'a') { |f| f.write "Starting AppSignal" } rescue => e puts e.inspect end #

প্রতিবার আপনি একটি নতুন এক্সেপশন পাবেন, সেসব এক্সেপশন বা এর রিলেভ্যান্ট পেরেন্ট ক্লাসের জন্য স্পেসিফিক রেসকিউ কেস যোগ করুন। অনেক এক্সেপশন রেসকিউ করার চেয়ে স্পেসিফিক কী রেসকিউ করবেন সেটি জানা ভালো।

begin file = '/tmp/appsignal.log' File.open(file, 'a') { |f| f.write("AppSignal started!") } rescue Errno::ENOENT => e puts "File or directory #{file} doesn't exist." rescue Errno::EACCES => e puts "Cannot write to #{file}. No permissions." end # অথবা, পেরেন্ট এরর ক্লাস ব্যবহার করে begin file = '/tmp/appsignal.log' File.open(file, 'a') rescue SystemCallError => e puts "Error while writing to file #{file}." puts e end

এটি আমাদের রুবিতে এক্সেপশন হ্যান্ডলিং প্রাইমারের সমাপ্তি। @AppSignal-এ আমাদের জানান আপনি যদি আরও জানতে চান, বা কোনো স্পেসিফিক প্রশ্ন থাকে। আপনার অ্যাপে কোথায় এবং কতবার এক্সেপশন রেইজ হচ্ছে সেটি বুঝতে চাইলে AppSignal ট্রাই দিন।


  1. C++-এ এক্সসেপশন কীভাবে কাজ করে

  2. প্রাইরে এক্সেপশন হ্যান্ডলিং মাস্টার করুন: রেলস কনসোলে অ্যাডভান্সড টেকনিক

  3. রুবিতে কাস্টোম এক্সেপশন মাস্টার করুন: ধাপে ধাপে গাইড

  4. রুবি এক্সেপশনে কনটেক্সচুয়াল ডেটা যোগ করুন: একটি প্র্যাক্টিক্যাল গাইড