ตัวโหลดโค้ดใน Ruby - ทำความเข้าใจ Zeitwerk
ด้วย Zeitwerk คุณสามารถปรับปรุงการเขียนโปรแกรมของคุณโดยรู้ว่าคลาสและโมดูลพร้อมใช้งานได้ทุกที่
โค้ดโหลดเดอร์คืออะไร
ตัวโหลดโค้ดช่วยให้นักพัฒนากำหนด classes
และ modules
ข้ามไฟล์และโฟลเดอร์ต่าง ๆ และใช้ทั่วทั้ง codebase โดยไม่ต้องระบุอย่างชัดเจน Rails เป็นตัวอย่างที่ดีของซอฟต์แวร์ที่ใช้ตัวโหลดโค้ด การเขียนโปรแกรมใน Rails ไม่ต้องการ require
. อย่างชัดเจน เรียกให้โหลดโมเดลก่อนใช้ในคอนโทรลเลอร์ อันที่จริงใน Rails 6 ทุกอย่างใน app
ไดเร็กทอรีโหลดอัตโนมัติในการบูตแอป โดยมีข้อยกเว้นบางประการ
แม้ว่าการโหลดโค้ดจะเป็นเรื่องง่ายๆ ในการโทรหา require
มันไม่ง่ายอย่างนั้น การโหลดโค้ดสามารถแบ่งออกเป็นสามส่วนเพิ่มเติมได้ดังนี้
- โหลดอัตโนมัติ: ซึ่งหมายความว่าโค้ดจะโหลดได้ทันทีตามต้องการ ตัวอย่างเช่น ใน Rails ให้เรียกใช้
rails s
ไม่โหลดโมเดล คอนโทรลเลอร์ ฯลฯ ทั้งหมด แต่ในการโจมตีครั้งแรกของโมเดลUser
มันรันกลไกการโหลดอัตโนมัติเพื่อค้นหาและใช้โมเดล นี่คือการโหลดอัตโนมัติในการดำเนินการ มีข้อดีบางประการสำหรับสภาพแวดล้อมการพัฒนาของเรา เนื่องจากเรามีแอปที่เร็วกว่าและrails console
เวลาเริ่มต้นRails.config.autoload_path
ควบคุมเส้นทางที่จะโหลดอัตโนมัติ - กระตือรือร้นในการโหลด: ซึ่งหมายความว่ามีการโหลดโค้ดลงในหน่วยความจำเมื่อเริ่มต้นแอปและไม่รอให้เรียกค่าคงที่ก่อนที่จะต้องใช้ ใน Rails โค้ดมีความกระตือรือร้นในการผลิต จากคำอธิบายข้างต้น โค้ดการโหลดอัตโนมัติในการผลิตจะส่งผลให้เวลาตอบสนองช้า เนื่องจากแต่ละค่าคงที่จะต้องใช้ทันที
Rails.config.eager_load_paths
ควบคุมเส้นทางที่จะโหลดอย่างกระตือรือร้น - กำลังโหลด: ตัวโหลดโค้ดคอยเฝ้าดูการเปลี่ยนแปลงของไฟล์ใน
autoload_path
และโหลดไฟล์ใหม่เมื่อสังเกตเห็นการเปลี่ยนแปลงใดๆ ใน Rails สิ่งนี้มีประโยชน์มากในการพัฒนา เนื่องจากช่วยให้เราสามารถเรียกใช้rails s
และทำการเปลี่ยนแปลงพร้อมกันโดยไม่จำเป็นต้องรีสตาร์ทเซิร์ฟเวอร์ Rails กำลังโหลดซ้ำในการดำเนินการ
เราจะเห็นได้ง่าย ๆ ว่าแนวคิดเหล่านี้ส่วนใหญ่ได้รับการพัฒนาและใช้งานจริงใน Rails Zeitwerk เปลี่ยนแปลงสิ่งนี้! Zeitwerk ช่วยให้เราสามารถโหลดโค้ดทั้งหมดไปยังโปรเจ็กต์ Ruby ได้
Zeitwerk คืออะไร
Zeitwerk เป็นตัวโหลดโค้ดที่มีประสิทธิภาพและปลอดภัยต่อเธรดสำหรับ Ruby และสามารถใช้ได้ในโปรเจ็กต์ Ruby ใดๆ รวมถึงเว็บเฟรมเวิร์ก (Rails, Hanami, Sinatra), เครื่องมือ Cli และ gems ด้วยสิ่งนี้ คุณสามารถปรับปรุงการเขียนโปรแกรมของคุณโดยรู้ว่าคลาสและโมดูลพร้อมใช้งานทุกที่ ตามเนื้อผ้า Rails และ บางส่วน อัญมณีอื่น ๆ มีตัวโหลดโค้ดในตัวเพื่อเปิดใช้งานฟังก์ชันนี้ อย่างไรก็ตาม Zeitwerk แยกแนวคิดเหล่านี้ออกเป็นอัญมณีและอนุญาตให้ Rubyists นำแนวคิดเหล่านี้ไปใช้กับโครงการของพวกเขาได้
กำลังติดตั้ง Zeitwerk
ก่อนอื่นเราต้องติดตั้งอัญมณี:
gem install zeitwerk
# OR in your Gemfile
gem 'zeitwerk', '~> 2.4.0'
การกำหนดค่า Zeitwerk
เริ่มจากพื้นฐานกันก่อน:
require 'zeitwerk'
loader = Zeitwerk::Loader.new
...
loader.setup
โค้ดด้านบนสร้างอินสแตนซ์ตัวโหลดและเรียกใช้ setup
. หลังจากโทรไป setup
, รถตักพร้อมที่จะโหลดรหัส แต่ก่อนหน้านั้น การกำหนดค่าที่จำเป็นทั้งหมดใน loader
ควรจะครอบคลุมอยู่แล้ว ในบทความนี้ ผมจะกล่าวถึงการกำหนดค่าบางส่วนใน loader
และข้อตกลงสำหรับการจัดโครงสร้างโค้ดของคุณ
- โครงสร้างไฟล์:เพื่อให้ Zeitwerk ทำงานได้ ชื่อไฟล์และไดเร็กทอรีต้องตรงกับโมดูลและชื่อคลาสที่พวกเขากำหนด ตัวอย่างเช่น
lib/my_gem.rb -> MyGem
lib/my_gem/foo.rb -> MyGem::Foo
lib/my_gem/bar_baz.rb -> MyGem::BarBaz
lib/my_gem/woo/zoo.rb -> MyGem::Woo::Zoo
- Root Namespaces:Root Namespaces เป็นไดเร็กทอรีที่
Zeitwerk
สามารถค้นหารหัสของคุณ เมื่อmodules
และclasses
มีการอ้างอิง Zeitwerk รู้ในการค้นหาเนมสเปซรูทด้วยชื่อไฟล์ที่ตรงกัน ตัวอย่างเช่น
require 'zeitwerk'
loader = Zeitwerk::Loader.new
loader.push_dir("app/models")
loader.push_dir("app/controllers")
// matches as follows
app/models/user.rb -> User
app/controllers/admin/users_controller.rb -> Admin::UsersController
มีสองวิธีหลักในการกำหนดเนมสเปซรูทสำหรับกรณีการใช้งานที่แตกต่างกันสองกรณี วิธีเริ่มต้นแสดงอยู่ด้านล่าง:
// init.rb
require 'zeitwerk'
loader = Zeitwerk::Loader.new
loader.push_dir("#{__dir__}/bar")
...
loader.setup
// bar/foo.rb
class Foo; end
หมายถึงคลาส Foo
สามารถอ้างอิงได้โดยไม่มี Bar::Foo
. ที่ชัดเจน เนื่องจากไดเร็กทอรี bar ทำหน้าที่เป็นเนมสเปซรูท วิธีที่สองในการกำหนดเนมสเปซคือการระบุเนมสเปซอย่างชัดเจนในการเรียกไปยัง push_dir
:
// init.rb
require 'zeitwerk'
module Bar
end
loader = Zeitwerk::Loader.new
loader.push_dir("#{__dir__}/src", namespace: Bar)
loader.setup
// src/foo.rb
class Bar::Foo; end
มีบางสิ่งที่ควรทราบในรหัสนี้:
- โมดูล
Bar
ถูกกำหนดไว้แล้วก่อนที่จะถูกใช้โดยpush_dir
. หากโมดูลที่เราต้องการใช้ถูกกำหนดโดยบุคคลที่สาม ความต้องการธรรมดาจะกำหนดมันก่อนที่เราจะใช้ในการเรียกpush_dir
. - The
push_dir
ระบุเนมสเปซBar
. อย่างชัดเจน . - ไฟล์
src/foo.rb
กำหนดBar::Foo
ไม่ใช่Foo
และไม่จำเป็นต้องสร้างไดเร็กทอรี เช่นsrc/bar/foo.rb
.
-
ตัวโหลดโค้ดอิสระ:จากการออกแบบ Zeitwerk อนุญาตให้แต่ละโปรเจ็กต์หรือแอพพึ่งพาเพื่อจัดการแผนผังโปรเจ็กต์แต่ละรายการ ซึ่งหมายความว่ากลไกการโหลดโค้ดของการขึ้นต่อกันแต่ละรายการได้รับการจัดการโดยสิ่งที่ขึ้นต่อกันนั้น ตัวอย่างเช่น ใน Rails 6 Zeitwerk จะจัดการการโหลดโค้ดสำหรับแอป Rails และอนุญาตให้การขึ้นต่อกันของอัญมณีแต่ละรายการสามารถจัดการแผนผังโครงการของตนเองแยกกันได้ เป็นเงื่อนไขข้อผิดพลาดที่จะมีไฟล์ทับซ้อนกันระหว่างตัวโหลดโค้ดหลายตัว
-
โหลดอัตโนมัติ:ด้วยการตั้งค่าข้างต้น เมื่อเรียกไปที่
setup
ถูกสร้างขึ้น ทุกคลาสและโมดูลจะพร้อมใช้งานตามต้องการ -
การโหลดซ้ำ:หากต้องการเปิดใช้งานการโหลดซ้ำ
loader
ต้องมีการกำหนดค่าไว้อย่างชัดเจน ตัวอย่างเช่น
loader = Zeitwerk::Loader.new
...
loader.enable_reloading # you need to opt-in before setup
loader.setup
...
loader.reload
loader.reload
การเรียกจะโหลดโครงสร้างโครงการใหม่ทันที และการเปลี่ยนแปลงใหม่จะปรากฏทันที อย่างไรก็ตาม เรายังคงต้องการกลไกรอบข้างเพื่อตรวจจับการเปลี่ยนแปลงของระบบไฟล์และเรียก loader.reload
. เวอร์ชันง่าย ๆ แสดงอยู่ด้านล่าง:
require 'filewatcher'
loader = Zeitwerk::Loader.new
...
loader.enable_reloading
loader.setup
...
my_filewatcher = Filewatcher.new('lib/')
Thread.new(my_filewatcher) {|fw| fw.watch {|filename| loader.reload } }
การใช้ Zeitwerk ใน Rails
Zeitwerk เปิดใช้งานโดยค่าเริ่มต้นใน Rails 6.0 อย่างไรก็ตาม คุณสามารถเลือกไม่ใช้และใช้ Rails classic
ตัวโหลดโค้ด
# config/application.rb
config.load_defaults "6.0"
config.autoloader = :classic
การใช้ Zeitwerk ในอัญมณี
Zeitwerk มอบวิธีที่สะดวกสำหรับอัญมณี ตราบใดที่พวกเขาใช้โครงสร้างอัญมณีมาตรฐาน (lib/special_gem
). วิธีอำนวยความสะดวกนี้สามารถใช้ได้ดังนี้:
# lib/special_gem.rb
require 'zeitwerk'
module SpecialGem
end
loader = Zeitwerk::Loader.for_gem
loader.setup
ด้วยโครงสร้างอัญมณีมาตรฐาน for_gem
โทรเพิ่ม lib
ไดเร็กทอรีเป็นเนมสเปซรูท เปิดใช้งานทุกโค้ดใน lib
ให้ค้นหาไดเร็กทอรีโดยอัตโนมัติ
สำหรับแรงบันดาลใจเพิ่มเติม คุณสามารถตรวจสอบอัญมณีโดยใช้ Zeitwerk:
- คาราฟคา
- เครื่องบินเจ็ตส์
ข้อมูลอ้างอิง
การโหลดอัตโนมัติของ Rails — วิธีการทำงานและเมื่อไม่ทำงาน
ไซท์เวิร์ค