রেইলস একটি বিশাল ফ্রেমওয়ার্ক, এবং এটি প্রতি বছর বড় হচ্ছে। এর ফলে কিছু উপকারী ফিচার অদৃষ্টে চলে যেতে পারে। এই সিরিজে, আমরা রেইলসে নির্দিষ্ট কাজের জন্য বিল্ট-ইন কিছু কম পরিচিত ফাংশনালিটি দেখব।
এই সিরিজের প্রথম আর্টিকেলে, আমরা দেখব Rails.env.test? কল কীভাবে আন্ডার-দ-হুড কাজ করে, ActiveSupport-এর কম পরিচিত StringInquirer ক্লাস ব্যবহার করে। এক কদাম aurrে যেয়ে, আমরা StringInquirer সোর্স কোডও ওয়াক-থ্রু করব কীভাবে এটি কাজ করে, যা (স্পয়লার অ্যালার্ট) রুবির মেটাপ্রোগ্রামিং-র একটি সহজ উদাহরণ, বিশেষ method_missing মেথডের মাধ্যমে।
Rails.env হেল্পার
আপনি সম্ভবত রেইলস এনভায়রনমেন্ট চেক করার কোড দেখেছেন:
if Rails.env.test? # হার্ড-কোডেড মান রিটার্ন... else # এক্সটারনাল API কন্টাক্ট... end
তবে, test? আসলে কী করে, এবং এই মেথডটি কোথা থেকে আসে?
কনসোলে Rails.env চেক করলে, এটি একটি স্ট্রিং처럼 কাজ করে:
=> Rails.env "development"
এখানে স্ট্রিংটি হল RAILS*ENV এনভায়রনমেন্ট ভেরিয়েবলে যা সেট করা আছে; তবে এটি শুধু একটি String নয়। কনসোলে ক্লাস পড়লে দেখা যায়:
=> Rails.env.class ActiveSupport::StringInquirer
Rails.env আসলে একটি StringInquirer।
StringInquirer
StringInquirer-এর কোড নাহলে সংক্ষিপ্ত এবং ভালোভাবে ডকুমেন্টেড। এটি কীভাবে কাজ করে, তা হয়তো স্পষ্ট নয় যদি আপনি রুবির মেটাপ্রোগ্রামিং ক্ষমতার সাথে পরিচিত না হন। আমরা এখানে যা হচ্ছে তা ওয়াক-থ্রু করব।
class StringInquirer < String ... end
প্রথমে, আমরা দেখি StringInquirer, String-র একটি সাবক্লাস। এই কারণেই Rails.env কল করলে এটি স্ট্রিং মতো কাজ করে। String-র থেকে ইনহেরিট করার কারণে, আমরা অটোমেটিক্যালি স্ট্রিং-সদৃশ সকল ফাংশনালিটি পাই, তাই আমরা এটি স্ট্রিংযে ভাবে ব্যবহার করতে পারি। Rails.env.upcase কাজ করে, ActiveRecordModel.find_by(string_column: Rails.env)-ও কাজ করে।
Rails.env StringInquirer-এর একটি হ্যান্ডি, বিল্ট-ইন উদাহরণ, কিন্তু আমরা নিজেদেরও তৈরি করতে পারি:
def type
result = "old"
result = "new" if @new
ActiveSupport::StringInquirer.new(result)
end
তারপর, রিটার্ন করা ভ্যালুতে ক্যোশ্চন-মার্ক মেথডগুলো मिलে:
=> @new = false
=> type.old? true
=> type.new? false
=> type.testvalue? false
=> @new = true
=> type.new? true
=> type.old? false
method_missing
method_missing হল এখানে আসল সিক্রেট সস। রুবিতে, যখনই আমরা একটি অবজেক্টে কোনো মেথড কল করি, রুবি সেটার সকল এনসেস্টর (বেস ক্লাস বা ইনক্লুডেড মডিউল) তে সেই মেথডটি খোঁজা শুরু করে। যদি না পায়, তবে রুবি method_missing কল করে, মেথডের নাম এবং আর্গুমেন্ট সহ পাস করে।
ডিফল্টলি, এই মেথড শুধু এক্সসেপশন রেইজ করে। আমরা সবাই সম্ভবত NoMethodError: undefined method 'test' for nil:NilClass টাইপ এরর মেসেজে পরিচিত। আমরা নিজের method_missing বাস্তবায়ন করতে পারি যা এক্সসেপশন রেইজ করে না, যা ঠিকই StringInquirer করে:
def method_missing(method_name, *arguments)
if method_name.end_with?("?")
self == method_name[0..-2]
else
super
end
end
যেকোনো মেথড নাম যা ? দিয়ে শেষ হয়, সেক্ষেত্রে আমরা self (অর্থাৎ স্ট্রিং) এর মান চেক করি মেথডের নামের সাথে (শেষের ? বাদে)। অন্য কথায়, যদি আমরা StringInquirer.new("test").long_test_method_name? কল করি, রিটার্ন মান হবে "test" == "long_test_method_name"।
যদি মেথডের নাম না হয় ক্যোশ্চন মার্ক দিয়ে শেষ, তবে আমরা অরিজিনাল method_missing-এ ফলব্যাক করি (যেটি এক্সসেপশন রেইজ করবে)।
respond_to_missing?
ফাইলে আরেকটি মেথড আছে: respond_to_missing?। আমরা বলতে পারি এটি method_missing-এর একটি কম্পানিয়ন মেথড। যদিও method_missing আমাদের ফাংশনালিটি দেয়, আমাদের রুবিকে জানাতে হয় যে আমরা এই ক্যোশ্চন-মার্ক-এন্ডিং মেথডগুলো গ্রহণ করি।
def respond_to_missing?(method_name, include_private = false)
method_name.end_with?("?") || super
end
এটি প্রযোজ্য হয় যখন আমরা অবজেক্টে respond_to? কল করি। এর অভাবে, যদি আমরা StringInquirer.new("test").respond_to?(:test?) কল করি, ফলাফল হবে false কারণ আমাদের test? নামে কোনো এক্সপ্লিসিট মেথড নেই। এটি অবশ্যই মিসলিডিং কারণ আমি আশা করি এটি true রিটার্ন করবে যদি আমি Rails.env.respond_to?(:test?) কল করি।
respond_to_missing? হলো যা আমাদের রুবিকে বলতে দেয় "হ্যাঁ, আমি সেই মেথডটি হ্যান্ডল করতে পারি"। যদি মেথডের নাম ক্যোশ্চন মার্ক দিয়ে শেষ না হয়, তবে আমরা সুপারক্লাস মেথডে ফলব্যাক করি।
ব্যবহারিক ব্যবহারের ক্ষেত্র
এখন আমরা জানি কীভাবে StringInquirer কাজ করে, আসুন নजर দিই কিছু উদাহরণে যেখানে এটি উপযোগী হতে পারে:
১. এনভায়রনমেন্ট ভেরিয়েবল
এনভায়রনমেন্ট ভেরিয়েবল দুটি মানের পূরণ করে যা সেগুলোকে StringInquirer-এর জন্য চমৎকার করে তোলে। প্রথমে, তাদের প্রায়শই সীমিত, পরিচিত সম্ভাব্য মান থাকে (যেমন একটি enum), এবং দ্বিতীয়ে, আমাদের প্রায়শই তাদের মানের উপর নির্ভরশীল কন্ডিশনাল লজিক থাকে।
মান लीজিয়ে আমাদের অ্যাপ একটি পেমেন্ট API-এর সাথে জড়িত, এবং ক্রেডেনশিয়ালগুলি এনভায়রনমেন্ট ভেরিয়েবলে সংরক্ষিত। প্রোডাকশন সিস্টেমে, এটি বাস্তব API, কিন্তু স্টেজিং বা ডেভেলপমেন্ট ভার্সনে, আমরা তাদের স্যান্ডবক্স API ব্যবহার করতে চাই:
# ENV["PAYMENT_API_MODE"] = sandbox/production
class PaymentGateway
def api_mode
# আমরা ENV.fetch ব্যবহার করছি কারণ ভ্যালু মিসিং হলে এক্সসেপশন চাই
@api_mode ||= ENV.fetch("PAYMENT_API_MODE").inquiry
end
def api_url
# প্রো-টিপ: আমরা *শুধু* প্রোডাকশন ব্যবহার করি যদি MODE == 'production', অন্যথায়
# ডিফল্ট স্যান্ডবক্স, এটি আপনাকে প্রোডাকশন মান ব্যবহার করা থেকে বাঁচায় যদি মান ভুল টাইপ হয়
if api_mode.production?
PRODUCTION_URL
else
SANDBOX_URL
end
end
end
উপরে আমরা ActiveSupport-এর String#inquiry মেথড ব্যবহার করছি, যা সুবিধাজনকভাবে একটি স্ট্রিংকে StringInquirer-এ কনভার্ট করে।
২. API রেসপন্স
উপরের পেমেন্ট API উদাহরণটি চালিয়ে যাই, API একটি রেসপন্স পাঠাবে যাতে কিছু সাকসেস/ফেইলার স্টেট থাকবে। আবার, এটি StringInquirer-এর জন্য উপযুক্ত দুটি মানের পূরণ করে: সীমিত সম্ভাব্য মান এবং শর্তসাপেক্ষ লজিক যা এই মানগুলো টেস্ট করবে।
class PaymentGateway
def create_charge
response = JSON.parse(api_call(...))
result = response["result"].inquiry
if result.success?
...
else
...
end
# result এখনও স্ট্রিং처럼 কাজ করে
Rails.logger.info("Payment result was: #{result}")
end
end
উপসংহার
StringInquirer আপনার ব্যাগে রাখার জন্য একটি আকর্ষণীয় টুল, কিন্তু ব্যক্তিগতভাবে আমি এটি খুব কম ব্যবহার করব। এর কিছু ব্যবহার আছে, लेकिन অধিকাংশ সময়, একটি অবজেক্টে এক্সপ্লিসিট মেথড আপনাকে একই ফলাফল দেবে। একটি এক্সপ্লিসিট নামকৃত মেথডের ও কিছু সুবিধা আছে; যদি ভ্যালু কখনো পরিবর্তন করতে হয়, আপনাকে শুধু এক জায়গা আপডেট করতে হবে, এবং এটি কোডবেসকে সার্চ করার জন্য সহজ করে দেয় যদি কোনো ডেভেলপার মেথডটি খুঁজতে চায়।
যদিও এটি StringInquirer-এ ফোকাস করে, এই আর্টিকেলটি রুবির মেটাপ্রোগ্রামিং ক্ষমতা সম্পর্কে method_missing ব্যবহার করে একটি মৃদু পরিচিতি দেওয়ার উদ্দেশ্যে লিখা। আমি বলব আপনার অ্যাপ্লিকেশনে method_missing ব্যবহার করা উচিত নয়। যাইহোক, এটি প্রায়শই ফ্রেমওয়ার্কে (যেমন রেইলস বা জেম্স দ্বারা প্রদত্ত ডোমেন-স্পেসিফিক ল্যাঙ্গুয়েজ) ব্যবহার করা হয়, তাই এটি জানা উপকারী হতে পারে "সসেজ কীভাবে তৈরি হয়" যখন আপনি সমস্যা সম্মুখীন হন।