আপনি যখন রুবিতে একটি rescue ক্লজ ব্যবহার করেন, আপনি স্পেসিফাই করতে পারেন আপনি কী ধরনের এক্সেপশন রেসকিউ করতে চান। আপনাকে শুধু এক্সেপশন ক্লাসের একটি লিস্ট প্রদান করতে হবে যেমন:
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 আপْرোচ নয়। এটি অনেক বেশি ইন্টেরেস্টিং হত যদি আমরা কেমনভাবে rescue ক্লজকে বলতে পারতাম শুধু আমাদের কন্ডিশন ম্যাচ করে এমন এক্সেপশনগুলো রেসকিউ করতে। এবং যেহেতু এটি রুবি, আমরা এটি করতে পারি!
rescue কীভাবে এক্সেপশন ম্যাচ করে
যখন একটি rescue ব্লকের ভিতরে একটি এক্সেপশন ঘটে, রুবি ইন্টারপ্রেটার এক্সেপশনের ক্লাসটি আপনার দেওয়া এক্সেপশন ক্লাসের লিস্টের সাথে চেক করে। যদি ম্যাচ পাওয়া যায়, এক্সেপশনটি রেসকিউ হয়।
ম্যাচিংটি এরকম দেখে:
exception_classes_to_rescue.any? do |c| c === raised_exception.class end রুবির অন্য সব অপারেটরের মত, === সিম্পলি একটি মেথড। এই কেসে এটি c-র একটি মেথড। তাহলে যদি আমরা আমাদের নিজস্ব === মেথড ডিফাইন করি তাহলে কী হবে?
নিচের উদাহরণে আমি একটি ক্লাস তৈরি করছি Anything নামক যেখানে Anything === x যেকোনো x-এর ভ্যালুর জন্য true রিটার্ন করে। যদি আমি এই ক্লাসকে rescue-এর আর্গুমেন্ট হিসেবে দিই, তবে এটি সকল এক্সেপশন রেসকিউ করবে।
class Anything def self.===(exception) true end end begin raise EOFError rescue Anything puts "It rescues ANYTHING!" end যদিও সকল এক্সেপশন রেসকিউ করার অনেক ভালো উপায় রয়েছে, এই কোডটি ইন্টেরেস্টিং কারণ এটি আমাদের দুটি জিনিস দেখাচ্ছে:
আপনি rescue ক্লজে এমন ক্লাস দিতে পারেন যা
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"। আপনি সকল "low severity" occurrence গুলো নিগলতে চান, কিন্তু "high severity" occurrence গুলো পাস করতে দিতে চান। আপনি সেটি এমনভাবে ইমপ্লিমেন্ট করতে পারেন:
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 इसे ডাইনামিক করা
এই সব খুব কুল, কিন্তু এতে অনেক বয়লারপ্লেট কোড ইনভলভড। প্রতিটি ম্যাচারের জন্য আলাদা ক্লাস ম্যানুয়ালি ডিফাইন করা একসেসিভ লাগে। خوشবর্তমান, আমরা একটু মেটাপ্রোগ্রামিং ব্যবহার করে এটি খুব ড্রাই আপ করতে পারি।
নিচের উদাহরণে, আমরা একটি মেথড ডিফাইন করছি যা আমাদের জন্য ম্যাচার ক্লাস জেনারেট করে। আপনি ম্যাচিং লজিক একটি ব্লক দিয়ে প্রদান করেন, এবং ম্যাচার জেনারেটর একটি নতুন ক্লাস তৈরি করে যা ব্লকটি তার === মেথডের ভিতরে ব্যবহার করে।
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 এক কণা লবণ
রুবিতে অনেক কুল ট্রিকের মত, আমি পুরোপুরি ডিসাইড করিনি এটা ইনস্যানিটি নাকি একটি গ্রেট আইডিয়া। হয়তো দুটোর মিশ্রণ। যদিও আমি নিশ্চিতভাবেই সাজেস্ট করব না যে আপনি এই টেকনিকটি ফার্স্ট চয়েস হিসেবে ইউজ করবেন, আমি দেখতে পাচ্ছি কীভাবে এটি উপযোগী হতে পারে উপরের সিনেরিওর মত যেখানে আপনি severity-এর ওপর ভিত্তি করে এক্সেপশন রেসকিউ করতে চান। যাক হোক, এটি আপনার টুলবেল্টে আরেকটি টুল!