কয়েক সপ্তাহ আগে, আমি রুবিজেমস কীভাবে রুবির লোড পাথ ম্যানেজ করে সেটা নিয়ে লিখেছিলাম। কিন্তু রেইলস সরাসরি রুবিজেমস ব্যবহার করে না - এটা বান্ডলার ব্যবহার করে জেম ম্যানেজ করার জন্য।
আপনি যদি না জানেন বান্ডলার কীভাবে কাজ করে, তবে Gemfile-এ একটি লাইন যোগ করলে কোড আপনার অ্যাপ্লিকেশনে কীভাবে আসে সেটা একটু অতিরিক্ত ম্যাজিকাল লাগতে পারে। Gemfile-এ লাইন যোগ করলে কোড অ্যাপ্লিকেশনে কীভাবে আসে? বান্ডলার, রেইলস, এবং রুবিজেমস কীভাবে একসাথে কাজ করে ডিপেন্ডেন্সি হ্যান্ডলিং সহজ করে?
কেন বান্ডলার?
আমি বান্ডলারকে একটি স্ট্রিক্ট জেম ম্যানেজার হিসেবে ভাবি। অর্থাৎ, বান্ডলার আপনাকে আপনার প্রয়োজনীয় জেমগুলোর সঠিক ভার্সন ইনস্টল করতে সাহায্য করে, এবং আপনার অ্যাপ্লিকেশনে শুধু সেই ভার্সনগুলো ব্যবহার করতে বাধ্য করে।
এটা খুব হেল্পফুল সাবিত হয়। কেন বুঝতে হলে, আপনাকে বান্ডলার-পূর্বের যুগে ফিরে যেতে হবে।
বান্ডলার-পূর্বে, কিছু সেট আপ স্ক্রিপ্ট দিয়ে আপনার জেমগুলোর সঠিক ভার্সন ইনস্টল করা এখনো সহজ ছিল:
gem install rails -v 4.1.0 gem install rake -v 10.3.2 ...
(তবে, যতক্ষণ না রেইলস ৪.১-এর ডিপেন্ডেন্সি রেক ১০.৩.২-এর ডিপেন্ডেন্সির সাথে কনফ্লিক্ট করে!)
তবে কী হয় যখন আপনি কয়েকটি আলাদা রেইলস অ্যাপ্লিকেশনে কাজ করছেন, যারা জেমের ভিন্ন ভিন্ন ভার্সনে নির্ভরশীল? আপনি যদি খুব সতর্ক না হন, তবে সেটা সেরে যাবে সেরেটো জেম এক্টিভেশন এরর দিয়ে:
Gem::Exception: can't activate hpricot (= 0.6.161, runtime), already activated hpricot-0.8.3
উফ। সেই মেসেজটি এখনও আমাকে নাইটমেער দেয়। সেটা সাধারণত মানে আপনি একদিন জেম ইনস্টল এবং আনইনস্টল করতে থাকবেন, যাতে ঠিক ভার্সনগুলো সেই মেশিনে পাওয়া যায়। এবং একটা অকস্মাত gem install rake আপনার সকল সতর্ক প্ল্যানিংকে পুরোপুরি গুলিয়ে ফেলতে পারে।
rvm gemsets কিছু সময় এই সমস্যাতে সাহায্য করেছিল। কিন্তু সেগুলো সেট আপে কিছু সময় লাগত, এবং আপনি যদি ভুলে ভুলে ভুল জেমসেটে ইনস্টল করতেন, তবে আপনি একই সমস্যাতে ফিরে যেতেন। বান্ডলারের সাথে, আপনাকে খুব কম আপনার ডিপেন্ডেন্সি নিয়ে চিন্তা করতে হয়। আপনার অ্যাপ্লিকেশনগুলো সাধারণত শুধু কাজ করে। এবং বান্ডলার জেমসেটের চেয়ে অনেক কম সেট আপ নেয়।
তাই, বান্ডলার আপনার জন্য দুটি গুরুত্বপূর্ণ জিনিস করে। এটা আপনার প্রয়োজনীয় সব জেম ইনস্টল করে, এবং রুবিজেমসকে লক করে দেয়, যাতে সেই জেমগুলোই শুধু ঐ রেইলস অ্যাপ্লিকেশনের ভিতরে রিকোয়ার করা যায়।
রেইলস কীভাবে বান্ডলার ব্যবহার করে?
বান্ডলার কোরে জেম ইনস্টল ও আইসোলেট করে। কিন্তু এটাই সব নয়। আপনার Gemfile-এ থাকা জেমের কোড আপনার রেইলস অ্যাপ্লিকেশনে কীভাবে আসে?
আপনি bin/rails-এ তাকালে:
#!/usr/bin/env ruby begin load File.expand_path("../spring", __FILE__) rescue LoadError end APP_PATH = File.expand_path('../../config/application', __FILE__) require_relative '../config/boot' require 'rails/commands'
আপনি দেখবেন এটি ../config/boot রিকোয়ার করলে রেইলস লোড করে। সেটা দেখি:
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) require 'bundler/setup' # Set up gems listed in the Gemfile.
ওহে, এটা বান্ডলার! (এছাড়া, আমি এখন শিখলাম আপনি BUNDLE_GEMFILE এনভায়রনমেন্ট ভেরিয়েবল সেট করে অন্য Gemfile বেছে নিতে পারেন। এটা খুব কুল।)
bundler/setup কয়েকটা জিনিস করে:
- এটা
$LOAD_PATH- থেকে সকল জেম পাথ সরিয়ে দেয় (যে রুবিজেমসের লোড পাথ ওয়ার্ক উল্টা করে)। - তারপর, শুধু আপনার
Gemfile.lock-এ থাকা জেমগুলোর লোড পাথ গুলো$LOAD_PATH-এ ফেরত যোগ করে।
এখন, আপনি শুধু আপনার Gemfile-এ থাকা জেমগুলো থেকে ফাইল require করতে পারবেন।
তাহলে আপনার প্রয়োজনীয় সব জেম লোড পাথেই আছে। কিন্তু রুবিজেমস নিজে ব্যবহার করলে, আপনাকে এখনও আপনার প্রয়োজনীয় ফাইলগুলো require করতে হয়। রেইলস বান্ডলারের সাথে ব্যবহার করলে আপনাকে আপনার জেমগুলো রিকোয়ার কেন করতে হয় না?
config/application.rb-এ দ্রুত এক নজর দিন, যা রেইলস বুট হওয়ার পর রান হয়:
# Require the gems listed in Gemfile, including any gems # you've limited to :test, :development, or :production. Bundler.require(*Rails.groups)
আবার বান্ডলার! Bundler.require আপনার পাস করা সব গ্রুপের সব জেম রিকোয়ার করে। (গ্রুপ বলতে আমি আপনার Gemfile-এ স্পেসিফাই করা গ্রুপগুলো বুঝাচ্ছি।)
তবে Rails.groups-এ কোন গ্রুপগুলো থাকে?
# Returns all rails groups for loading based on: # # * The Rails environment; # * The environment variable RAILS_GROUPS; # * The optional envs given as argument and the hash with group dependencies; # # groups assets: [:development, :test] # # # Returns # # => [:default, :development, :assets] for Rails.env == "development" # # => [:default, :production] for Rails.env == "production" def groups(*groups) hash = groups.extract_options! env = Rails.env groups.unshift(:default, env) groups.concat ENV["RAILS_GROUPS"].to_s.split(",") groups.concat hash.map { |k, v| k if v.map(&:to_s).include?(env) } groups.compact! groups.uniq! groups end
ভালো, এটা ব্যাখ্যা করলো। Rails.groups ডেভেলপমেন্ট মোডে [:default, :development] হবে, প্রোডাকশন মোডে [:default, :production], এবং সেভাবে।
তাই, বান্ডলার আপনার Gemfile-এ সেই গ্রুপগুলোর যেসব জেম আছে সেগুলো খুঁজে বের করবে, এবং যেসব জেম পাবে সেগুলোর ওপর require কল করবে। আপনার কাছে nokogiri জেম থাকলে, সেটা আপনার জন্য require "nokogiri" কল করবে। এবং এটাই কারণ আপনার জেমগুলো রেইলসে সাধারণত কোনো এক্সট্রা কোড ছাড়া কাজ করে।
আপনার টুলগুলো জানুন
আপনি যদি আপনার ব্যবহার করা টুলগুলো ভালো বুঝেন, তবে সেগুলোর সাথে কাজ করা সহজ হবে। তাই আপনি যদি কোনো জিনিস সবসময় ব্যবহার করেন, তবে সেটা কুয়etto একটু গভীরে যেতে লायक।
আপনি যদি রুবি এবং রেইলসে কাজ করেন, আপনি প্রতিদিন জেম ব্যবহার করবেন। সেগুলো ভালোভাবে শিখতে সময় দিন!