সभी ত্রুটি ফ্যাটাল নয়। কিছু শুধু ইঙ্গিত দেয় যে আপনাকে আবার চেষ্টা করতে হবে। خوشখবর হলো, রুবি কিছু আকর্ষণীয় মেকানিজম প্রদান করে যা "আবার চেষ্টা করা" সহজ করে তোলে—যদিও সবগুলো স্বচ্ছ বা পরিচিত নয়। এই পোস্টে আমরা সেসব মেকানিজম এবং বাস্তব জগতে সেগুলি কীভাবে কাজ করে তা দেখব।
Retry পরিচয়
এটি একটি bekend বিষয়, কিন্তু শুধু তখনই যখন আপনি জানেন এর অস্তিত্ব। ব্যক্তিগতভাবে, আমার রুবি ক্যারিয়ারে অনেক দূর যাওয়ার পর আমি "retry" কিওয়ার্ড সম্পর্কে জানতে পারলাম।
Retry রুবির এক্সসেপশন রescue সিস্টেমের অভিন্ন অংশ। এটি খুব সহজ। আপনার rescue ব্লকে "retry" ব্যবহার করলে রescue করা কোড সেকশনটি আবার চালানো হয়। একটি উদাহরণ দেখি।
begin
retries ||= 0
puts "try ##{ retries }"
raise "the roof"
rescue
retry if (retries += 1) < 3
end
# ... নিচের আউটপুট দেয়:
# try #0
# try #1
# try #2
এখানে কিছু দ্রষ্টব্য বিষয় রয়েছে:
যখন retry কল করা হয়, begin এবং rescue-এর মধ্যবর্তী সকল কোড আবার চালানো হয়। এটি সব Âu করে না "যেখানে থেকে ছেড়েছিলাম সেখান থেকে শুরু করো" বা 그와 কিছুই।
আপনি যদি রিট্রাই সীমিত করার কোনো মেকানিজম না দেন, তবে আপনি একটি ইনফিনিট লুপে পড়বেন।
Begin এবং rescue ব্লক দুটোই পেরেন্ট স্কোপের একই retries ভেরিয়েবল অ্যাক্সেস করতে পারে।
Retry-এর সমস্যা
Retry চমৎকার হলেও এর কিছু সীমাবদ্ধতা রয়েছে। প্রধানটি হলো পুরো begin ব্লকটি re-run হয়। কিন্তু কখনো কখনো এটি আদর্শ নয়।
উদাহরণস্বরূপ, কল্পনা করুন আপনি একটি জেম ব্যবহার করছেন যা আপনাকে টুইটার, ফেসবুক এবং অন্যান্য অনেক সাইটে একটি মেথড কল দিয়ে স্ট্যাটাস আপডেট পোস্ট করতে দেয়। এটি কিছু এইরকম দেখতে পারে।
SocialMedia.post_to_all("Zomg! আমি খাইলাম সবচেয়ে বড় হ্যামবার্গার")
# ...টুইটার API-তে পোস্ট করল
# ...ফেসবুক API-তে পোস্ট করল
# ...এত্যাদি
যদি একটি API রেসপন্ড না করে, তবে জেমটি SocialMedia::TimeoutError raise করে এবং অ bort করে। আমরা যদি এই এক্সসেপশন ক্যাচ করে retry করি, তবে ডুপ্লিকেট পোস্ট হবে কারণ retry শুরু থেকে শুরু করবে।
begin
SocialMedia.post_to_all("Zomg! আমি খাইলাম সবচেয়ে বড় হ্যামবার্গার")
rescue SocialMedia::TimeoutError
retry
end
# ...টুইটার API-তে পোস্ট করল
# ফেসবুক এরর
# ...টুইটার API-তে পোস্ট করল
# ফেসবুক এরর
# ...টুইটার API-তে পোস্ট করল
# এবং এভাবে চলতে থাকবে
কতটা ভালো হতো যদি আমরা জেমকে বলতে পারতাম "শুধু ফেসবুক বাদ দাও, এবং API লিস্টে পরবর্তীগুলিতে পোস্ট করতে থাকো।"
সৌভাগ্যtois, রুবি আমাদের সেটি করার অনুমতি দেয়।
দ্রষ্টব্য: অবশ্যই এই সমস্যার আসল সমাধান হলো সোশ্যাল মিডিয়া লাইব্রেরি পুনর্গঠন করা। কিন্তু এটি একমাত্র ইউজ-কেস নয় যা আমি আপনাকে দেখাতে যাচ্ছি।
কন্টিনুয়েশন উদ్ధারে
কন্টিনুয়েশন লোকদের ভয় পায়। কিন্তু সেটি শুধু তাই কারণ সেগুলি কম ব্যবহার করা হয় এবং একটু অদ্ভুত দেখায়। কিন্তু একবার বেসিক বুঝলে সেগুলি খুব সহজ।
একটি কন্টিনুয়েশন আপনার কোডে একটি "সেভ পয়েন্ট" এর মতো, ঠিক ভিডিও গেমের মতো। আপনি অন্য কাজ করতে যেতে পারেন, তারপর সেভ পয়েন্টে ফিরে আসতে পারেন এবং সব কিছু যেন আগের মতোই থাকবে।
...ঠিক এটি একটি পুর্নম argile নয়, কিন্তু কিছুটা কাজ করে। কিছু কোড দেখি:
require "continuation"
counter = 0
continuation = callcc { |c| c } # আমাদের সেভপয়েন্ট সংজ্ঞায়িত করলাম
puts(counter += 1)
continuation.call(continuation) if counter < 5 # সেভপয়েন্টে ফিরে যান
আপনি কিছু অদ্ভুত বিষয় লক্ষ্য করতে পারেন। সেগুলি যাচাই করি:
আমরা Continuation অবজেক্ট তৈরি করতে
callccমেথড ব্যবহার করি। এর জন্য কোনো ক্লিন OO সিনট্যাক্স নেই।প্রথমবার
continuationভেরিয়েবলে মান দিতে হলে, সেটিcallccএর ব্লকের রিটার্ন ভ্যালুতে সেট হয়। তাই ব্লকটি থাকতে হবে।প্রতিবার সেভপয়েন্টে ফিরলে,
continuationভেরিয়েবলে সেটিcallমেথডে পাস করা আর্গুমেন্ট অ্যাসাইন হয়। তাই আমরা অদ্ভুত দেখানোcontinuation.call(continuation)সিনট্যাক্স ব্যবহার করি।
এক্সসেপশনে কন্টিনুয়েশন যোগ
আমরা কন্টিনুয়েশন ব্যবহার করে সব এক্সসেপশনে একটি skip মেথড যোগ করব। নিচের উদাহরণে দেখানো হয়েছে কীভাবে কাজ করা উচিত। যখনই আমি একটি এক্সসেপশন রescue করি, আমাকে skip কল করতে পারতে হবে, যা এক্সসেপশন raise করার কোডটিকে যেন সেটি কখনো ঘটেনি সেভাবে আচরণ করাবে।
begin
raise "the roof"
puts "The exception was ignored"
rescue => e
e.skip
end
# ...আউটপুট: "The exception was ignored"
এটি করতে আমাকে কিছু "পাপ" করতে হবে। Exception শুধু একটি ক্লাস। অর্থাৎ আমি সেটিকে monkey-patch করে skip মেথড যোগ করতে পারি।
class Exception
attr_accessor :continuation
def skip
continuation.call
end
end
এখন আমাদের প্রতিটি এক্সসেপশনের জন্য continuation অ্যাট্রিবিউট সেট করতে হবে। اليم raise শুধু একটি মেথড, যা আমরা ওভাররাইড করতে পারি।
ব ああ, নিচের কোডটি Advi-এর চমৎকার স্লাইড ডেক "Things You Didn't know about Exceptions" থেকে প্রায় শব্দশः নেওয়া। আমি এর চেয়ে ভালো ইমপ্লিমেন্টেশন ভাবতে পারিনি:
require 'continuation'
module StoreContinuationOnRaise
def raise(*args)
callcc do |continuation|
begin
super
rescue Exception => e
e.continuation = continuation
super(e)
end
end
end
end
class Object
include StoreContinuationOnRaise
end
এখন আমি যেকোনো এক্সসেপশনের জন্য skip মেথড কল করতে পারি এবং সেটি যেন এক্সসেপশনটি কখনো ঘটেনি সেভাবে কাজ করবে।