Thêm thư mục vào $ LOAD_PATH (Ruby)


95

Tôi đã thấy hai kỹ thuật thường được sử dụng để thêm thư mục của tệp hiện đang được thực thi vào $ LOAD_PATH (hoặc $ :). Tôi thấy những lợi thế của việc này trong trường hợp bạn không làm việc với một viên ngọc. Rõ ràng là một cái có vẻ dài dòng hơn cái kia, nhưng có lý do gì để đi với cái này hơn cái kia không?

Phương thức đầu tiên, dài dòng (có thể quá mức cần thiết):

$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__))) unless $LOAD_PATH.include?(File.expand_path(File.dirname(__FILE__)))

và đơn giản hơn, nhanh chóng và bẩn thỉu hơn:

$:.unshift File.dirname(__FILE__)

Bất kỳ lý do để đi với một trong những khác?


2
Một chút phiên bản ít tiết của tiết một là:File.expand_path(File.dirname(__FILE__)).tap {|pwd| $LOAD_PATH.unshift(pwd) unless $LOAD_PATH.include?(pwd)}
Nathan dài

làm thế nào về mệnh đề "trừ khi"? Làm thế nào mà hai ở trên có thể tương đương?
inger

Là một người đến đây để cố gắng hiểu cách sử dụng điều này, nó thật siêu khó hiểu. Tôi không thấy tên thư mục đến từ đâu trên các ví dụ. Tôi sẽ đánh giá cao nếu ai đó có thể làm rõ điều này.
SlySherZ

1
Sử dụng __dir__(kể từ Ruby 2.0) có thể làm cho bất kỳ điều nào trong số này ngắn gọn hơn.
Nathan Long

Câu trả lời:


50

Tôi sẽ nói đi với $:.unshift File.dirname(__FILE__)cái kia, đơn giản vì tôi đã thấy nhiều cách sử dụng nó trong mã hơn cái $LOAD_PATHkia, và nó cũng ngắn hơn!


Khi tôi lần đầu tiên bắt đầu với Ruby, rõ ràng tôi nghĩ rằng $ LOAD_PATH tốt hơn. Nhưng khi bạn đã tốt nghiệp từ trạng thái mới bắt đầu, tôi sẽ chỉ sử dụng $ LOAD_PATH nếu tôi đang cố gắng làm cho mã của mình dễ đọc hơn đối với người mới bắt đầu. Đó là một sự đánh đổi. Nó phụ thuộc vào mức độ "công khai" của mã, miễn là việc sử dụng bộ nhớ là như nhau cho mỗi mã, mà tôi cho rằng về cơ bản là như vậy.
boulder_ruby

9
Phụ thuộc vào hướng dẫn phong cách bạn làm theo cho dự án của mình. Hướng dẫn phong cách Ruby phổ biến nói rằng "Tránh sử dụng các biến đặc biệt kiểu Perl (như $ :, $ ;, v.v.). Chúng khá khó hiểu và việc sử dụng chúng trong bất kỳ thứ gì ngoại trừ các tập lệnh một lớp không được khuyến khích."
bobmagoo 19/09/15

152

Đường dẫn tải của Ruby rất hay được viết là $:, nhưng chỉ vì nó ngắn nên không làm cho nó tốt hơn. Nếu bạn thích sự rõ ràng hơn là thông minh, hoặc nếu sự ngắn gọn chỉ vì lợi ích của nó khiến bạn ngứa ngáy, bạn không cần phải làm điều đó chỉ vì những người khác cũng vậy. Gửi lời chào đến ...

$LOAD_PATH

... và tạm biệt ...

# I don't quite understand what this is doing...
$:

29
Ngoài ra, Google sẽ khó hơn rất nhiều đối với các chuỗi như "$:" chỉ chứa các ký hiệu.
DSimon

23

Tôi không thích cách 'nhanh chóng và bẩn thỉu'. Bất kỳ ai mới sử dụng Ruby đều sẽ cân nhắc xem $:.nó là gì .

Tôi thấy điều này rõ ràng hơn.

libdir = File.dirname(__FILE__)
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)

Hoặc nếu tôi quan tâm đến việc có con đường đầy đủ ...

libdir = File.expand_path(File.dirname(__FILE__))
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)

CẬP NHẬT 2009/09/10

Tính đến cuối giờ, tôi đã làm những việc sau:

$:.unshift(File.expand_path(File.dirname(__FILE__))) unless
    $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))

Tôi đã thấy nó trong một loạt các dự án ruby ​​khác nhau khi duyệt GitHub.

Có vẻ là quy ước?


@LukeAntins, điều này thực sự tuyệt vời nhưng tôi nên "bootstrap" load_path ở đâu trong ứng dụng?
gaussblurinc

@gaussblurinc Ở đâu đó 'gần đầu' lib / ứng dụng của bạn, nhưng nó thực sự phụ thuộc. Nếu bạn có một bintệp luôn liên quan đến tệp của bạn codevà nó chỉ được chạy bởi bintệp ... bootstrap trong thùng. Nếu bạn có một thư viện, hãy bootstrap ở đầu mã thư viện của bạn như trong lib/code.rbđể có quyền truy cập vào mọi thứ bên dưới lib/code/. Hy vọng lan man này sẽ giúp!
Luke Antins

1
RuboCop thông báo cho tôi rằng __dir__có thể được sử dụng để lấy đường dẫn đến thư mục của tệp hiện tại.
Raphael

8

Nếu bạn nhập script/consoledự án Rails của mình và enter $:, bạn sẽ nhận được một mảng bao gồm tất cả các thư mục cần thiết để tải Ruby. Điểm rút ra từ bài tập nhỏ này $:là một mảng. Vì vậy, bạn có thể thực hiện các chức năng trên nó như thêm trước các thư mục khác với unshiftphương thức hoặc <<toán tử. Như bạn ngụ ý trong tuyên bố của bạn $:$LOAD_PATHgiống nhau.

Điểm bất lợi khi thực hiện nó theo cách nhanh chóng và bẩn thỉu như bạn đã đề cập là: nếu bạn đã có thư mục trong đường dẫn khởi động của mình, nó sẽ tự lặp lại.

Thí dụ:

Tôi có một plugin mà tôi đã tạo có tên là todo. Thư mục của tôi được cấu trúc như vậy:

/ --- nhà cung cấp
  |
  | --- / plugin
        |
        | --- / todo
              |
              | --- / lib
                    |
                    | --- / ứng dụng
                          |
                          | --- / mô hình
                          | --- / bộ điều khiển
              |
              | --- / rails
                    |
                    | --- init.rb

Trong tệp init.rb, tôi đã nhập mã sau:

## In vendor/plugins/todo/rails/init.rb
    %w{ models controllers models }.each do |dir|
      path = File.expand_path(File.join(File.dirname(__FILE__), '../lib', 'app', dir))
      $LOAD_PATH << path
      ActiveSupport::Dependencies.load_paths << path
      ActiveSupport::Dependencies.load_once_paths.delete(path)
    end 

Lưu ý cách tôi yêu cầu khối mã thực hiện các hành động bên trong khối đối với các chuỗi 'mô hình', 'bộ điều khiển' và 'mô hình', nơi tôi lặp lại 'mô hình'. (FYI, %w{ ... }chỉ là một cách khác để yêu cầu Ruby giữ một mảng chuỗi). Khi tôi chạy script/console, tôi gõ như sau:

>> puts $:

Và tôi gõ cái này để dễ đọc nội dung trong chuỗi hơn. Đầu ra tôi nhận được là:

...
...
./Users/Me/mySites/myRailsApp/vendor/plugins/todo/lib/app/models
./Users/Me/mySites/myRailsApp/vendor/plugins/todo/lib/app/controllers
./Users/Me/mySites/myRailsApp/vendor/plugins/todo/lib/app/models

Như bạn có thể thấy, mặc dù đây là một ví dụ đơn giản mà tôi có thể tạo trong khi sử dụng một dự án mà tôi hiện đang thực hiện, nhưng nếu bạn không cẩn thận, cách nhanh chóng và bẩn thỉu sẽ dẫn đến các đường dẫn lặp lại. Cách dài hơn sẽ kiểm tra các đường dẫn lặp lại và đảm bảo chúng không xảy ra.

Nếu bạn là một lập trình viên Rails có kinh nghiệm, bạn có thể có một ý tưởng rất tốt về những gì bạn đang làm và có khả năng không mắc lỗi lặp lại các đường dẫn. Nếu bạn là người mới, tôi sẽ đi con đường dài hơn cho đến khi bạn thực sự hiểu mình đang làm gì.


phản hồi của bạn rất hữu ích và cũng được giải thích tốt. Chỉnh sửa được đề xuất: phương pháp load_pathsload_once_paths.deletekhông được dùng nữa. Sẽ được giúp đỡ để cập nhật các dòng đề cập đến họ như: ActiveSupport::Dependencies.autoload_paths << path ActiveSupport::Dependencies.autoload_once_paths.delete(path)
Uzzar

8

Tốt nhất tôi đã thử thêm một dir qua đường dẫn tương đối khi sử dụng Rspec. Tôi thấy nó đủ dài nhưng cũng vẫn là một lớp lót tốt đẹp.

$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))


-2

Tôi biết đã lâu rồi kể từ lần đầu tiên câu hỏi này được hỏi, nhưng tôi có một câu trả lời bổ sung mà tôi muốn chia sẻ.

Tôi có một số ứng dụng Ruby được phát triển bởi một lập trình viên khác trong vài năm và chúng sử dụng lại các lớp giống nhau trong các ứng dụng khác nhau mặc dù chúng có thể truy cập cùng một cơ sở dữ liệu. Vì điều này vi phạm quy tắc DRY, tôi quyết định tạo một thư viện lớp để tất cả các ứng dụng Ruby chia sẻ. Tôi có thể đã đặt nó trong thư viện Ruby chính, nhưng điều đó sẽ ẩn mã tùy chỉnh trong cơ sở mã chung mà tôi không muốn làm.

Tôi đã gặp sự cố trong đó tôi có xung đột tên giữa tên đã được xác định "profile.rb" và lớp tôi đang sử dụng. Xung đột này không phải là vấn đề cho đến khi tôi cố gắng tạo thư viện mã chung. Thông thường, Ruby tìm kiếm các vị trí ứng dụng trước, sau đó đi đến các vị trí $ LOAD_PATH.

Application_controller.rb không thể tìm thấy lớp tôi đã tạo và gây ra lỗi cho định nghĩa ban đầu vì nó không phải là một lớp. Vì tôi đã xóa định nghĩa lớp khỏi phần ứng dụng / mô hình của ứng dụng, nên Ruby không thể tìm thấy nó ở đó và đã tìm kiếm nó trong các đường dẫn của Ruby.

Vì vậy, tôi đã sửa đổi biến $ LOAD_PATH để bao gồm một đường dẫn đến thư mục thư viện mà tôi đang sử dụng. Điều này có thể được thực hiện trong tệp môi trường.rb tại thời điểm khởi tạo.

Ngay cả khi thư mục mới được thêm vào đường dẫn tìm kiếm, Ruby vẫn gặp lỗi vì nó đã ưu tiên lấy tệp do hệ thống xác định trước. Đường dẫn tìm kiếm trong biến $ LOAD_PATH ưu tiên tìm kiếm các đường dẫn Ruby trước.

Vì vậy, tôi cần thay đổi thứ tự tìm kiếm để Ruby tìm thấy lớp trong thư viện chung của tôi trước khi nó tìm kiếm các thư viện tích hợp sẵn.

Mã này đã thực hiện nó trong tệp môi trường.rb:

Rails::Initializer.run do |config|

* * * * *

path = []
path.concat($LOAD_PATH)
$LOAD_PATH.clear
$LOAD_PATH << 'C:\web\common\lib'
$LOAD_PATH << 'C:\web\common'
$LOAD_PATH.concat(path)

* * * * *

end

Tôi không nghĩ rằng bạn có thể sử dụng bất kỳ cấu trúc mã hóa nâng cao nào được đưa ra trước đó ở cấp độ này, nhưng nó hoạt động tốt nếu bạn muốn thiết lập thứ gì đó tại thời điểm khởi tạo trong ứng dụng của mình. Bạn phải duy trì thứ tự ban đầu của biến $ LOAD_PATH ban đầu khi nó được thêm trở lại biến mới nếu không một số lớp Ruby chính sẽ bị mất.

Trong tệp application_controller.rb, tôi chỉ cần sử dụng

require 'profile'
require 'etc' #etc

và điều này sẽ tải các tệp thư viện tùy chỉnh cho toàn bộ ứng dụng, tức là tôi không phải sử dụng các lệnh request trong mọi bộ điều khiển.

Đối với tôi, đây là giải pháp tôi đang tìm kiếm và tôi nghĩ rằng tôi sẽ thêm nó vào câu trả lời này để truyền thông tin.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.