নববর্ষ শুভেচ্ছা, এবং রুবি ম্যাজিকে ফিরে আসার জন্য ধন্যবাদ! এই শীতকালীন épisোডে, আমরা বাইন্ডিং এবং স্কোপে ডুব করব। তাই আপনার স্কি পরুন এবং আমাদের সাথে গভীর বনে চলে যান।
গতবার, আমরা রুবিতে ক্লোজার দেখেছিলাম ব্লক, প্রক এবং ল্যাম্বডা কমপেয়ার করে। তিনটি টাইপের পার্থক্যের পাশাপাশি, আমরা ছুঁয়ে দিয়েছিলাম কী একটি ক্লোজার ডিফাইন করে।
একটি ক্লোজার হলো একটি ফার্স্ট-ক্লাস ফাংশন একটি এনভায়রনমেন্টের সাথে। এনভায়রনমেন্টটি হলো ভেরিয়েবলগুলোর একটি ম্যাপিং যেগুলো এক্সিস্ট করতো যখন ক্লোজারটি তৈরি হয়েছিল। ক্লোজারটি তাদের এক্সেস রিটেইন করে, এমনকি যদি সেগুলো অন্য স্কোপে ডিফাইন করা থাকে।
আমরা রুবির ফার্স্ট-ক্লাস ফাংশন একুিভ্যালেন্ট এক্সপ্লোর করেছি, কিন্তু আমরা সুবিধাজন্যভাবে এনভায়রনমেন্ট-এর ওপর স্কিপ করলাম। এই épisোডে, আমরা দেখব কিভাবে সেই এনভায়রনমেন্ট ক্লোজার, ক্লাস এবং ক্লাস ইনস্ট্যান্সের জন্য কাজ করে রুবি কীভাবে লেক্সিক্যাল স্কোপ হ্যান্ডেল করে তার বাইন্ডিংস-এর মাধ্যমে এক্সামিন করে।
লেক্সিক্যাল স্কোপ
প্রোগ্রামিং-এ, স্কোপ বলতে একটি নির্দিষ্ট কোড পার্টে উপলব্ধ বাইন্ডিংস-কে বোঝায়। একটি বাইন্ডিং, বা নেম বাইন্ডিং, একটি নামকে একটি মেমরি রেফারেন্সের সাথে বাঁধে, যেমন একটি ভেরিয়েবলের নামকে তার ভ্যালুর সাথে। স্কোপ ডিফাইন করে self কী বোঝায়, কোন মেথডগুলো কল করা যাবে, এবং কোন ভেরিয়েবলগুলো উপলব্ধ।
রুবি, অধিকাংশ আধুনিক প্রোগ্রামিং ল্যাঙ্গুয়েজের মত, স্ট্যাটিক স্কোপ ব্যবহার করে, যাকে প্রায়শই লেক্সিক্যাল স্কোপ বলা হয় (ডাইনামিক স্কোপের বিপরীতে)। বর্তমান স্কোপ কোডের স্ট্রাকচার বেসড এবং নির্ধারণ করে নির্দিষ্ট কোড পার্টে কোন ভেরিয়েবলগুলো উপলব্ধ। এর মানে স্কোপ পরিবর্তন হয় যখন কোড মেথড, ব্লক এবং ক্লাসের মধ্যে জাম্প করে — কারণ उन सबের আলাদা লোকাল ভেরিয়েবল থাকতে পারে।
def bar foo = 1 foo end bar # => 1
এই মেথডে, আমরা একটি লোকাল ভেরিয়েবল তৈরি করি একটি মেথডের ভিতরে এবং কনসোলে প্রিন্ট করি। ভেরিয়েবলটি ইন স্কোপ মেথডের ভিতরে, কারণ সেটা সেখানে তৈরি হয়েছে।
foo = 1 def bar foo end bar # => NameError (undefined local variable or method `foo' for main:Object)
এই উদাহরণে, আমরা ভেরিয়েবলটি মেথডের বাইরে তৈরি করি। যখন আমরা মেথডের ভিতরে ভেরিয়েবলটি কল করি, আমরা একটি এরর পাই, কারণ ভেরিয়েবলটি আউট অফ স্কোপ। লোকাল ভেরিয়েবল টাইটলি স্কোপড, অর্থাৎ একটি মেথড আউটসাইড ভেরিয়েবল অ্যাক্সেস করতে পারে না যদি সেটি আর্গুমেন্ট হিসেবে পাস করা না হয়।
@foo = 1 def bar @foo end bar # => 1
যখন লোকাল ভেরিয়েবল লোকালি উপলব্ধ, ইনস্ট্যান্স ভেরিয়েবল একটি ক্লাস ইনস্ট্যান্সের সকল মেথডে উপলব্ধ।
ইনহেরিটেড স্কোপ এবং Proc বাইন্ডিংস
যে উদাহরণগুলোতে আমরা দেখেছি, স্কোপ কোডের লোকেশন বেসড। একটি মেথডের বাইরে ডিফাইন করা লোকাল ভেরিয়েবল মেথডের ভিতরে ইন স্কোপ নন কিন্তু ইনস্ট্যান্স ভেরিয়েবল বানিয়ে উপলব্ধ করা যেতে পারে। মেথডগুলো লোকাল ভেরিয়েবল অ্যাক্সেস করতে পারে না তাদের বাইরে ডিফাইন করা কারণ মেথডগুলোর নিজস্ব স্কোপ থাকে, তাদের নিজস্ব বাইন্ডিংসহ।
প্রকস (ব্লক এবং ল্যাম্বডা এক্সটেনশনসহ) আলাদা। যখনই একটি প্রক ইনস্ট্যানশিয়েট করা হয়, একটি বাইন্ডিং তৈরি হয় যা ইনহেরিট করে লোকাল ভেরিয়েবলগুলোর রেফারেন্স কনটেক্স্ট থেকে যেখানে ব্লকটি তৈরি হয়েছিল।
foo = 1 Proc.new { foo }.call # => 1
এই উদাহরণে, আমরা foo নামক একটি ভেরিয়েবল সেট করি 1-এ। ইন্টারন্যালি, দ্বিতীয় লাইনে তৈরি Proc অবজেক্ট একটি নতুন বাইন্ডিং তৈরি করে। প্রকটি কল করার সময়, আমরা ভেরিয়েবলের ভ্যালু জিজ্ঞেস করতে পারি।
যেহেতু বাইন্ডিং তৈরি হয় যখন প্রক ইনিশিয়ালাইজড হয়, আমরা প্রকটি ভেরিয়েবল ডিফাইন করার আগে তৈরি করতে পারি না, এমনকি যদি ব্লকটি কল না হয় ভেরিয়েবল ডিফাইন হওয়ার পর।
proc = Proc.new { foo } foo = 1 proc.call # => NameError (undefined local variable or method `foo' for main:Object)
প্রকটি কল করলে NameError প্রোডিউস করবে কারণ ভেরিয়েবলটি প্রকের বাইন্ডিংসে ডিফাইন করা নেই। সুতরাং, প্রকে অ্যাক্সেস করা যেকোনো ভেরিয়েবল প্রক তৈরি হওয়ার আগে ডিফাইন করা উচিত বা আর্গুমেন্ট হিসেবে পাস করা উচিত।
foo = 1 proc = Proc.new { foo } foo = 2 proc.call # => 2
তবে, আমরা ভেরিয়েবলটি পরিবর্তন করতে পারি মেইন কনটেক্স্টে ডিফাইন হওয়ার পর যেহেতু প্রকের বাইন্ডিং এটিকে রেফারেন্স দিয়ে ধরে রাখে কপি করার বদলে।
foo = 1 Proc.new { foo = 2 }.call foo #=> 2
এই উদাহরণে, আমরা দেখতে পাচ্ছি যে foo ভেরিয়েবলটি একই অবজেক্টকে পয়েন্ট করে প্রকের ভিতরেও এবং বাইরেও। আমরা এটিকে প্রকের ভিতরে আপডেট করতে পারি যাতে বাহিরের ভেরিয়েবলটিও আপডেট হয়।
বাইন্ডিংস
বর্তমান স্কোপ ট্র্যাক রাখতে, রুবি বাইন্ডিংস ব্যবহার করে, যেগুলো প্রতিটি কোড পজিশনে এক্সিকিউশন কনটেক্সট এনক্যাপসুলেট করে। binding মেথড একটি Binding অবজেক্ট রিটার্ন করে যেটি বর্তমান পজিশনে বাইন্ডিংস ডেসক্রাইব করে।
foo = 1 binding.local_variables # => [:foo]
বাইন্ডিং অবজেক্টে একটি মেথড থাকে #local_variables নামে যেটি বর্তমান স্কোপে উপলব্ধ সকল লোকাল ভেরিয়েবলের নাম রিটার্ন করে।
foo = 1 binding.eval("foo") # => 1
বাইন্ডিং-এ কোড ইভ্যালুয়েট করা যেতে পারে #eval মেথড ব্যবহার করে। উপরের উদাহরণটি খুব ইউজফুল নন, কারণ সরাসরি foo কল করলে একই রেজাল্ট হতো। যাইহোক, যেহেতু একটি বাইন্ডিং একটি অবজেক্ট যা পাস করা যাবে, এটি কিছু আরও দিলচস্প জিনিসের জন্য ব্যবহার করা যেতে পারে। আসুন একটি উদাহরণ দেখি।
একটি রিয়েল-লাইফ উদাহরণ
এখন যেহেতু আমরা বাইন্ডিংস গ্যারেজের সেফ্টিতে শিখেছি, সেগুলো বर्फের ওপর নিয়ে যাই এবং বরফে খেলি। রুবির ইন্টারন্যাল বাইন্ডিংস ইউজের পাশাপাশি, কিছু পরিস্থিতি আছে যেখানে বাইন্ডিং অবজেক্ট এক্সপ্লিসিটলি ব্যবহার করা হয়। একটি ভালো উদাহরণ হলো ERB — রুবির টেমপ্লেটিং সিস্টেম।
require 'erb' x = 1 def y 2 end template = ERB.new("x is <%= x %>, y() returns <%= y %>, self is `<%= self %>`") template.result(binding) # => "x is 1, y() returns 2, self is `main`"
এই উদাহরণে, আমরা x নামক একটি ভেরিয়েবল, y নামে একটি মেথড, এবং একটি ERB টেমপ্লেট তৈরি করি যেটি উভয়কে রেফার করে। তারপর আমরা কারেন্ট বাইন্ডিং ERB#result-এ পাস করি, যেটি ERB ট্যাগগুলো ইভ্যালুয়েট করে এবং একটি স্ট্রিং রিটার্ন করে ভেরিয়েবলগুলো ফিল ইন করে।
হুডের নিচে, ERB Binding#eval ব্যবহার করে প্রতিটি ERB ট্যাগের কন্টেন্ট ইভ্যালুয়েট করে পাস করা বাইন্ডিং-এর স্কোপে। উপরের উদাহরণের জন্য একটি সিম্পলিফাইড ইমপ্লিমেন্টেশন এমন দেখতে পারে:
class DiyErb def initialize(template) @template = template end def result(binding) @template.gsub(/<%=(.+?)%>/) do binding.eval($1) end end end x = 1 def y 2 end template = DiyErb.new("x is <%= x %>, y() returns <%= y %>, self is `<%= self %>`") template.result(binding) # => "x is 1, y() returns 2, self is `main`"
DiyErb ক্লাস ইনিশিয়ালাইজেশনে একটি টেমপ্লেট স্ট্রিং নেয়। এর #result মেথড সকল ERB ট্যাগ খুঁজে বের করে এবং সেগুলো রিপ্লেস করে তাদের কন্টেন্ট ইভ্যালুয়েট করার রেজাল্ট দিয়ে। এটি করার জন্য, এটি Binding#eval কল করে পাস করা বাইন্ডিং-এ, ERB ট্যাগের কন্টেন্টসহ।
কারেন্ট বাইন্ডিং পাস করে #result মেথড কল করার সময়, eval কলগুলো মেথডের বাইরে ডিফাইন করা ভেরিয়েবল অ্যাক্সেস করতে পারে, এবং এমনকি ক্লাসের বাইরেও, সেগুলো এক্সপ্লিসিটলি পাস করা ছাড়া।
আমরা কি আপনাকে বনে হারিয়ে দিয়েছি?
আশা করি আপনি আমাদের স্কি ট্রিপ বনে উপভোগ করেছেন। আমরা স্কোপ এবং ক্লোজারের গভীরে গেলাম, সেগুলো গ্লস করা পড়ে। আশা করি আমরা আপনাকে বনে হারিয়ে দিয়েনি। অনুগ্রহ করে জানান আপনি বাইন্ডিংস সম্পর্কে আরও জানতে চান, বা অন্য কোনো রুবি টপিকে ডুব করতে চান।
আমাদের ফলো করার জন্য ধন্যবাদ এবং অনুগ্রহ করে আপনার বাইন্ডিংস থেকে বরফ ঝাড়িয়ে দিন তাদের জন্য যারা পরবর্তী ডেভেলপার। আপনি যদি এই ম্যাজিক্যাল ট্রিপ পছন্দ করেন, Ruby Magic সাবস্ক্রাইব করতে পারেন ই-মেইল পেতে যখন আমরা প্রায় মাসে একবার নতুন আর্টিকেল পাবলিশ করি।