Có sự khác biệt lớn nào giữa load
và require
trong các ứng dụng Ruby on Rails không? Hay cả hai đều có chức năng giống nhau?
Có sự khác biệt lớn nào giữa load
và require
trong các ứng dụng Ruby on Rails không? Hay cả hai đều có chức năng giống nhau?
Câu trả lời:
require
tìm kiếm thư viện trong tất cả các đường dẫn tìm kiếm đã xác định và cũng thêm .rb hoặc .so vào tên tệp bạn nhập. Nó cũng đảm bảo rằng một thư viện chỉ được đưa vào một lần. Vì vậy, nếu ứng dụng của bạn yêu cầu thư viện A và B và thư viện B cũng yêu cầu thư viện A thì A sẽ chỉ được tải một lần.
Với việc load
bạn cần thêm tên đầy đủ của thư viện và nó được tải mỗi khi bạn gọi load
- ngay cả khi nó đã có trong bộ nhớ.
require
theo dõi những gì đã được tải thông qua mảng toàn cục $LOADED_FEATURES
( $"
), mảng này sẽ load
bỏ qua.
Một sự khác biệt khác giữa Kernel#require
và Kernel#load
là Kernel#load
có đối số thứ hai tùy chọn cho phép bạn bọc mã đã tải vào một mô-đun trống ẩn danh.
Thật không may, nó không hữu ích cho lắm. Đầu tiên, rất dễ dàng để load
mã ed thoát ra khỏi mô-đun, bằng cách chỉ cần truy cập vào không gian tên toàn cục, tức là chúng vẫn có thể bắt một cái gì đó giống như vậy class ::String; def foo; end end
. Và thứ hai, load
không trả về mô-đun mà nó bao bọc mã, vì vậy về cơ bản, bạn phải xử lý nó ObjectSpace::each_object(Module)
bằng tay.
Kernel#load
có một lập luận khác
load
mã ed thoát ra khỏi mô-đun, bằng cách chỉ cần truy cập vào không gian tên toàn cục, tức là chúng vẫn có thể bắt một cái gì đó giống như vậy class ::String; def foo; end end
. Và thứ hai, load
không trả về mô-đun mà nó bao bọc mã, vì vậy về cơ bản, bạn phải xử lý nó ObjectSpace::each_object(Module)
bằng tay.
Tôi đang chạy một ứng dụng Rails và trong Gemfile, tôi có một viên ngọc tùy chỉnh cụ thể mà tôi đã tạo với tùy chọn "request: false". Bây giờ khi tôi tải lên máy chủ rails hoặc bảng điều khiển rails, tôi có thể yêu cầu đá quý trong trình khởi tạo và đá quý đã được tải. Tuy nhiên, khi tôi chạy kiểm tra tính năng thông số kỹ thuật với rspec và capybara, tôi đã gặp lỗi tải. Và tôi hoàn toàn hoang mang tại sao không tìm thấy Đá quý trong $ LOAD_PATH khi chạy thử nghiệm.
Vì vậy, tôi đã xem xét tất cả các cách khác nhau mà tải, yêu cầu, rubygems và Bundler tương tác. Và đây là bản tóm tắt những phát hiện của tôi đã giúp tôi tìm ra giải pháp cho vấn đề cụ thể của mình:
tải
1) Bạn có thể chuyển cho nó một đường dẫn tuyệt đối đến tệp ruby và nó sẽ thực thi mã trong tệp đó.
load('/Users/myuser/foo.rb')
2) Bạn có thể chuyển một đường dẫn tương đối để tải. Nếu bạn đang ở trong cùng một thư mục với tệp, nó sẽ tìm thấy nó:
> load('./foo.rb')
foo.rb loaded!
=> true
Nhưng nếu bạn cố gắng tải một tệp từ thư mục khác với load (), nó sẽ không tìm thấy tệp đó với một đường dẫn tương đối dựa trên thư mục làm việc hiện tại (ví dụ: ./):
> load('./foo.rb')
LoadError: cannot load such file -- foo.rb
3) Như được hiển thị ở trên, tải luôn trả về true (nếu không thể tải tệp, nó sẽ tăng a LoadError
).
4) Các biến toàn cục, lớp, hằng và phương thức đều được nhập, nhưng không phải là biến cục bộ.
5) Gọi tải hai lần trên cùng một tệp sẽ thực thi mã trong tệp đó hai lần. Nếu tệp được chỉ định xác định một hằng số, nó sẽ xác định hằng số đó hai lần, điều này tạo ra một cảnh báo.
6) $ LOAD_PATH là một mảng các đường dẫn tuyệt đối. Nếu bạn chỉ tải một tên tệp, nó sẽ lặp qua $ LOAD_PATH và tìm kiếm tệp trong mỗi thư mục.
> $LOAD_PATH.push("/Users/myuser")
> load('foo.rb')
foo.rb loaded!
=> true
yêu cầu
1) Việc gọi yêu cầu trên cùng một tệp hai lần sẽ chỉ thực hiện nó một lần. Nó cũng đủ thông minh để không tải cùng một tệp hai lần nếu bạn tham chiếu đến nó một lần với một đường dẫn tương đối và một lần với một đường dẫn tuyệt đối.
2) yêu cầu trả về true nếu tệp được thực thi và false nếu không.
3) yêu cầu theo dõi những tệp nào đã được tải trong biến tổng thể $ LOADED_FEATURES.
4) Bạn không cần bao gồm phần mở rộng tệp:
require 'foo'
5) request sẽ tìm kiếm foo.rb, nhưng cũng có các tệp thư viện động, như foo.so, foo.o hoặc foo.dll. Đây là cách bạn có thể gọi mã C từ ruby.
6) request không kiểm tra thư mục hiện tại, vì thư mục hiện tại theo mặc định không nằm trong $ LOAD_PATH.
7) request_relative nhận một đường dẫn liên quan đến tệp hiện tại, không phải thư mục làm việc của tiến trình.
Rubygems
1) Rubygems là một trình quản lý gói được thiết kế để dễ dàng quản lý việc cài đặt các thư viện Ruby được gọi là gems.
2) Nó đóng gói nội dung của nó dưới dạng tệp zip chứa một loạt tệp ruby và / hoặc tệp thư viện động có thể được nhập bằng mã của bạn, cùng với một số siêu dữ liệu.
3) Rubygems thay thế phương thức request mặc định bằng phiên bản riêng của nó. Phiên bản đó sẽ xem xét các đá quý đã cài đặt của bạn ngoài các thư mục trong $ LOAD_PATH. Nếu Rubygems tìm thấy tệp trong đá quý của bạn, nó sẽ thêm đá quý đó vào $ LOAD_PATH của bạn.
4) Lệnh cài đặt đá quý tìm ra tất cả các phụ thuộc của một viên đá quý và cài đặt chúng. Trên thực tế, nó cài đặt tất cả các phụ thuộc của một viên đá quý trước khi cài đặt viên đá quý đó.
Bundler
1) Bundler cho phép bạn chỉ định tất cả các viên đá quý mà dự án của bạn cần và tùy chọn phiên bản của những viên đá quý đó. Sau đó, lệnh gói cài đặt tất cả các đá quý đó và các phụ thuộc của chúng.
2) Bạn chỉ định những viên ngọc nào bạn cần trong một tệp có tên là Gemfile.
3) Lệnh gói cũng cài đặt tất cả các đá quý được liệt kê trong Gemfile.lock tại các phiên bản cụ thể được liệt kê.
4) Đặt gói thực thi trước một lệnh, ví dụ như gói thực thi rspec, đảm bảo yêu cầu đó sẽ tải phiên bản của một viên ngọc được chỉ định trong Gemfile.lock của bạn.
Rails và Bundler
1) Trong config / boot.rb, yêu cầu 'packler / setup' được chạy. Bundler đảm bảo rằng Ruby có thể tìm thấy tất cả các viên ngọc trong Gemfile (và tất cả các phụ thuộc của chúng). request 'Bundler / setup' sẽ tự động khám phá Gemfile của bạn và làm cho tất cả các viên ngọc trong Gemfile của bạn có sẵn cho Ruby (về mặt kỹ thuật, nó đặt các viên đá quý "trên đường tải"). Bạn có thể coi nó như một sự bổ sung thêm một số sức mạnh để yêu cầu 'rubygem'.
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
2) Bây giờ mã của bạn đã có sẵn cho Ruby, bạn có thể yêu cầu những viên đá quý mà bạn cần. Ví dụ: bạn có thể yêu cầu 'sinatra'. Nếu bạn có nhiều phụ thuộc, bạn có thể muốn nói “yêu cầu tất cả các viên ngọc trong Gemfile của tôi”. Để thực hiện việc này, hãy đặt đoạn mã sau ngay sau yêu cầu 'packler / setup':
Bundler.require(:default)
3) Theo mặc định, việc gọi Bundler.require sẽ yêu cầu từng viên ngọc trong Gemfile của bạn. Nếu dòng trong Gemfile cho biết gem 'foo',: request => false thì nó sẽ đảm bảo foo đã được cài đặt, nhưng nó sẽ không gọi request. Bạn sẽ phải gọi request ('foo') nếu muốn sử dụng gem.
Vì vậy, với bề rộng kiến thức này, tôi quay lại vấn đề của bài kiểm tra của mình và nhận ra rằng tôi phải yêu cầu rõ ràng viên ngọc trong rails_helper.rb, vì Bundler.setup đã thêm nó vào $ LOAD_PATH nhưng yêu cầu: sai Bundler đã bị loại trừ. Yêu cầu yêu cầu nó một cách rõ ràng . Và sau đó vấn đề đã được giải quyết.
require
,load
hoặcautoload
trong Ruby?