เมื่อไม่กี่สัปดาห์ก่อน ฉันเขียนเกี่ยวกับวิธีที่ RubyGems จัดการเส้นทางการโหลดของ Ruby แต่ Rails ไม่ได้ใช้ RubyGems โดยตรง แต่ใช้ Bundler เพื่อจัดการอัญมณี
หากคุณไม่รู้ว่า Bundler ทำงานอย่างไร วิธีดึงอัญมณีเข้าสู่ Rails อาจดูมากเกินไป มีมนต์ขลัง การเพิ่มบรรทัดใน Gemfile
. เป็นอย่างไร รับรหัสลงในแอพของคุณหรือไม่ Bundler, Rails และ RubyGems ทำงานร่วมกันอย่างไรเพื่อให้การจัดการการพึ่งพาเป็นเรื่องง่าย
ทำไมต้อง Bundler?
ฉันคิดว่า Bundler เป็นผู้จัดการอัญมณีที่เข้มงวด นั่นคือ Bundler ช่วยคุณติดตั้งเวอร์ชันที่ถูกต้องของอัญมณีที่คุณต้องการ และบังคับให้แอปของคุณเป็น เท่านั้น ใช้เวอร์ชันเหล่านั้น
สิ่งนี้กลับกลายเป็นว่ามีประโยชน์จริงๆ เพื่อให้เข้าใจเหตุผล คุณต้องกลับไปยังโลกก่อน Bundler
ก่อน Bundler การติดตั้ง Gem เวอร์ชันที่ถูกต้องนั้นยังค่อนข้างง่ายด้วยสคริปต์การตั้งค่าบางประเภท:
gem install rails -v 4.1.0
gem install rake -v 10.3.2
...
(นั่นคือตราบใดที่การขึ้นต่อกันของ Rails 4.1 ไม่ขัดแย้งกับการขึ้นต่อกันของ Rake 10.3.2!)
แต่จะเกิดอะไรขึ้นเมื่อคุณทำงานกับแอป Rails บางแอป ซึ่งแต่ละแอปก็ขึ้นอยู่กับอัญมณีเวอร์ชันต่างๆ กัน เว้นแต่คุณจะระมัดระวังจริงๆ คุณจะพบข้อผิดพลาดในการเปิดใช้งานอัญมณีที่น่ากลัว:
Gem::Exception: can't activate hpricot (= 0.6.161, runtime),
already activated hpricot-0.8.3
ฮึ. ข้อความนั้นยังคงทำให้ฉันฝันร้าย โดยปกติหมายความว่าคุณอยู่ในวันติดตั้งและถอนการติดตั้งอัญมณี คุณจึงสามารถเพียงได้ รุ่นที่ถูกต้องบนเครื่องนั้น และสิ่งที่ต้องทำก็คือ gem install rake
. โดยไม่ได้ตั้งใจ เพื่อทำให้การวางแผนอย่างรอบคอบของคุณยุ่งเหยิงไปหมด
rvm gemsets ช่วยแก้ปัญหานี้ได้ชั่วขณะหนึ่ง แต่พวกเขาต้องใช้เวลาพอสมควรในการตั้งค่า และหากคุณติดตั้ง Gemset ผิดโดยไม่ได้ตั้งใจ คุณจะกลับสู่ปัญหาเดิม ด้วย Bundler คุณแทบจะไม่ต้องนึกถึงการขึ้นต่อกันของคุณเลย แอปของคุณมักจะใช้งานได้ และ Bundler ใช้การตั้งค่าน้อยกว่า gemsets มาก
ดังนั้น Bundler จึงทำสองสิ่งที่สำคัญสำหรับคุณ มันติดตั้งอัญมณีทั้งหมดที่คุณต้องการและล็อค RubyGems ลง ดังนั้นอัญมณีเหล่านั้นจึงเป็นเท่านั้น สิ่งที่คุณต้องการภายในแอป Rails นั้น
Rails ใช้ Bundler อย่างไร
แก่นแท้ของมัน Bundler จะติดตั้งและแยกอัญมณีของคุณ แต่นั่นไม่ใช่ทั้งหมดที่ทำ รหัสจากอัญมณีใน Gemfile
. ของคุณเป็นอย่างไร ทำให้เป็นแอป Rails ของคุณหรือไม่
หากคุณดูที่ 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'
คุณจะเห็นว่ามันโหลด Rails โดยต้องการ ../config/boot
. มาดูไฟล์กัน:
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
require 'bundler/setup' # Set up gems listed in the Gemfile.
นี่มัน Bundler! (นอกจากนี้ ฉันเพิ่งเรียนรู้ว่าคุณสามารถเลือก Gemfile
. อื่นได้ เพื่อใช้โดยการตั้งค่าตัวแปรสภาพแวดล้อม BUNDLE_GEMFILE
. เจ๋งไปเลย)
bundler/setup
ทำบางสิ่ง:
- ลบเส้นทางทั้งหมดไปยังอัญมณีจาก
$LOAD_PATH
(ซึ่งย้อนกลับการทำงานของเส้นทางการโหลดที่ RubyGems ทำ) - จากนั้นจะเพิ่มเส้นทางการโหลดของ แค่อัญมณีใน
Gemfile.lock
ของคุณ กลับไปที่$LOAD_PATH
.
ตอนนี้ อัญมณีเดียวที่คุณสามารถ require
ไฟล์จากคือไฟล์ใน Gemfile
. ของคุณ .
ดังนั้นอัญมณีทั้งหมดที่คุณต้องการจะอยู่บนเส้นทางการโหลดของคุณ แต่เมื่อคุณใช้ RubyGems ด้วยตัวเอง คุณยังต้อง require
ไฟล์ที่คุณต้องการ ทำไมคุณไม่จำเป็นต้องใช้อัญมณีของคุณเมื่อคุณใช้ Rails กับ Bundler
ดูอย่างรวดเร็วที่ config/application.rb
ซึ่งทำงานหลังจากบูท Rails:
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
Bundler อีกแล้ว! 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]
เมื่อคุณใช้งาน Rails ในโหมดการพัฒนา [:default, :production]
ในโหมดการผลิต เป็นต้น
ดังนั้น Bundler จะดูใน Gemfile
. ของคุณ สำหรับอัญมณีของแต่ละกลุ่มและเรียก require
ในแต่ละอัญมณีที่พบ ถ้าคุณมีอัญมณี nokogiri
จะเรียก require "nokogiri"
เพื่อคุณ และนั่นเป็นสาเหตุที่อัญมณีของคุณมักจะใช้งานได้ใน Rails โดยไม่ต้องมีโค้ดเพิ่มเติมในส่วนของคุณ
รู้จักเครื่องมือของคุณ
หากคุณเข้าใจเครื่องมือที่คุณใช้เป็นอย่างดี การทำงานกับพวกเขาก็จะง่ายขึ้น ดังนั้น หากคุณพบว่าตัวเองกำลังใช้บางอย่างอยู่ตลอดเวลา ก็ควรสละเวลาสักสองสามนาทีเพื่อเจาะลึกลงไปอีกเล็กน้อย
หากคุณกำลังทำงานใน Ruby and Rails คุณจะใช้อัญมณีทุกวัน ใช้เวลาในการเรียนรู้ให้ดี!