Trình tải mã trong Ruby - Hiểu Zeitwerk
Với Zeitwerk, bạn có thể hợp lý hóa chương trình của mình khi biết rằng các lớp và mô-đun có sẵn ở mọi nơi.
Trình tải mã là gì?
Trình tải mã cho phép các nhà phát triển xác định các lớp classes
và modules
trên các tệp và thư mục khác nhau và sử dụng chúng trong toàn bộ cơ sở mã mà không yêu cầu chúng một cách rõ ràng. Rails là một ví dụ điển hình về một phần mềm sử dụng bộ nạp mã. Lập trình trong Rails không yêu cầu require
rõ ràng lệnh gọi để tải các mô hình trước khi sử dụng chúng trong bộ điều khiển. Trên thực tế, trong Rails 6, mọi thứ trong ứng dụng classes
thư mục được tải tự động khi khởi động ứng dụng, với một vài ngoại lệ.
Mặc dù dễ dàng nghĩ rằng việc tải mã là tất cả về việc thực hiện các cuộc gọi đến require
, nó không phải là đơn giản. Quá trình tải mã có thể được chia nhỏ thành ba phần như sau.
- Tải tự động: Điều này có nghĩa là mã được tải nhanh chóng theo yêu cầu. Ví dụ:trong Rails, chạy
rails s
không tải tất cả các mô hình, bộ điều khiển, v.v. Nhưng, trong lần truy cập đầu tiên của mô hìnhUser
, nó chạy cơ chế tải tự động để tìm và sử dụng mô hình. Đây là chế độ tải tự động đang hoạt động. Điều này có một số lợi thế cho môi trường phát triển của chúng tôi, vì chúng tôi có ứng dụng nhanh hơn vàrails console
thời gian khởi động.Rails.config.autoload_path
kiểm soát các đường dẫn được tải tự động. - Đang tải háo hức: Điều này có nghĩa là mã được tải vào bộ nhớ khi khởi động ứng dụng và không đợi các hằng số được gọi trước khi yêu cầu nó. Trong Rails, mã được tải sẵn sàng trong quá trình sản xuất. Từ giải thích ở trên, mã tự động tải trong quá trình sản xuất sẽ dẫn đến thời gian phản hồi chậm, vì mỗi hằng số sẽ được yêu cầu nhanh chóng.
Rails.config.eager_load_paths
kiểm soát các đường dẫn cần tải. - Đang tải lại: Trình tải mã liên tục theo dõi các thay đổi đối với tệp trong
autoload_path
và tải lại các tệp khi nhận thấy bất kỳ thay đổi nào. Trong Rails, điều này có thể khá hữu ích trong quá trình phát triển, vì nó cho phép chúng tôi chạyrails s
và đồng thời thực hiện các thay đổi mà không cần khởi động lại máy chủ rails. Quá trình tải lại đang hoạt động.
Chúng ta có thể dễ dàng nhận thấy rằng hầu hết các khái niệm này đã được phát triển và tồn tại trong Rails. Zeitwerk thay đổi điều này! Zeitwerk cho phép chúng tôi đưa tất cả hành động tải mã vào bất kỳ dự án Ruby nào.
Zeitwerk là gì?
Zeitwerk là một trình nạp mã hiệu quả và an toàn cho Ruby và có thể được sử dụng trong bất kỳ dự án Ruby nào, bao gồm các khung công tác Web (Rails, Hanami, Sinatra), các công cụ Cli và đá quý. Với nó, bạn có thể hợp lý hóa chương trình của mình khi biết rằng các lớp và mô-đun có sẵn ở mọi nơi. Theo truyền thống, Rails và một số các đá quý khác có bộ nạp mã tích hợp để kích hoạt chức năng này. Tuy nhiên, Zeitwerk trích xuất các khái niệm này thành một viên đá quý và cho phép các Rubyists áp dụng các khái niệm này vào các dự án của họ.
Cài đặt Zeitwerk
Điều đầu tiên, chúng ta cần cài đặt gem:
gem install zeitwerk
# OR in your Gemfile
gem 'zeitwerk', '~> 2.4.0'
Định cấu hình Zeitwerk
Vì vậy, hãy bắt đầu với những điều cơ bản:
require 'zeitwerk'
loader = Zeitwerk::Loader.new
...
loader.setup
Đoạn mã trên khởi tạo một phiên bản trình tải và gọi setup
. Sau cuộc gọi đến setup
, bộ nạp đã sẵn sàng để tải mã. Tuy nhiên, trước đó, tất cả các cấu hình cần thiết trên loader
nên được bảo hiểm rồi. Trong bài viết này, tôi sẽ trình bày một vài cấu hình trên loader
và các quy ước để cấu trúc mã của bạn.
- Cấu trúc tệp:Để Zeitwerk hoạt động, các tệp và tên thư mục cần phải khớp với các mô-đun và tên lớp mà chúng xác định. Ví dụ,
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
- Không gian tên gốc:Không gian tên gốc là các thư mục chứa
Zeitwerk
có thể tìm thấy mã của bạn. Khimodules
và các lớpclasses
được tham chiếu, Zeitwerk biết tìm kiếm không gian tên gốc với tên tệp phù hợp. Ví dụ,
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
Có hai cách chính để xác định không gian tên gốc cho hai trường hợp sử dụng khác nhau. Cách mặc định được hiển thị bên dưới:
// init.rb
require 'zeitwerk'
loader = Zeitwerk::Loader.new
loader.push_dir("#{__dir__}/bar")
...
loader.setup
// bar/foo.rb
class Foo; end
Điều này có nghĩa là lớp Foo
có thể được tham chiếu mà không có Bar::Foo
rõ ràng , vì thư mục thanh hoạt động như một không gian tên gốc. Cách thứ hai để xác định không gian tên là trình bày rõ ràng không gian tên trong lệnh gọi tới 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
Có một số điều cần lưu ý trong mã này:
- Mô-đun
Bar
đã được xác định trước khi được sử dụng bởipush_dir
. Nếu mô-đun chúng ta muốn sử dụng được xác định bởi bên thứ ba, thì một yêu cầu đơn giản sẽ xác định nó trước khi chúng ta sử dụng nó trong lệnh gọi tớipush_dir
. -
push_dir
chỉ định rõ ràng không gian tênBar
. - Tệp
src/foo.rb
Bar::Foo
được xác định , không phảiFoo
và không cần tạo thư mục, nhưsrc/bar/foo.rb
.
-
Bộ tải mã độc lập:Theo thiết kế, Zeitwerk cho phép từng dự án hoặc ứng dụng phụ thuộc quản lý cây dự án riêng lẻ của nó. Điều này có nghĩa là cơ chế tải mã của mỗi phụ thuộc được quản lý bởi phụ thuộc đó. Ví dụ:trong Rails 6, Zeitwerk xử lý việc tải mã cho ứng dụng Rails và cho phép từng phụ thuộc đá quý quản lý cây dự án của riêng nó một cách riêng biệt. Đây là một tình trạng lỗi khi có các tệp chồng chéo giữa nhiều trình tải mã.
-
Tự động tải:Với thiết lập ở trên, một lần gọi đến
setup
được thực hiện, tất cả các lớp và mô-đun sẽ có sẵn theo yêu cầu. -
Tải lại:Để cho phép tải lại,
loader
phải được định cấu hình rõ ràng cho nó. Ví dụ:
loader = Zeitwerk::Loader.new
...
loader.enable_reloading # you need to opt-in before setup
loader.setup
...
loader.reload
loader.reload
lệnh gọi tải lại cây dự án một cách nhanh chóng và bất kỳ thay đổi mới nào sẽ hiển thị ngay lập tức. Tuy nhiên, chúng tôi vẫn cần một cơ chế xung quanh để phát hiện các thay đổi đối với hệ thống tệp và gọi loader.reload
. Một phiên bản đơn giản được hiển thị bên dưới:
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 } }
Sử dụng Zeitwerk trong Rails
Zeitwerk được bật theo mặc định trong Rails 6.0. Tuy nhiên, bạn có thể chọn không tham gia và sử dụng Rails classic
trình tải mã.
# config/application.rb
config.load_defaults "6.0"
config.autoloader = :classic
Sử dụng Zeitwerk trong Đá quý
Zeitwerk cung cấp một phương pháp thuận tiện cho đá quý, miễn là chúng sử dụng cấu trúc đá quý chuẩn (lib/special_gem
). Phương pháp tiện lợi này có thể được sử dụng như sau:
# lib/special_gem.rb
require 'zeitwerk'
module SpecialGem
end
loader = Zeitwerk::Loader.for_gem
loader.setup
Với cấu trúc đá quý tiêu chuẩn, for_gem
cuộc gọi thêm lib
thư mục làm không gian tên gốc, cho phép mọi mã trong lib
thư mục sẽ được tìm thấy tự động.
Để có thêm cảm hứng, bạn có thể xem đá quý bằng Zeitwerk:
- Karafka
- Máy bay phản lực
Tài liệu tham khảo
Rails autoloading - nó hoạt động như thế nào và khi nào nó không hoạt động
Zeitwerk