Cách tốt nhất để yêu cầu tất cả các tập tin từ một thư mục trong ruby?


351

Cách tốt nhất để yêu cầu tất cả các tệp từ một thư mục trong ruby ​​là gì?


Bạn có thể tạo một tệp có cùng tên của thư mục và yêu cầu tất cả các tệp. RSpec làm điều này.
pedromanoel

Câu trả lời:


462

Làm thế nào về:

Dir["/path/to/directory/*.rb"].each {|file| require file }

18
Theo Pickaxe, phần mở rộng .rb là tùy chọn. Về mặt kỹ thuật, nó thay đổi ý nghĩa: "yêu cầu 'foo.rb'" yêu cầu foo.rb, trong khi "yêu cầu 'foo'" có thể yêu cầu foo.rb, foo.so hoặc foo.dll.
Sam Stokes

28
Có một gotcha tinh tế để không tước phần mở rộng. Nếu một số phần khác của các cuộc gọi mã yêu cầu 'foo' thì ruby ​​sẽ tải lại cùng một tệp, điều này có thể dẫn đến các lỗi giả. Tôi đã thêm câu trả lời của riêng mình để giải thích điều đó và chỉ ra cách loại bỏ phần mở rộng.
Pete Hodgson

4
@Pete, điều này có còn đúng không? Xem bình luận của Rene bên dưới.
Andres Riofrio

5
Điều này có thể rõ ràng, nhưng đáng lưu ý rằng việc bỏ .rb cũng sẽ yêu cầu bất kỳ tệp không phải .rb nào trong thư mục, điều này có thể không mong muốn.
dùng2398029

10
Đề xuất của @ PeteHodgson là không chính xác. Ruby requirekhông bị nhầm lẫn bởi sự hiện diện hay vắng mặt của .rbphần mở rộng. Đã thử nghiệm trên MRI 1.8.7-p374, 2.1.5 và 2.2.0 được thử nghiệm. Truyền thuyết đô thị này đến từ Rails, nơi tự động tải "thông minh" thể hiện hành vi mà ông mô tả trong các phiên bản cũ hơn (và vẫn có thể thể hiện nó).
sheldonh

345

Nếu đó là một thư mục liên quan đến tệp yêu cầu (ví dụ: bạn muốn tải tất cả các tệp trong thư mục lib):

Dir[File.dirname(__FILE__) + '/lib/*.rb'].each {|file| require file }

Chỉnh sửa: Dựa trên các bình luận bên dưới, một phiên bản cập nhật:

Dir[File.join(__dir__, 'lib', '*.rb')].each { |file| require file }

14
Bạn cũng có thể thêm tất cả các thư mục con như thế nàyDir[File.dirname(__FILE__) + '/support/**/*.rb'].each {|file| require file }
jspooner

62
Có thể an toàn hơn khi sử dụng File.join thay vì đưa ra các giả định về dấu gạch chéo tiến / lùi:Dir[File.join(File.dirname(__FILE__), 'lib', '*.rb')].each {|file| require file }
Chris

6
Ngoài ra còn có
request_relative

30
Nếu bạn đang sử dụng> = ruby ​​2.0, bạn có thể sử dụng __dir__thay vì File.dirname(__FILE__).
Christian Bankester

3
@maasha Làm thế nào để bạn sử dụng require_relativeđể yêu cầu tất cả các tập tin trong một thư mục?
David Moles

101

Hãy thử đá quý allow_all:

  1. http://github.com/jarmo/require_all
  2. https://rubygems.org/gems/require_all

Nó cho phép bạn đơn giản:

require_all 'path/to/directory'

Tôi cần bao gồm tất cả các mô hình ActiveRecord của mình, đá quý allow_all đã tìm ra tất cả các phụ thuộc và yêu cầu chúng hoàn hảo. Cảm ơn!
panupan

2
@panupan Chỉ cần lưu ý rằng require_allđộ phân giải phụ thuộc theo chu kỳ hoạt động xung quanh một vấn đề trong mã nguồn của bạn: bạn có các tệp nguồn Ruby không yêu cầu phụ thuộc của chúng. Điều này sẽ đóng cửa khi tải dao mổ, cam kết bạn sẽ tải tất cả hoặc không có gì. Đó không phải là vấn đề trong các thư viện nhỏ, nhưng đó là một quyết định mà bạn nên đưa ra một cách có ý thức.
sheldonh

Không có nghĩa là làm phồng ứng dụng của bạn bằng đá quý mà bạn có thể chỉ cần thay thế bằng một dòng mã. Điều này làm tăng thời gian tải ứng dụng của bạn và gây ra nhiều lỗi hơn trong thời gian dài.
Pere Joan Martorell

52
Dir[File.dirname(__FILE__) + '/../lib/*.rb'].each do |file| 
  require File.basename(file, File.extname(file))
end

Nếu bạn không loại bỏ phần mở rộng thì cuối cùng bạn có thể yêu cầu cùng một tệp hai lần (ruby sẽ không nhận ra rằng "foo" và "foo.rb" là cùng một tệp). Yêu cầu cùng một tệp hai lần có thể dẫn đến cảnh báo giả (ví dụ: "cảnh báo: hằng số đã khởi tạo").


11
Đây đúng là tình trạng đó phải không? Tài liệu nói: Một tính năng sẽ không được tải nếu tên của nó đã xuất hiện trong $ ". Tên tệp được chuyển đổi thành một đường dẫn tuyệt đối, vì vậy" yêu cầu 'a'; yêu cầu './a' "sẽ không tải a.rb hai lần. ruby-doc.org/core/groupes/Kernel.html#M001418
Derek

13
Thử nghiệm của tôi cho thấy điều tương tự mà Derek đã nói: require "foo.rb"; require "foo";sẽ tải foo.rbchỉ một lần.
Rene Saarsoo

@ PeteHodgson- Bạn có thể sao lưu này không?
Yarin

4
Không. Ruby requirekhông bị nhầm lẫn bởi sự hiện diện hay vắng mặt của .rbphần mở rộng. Đã thử nghiệm trên MRI 1.8.7-p374, 2.1.5 và 2.2.0. Truyền thuyết đô thị này đến từ Rails, nơi tự động tải "thông minh" thể hiện hành vi được mô tả trong các phiên bản cũ hơn (và vẫn có thể thể hiện nó).
sheldonh

45
Dir.glob(File.join('path', '**', '*.rb'), &method(:require))

hoặc cách khác, nếu bạn muốn phạm vi các tệp để tải vào các thư mục cụ thể:

Dir.glob(File.join('path', '{folder1,folder2}', '**', '*.rb'), &method(:require))

giải trình:

Dir.glob lấy một khối làm đối số.

phương thức (: Yêu cầu) sẽ trả về phương thức yêu cầu.

& method (: Yêu cầu) sẽ chuyển đổi phương thức thành một khối.


2
Đây là một số mã đẹp. Tôi yêu như thế nào không có khối có thể nhìn thấy.
Symate Nate

1
Dir.glob( File.join( File.dirname(__FILE__), '{lib,addons}', 'subfolder', '**', '*.rb' ), &method(:require) )loại bỏ sự phụ thuộc vào nền tảng (chẳng hạn như '/' hoặc '\'). Hoạt động tốt. Cảm ơn.
Ivan Black

30

Cách tốt nhất là thêm thư mục vào đường dẫn tải và sau đó requirelà tên cơ sở của mỗi tệp. Điều này là do bạn muốn tránh việc vô tình yêu cầu cùng một tệp hai lần - thường không phải là hành vi dự định. Một tập tin sẽ được tải hay không phụ thuộc vào việc bạn requiređã thấy đường dẫn được chuyển đến trước đó hay chưa. Ví dụ, phiên irb đơn giản này cho thấy rằng bạn có thể yêu cầu và tải cùng một tệp hai lần.

$ irb
irb(main):001:0> require 'test'
=> true
irb(main):002:0> require './test'
=> true
irb(main):003:0> require './test.rb'
=> false
irb(main):004:0> require 'test'
=> false

Lưu ý rằng hai dòng đầu tiên trả về truecó nghĩa là cùng một tệp đã được tải cả hai lần. Khi các đường dẫn được sử dụng, ngay cả khi các đường dẫn trỏ đến cùng một vị trí, requirekhông biết rằng tệp đã được yêu cầu.

Thay vào đó, chúng tôi thêm một thư mục vào đường dẫn tải và sau đó yêu cầu tên cơ sở của từng tệp * .rb bên trong.

dir = "/path/to/directory"
$LOAD_PATH.unshift(dir)
Dir[File.join(dir, "*.rb")].each {|file| require File.basename(file) }

Nếu bạn không quan tâm đến việc tệp được yêu cầu nhiều lần, hoặc ý định của bạn chỉ là tải nội dung của tệp, có lẽ loadnên được sử dụng thay vì require. Sử dụng tải trong trường hợp này, vì nó thể hiện tốt hơn những gì bạn đang cố gắng thực hiện. Ví dụ:

Dir["/path/to/directory/*.rb"].each {|file| load file }

14

Thay vì nối các đường dẫn như trong một số câu trả lời, tôi sử dụng File.expand_path:

Dir[File.expand_path('importers/*.rb', File.dirname(__FILE__))].each do |file|
  require file
end

Cập nhật:

Thay vì sử dụng, File.dirnamebạn có thể làm như sau:

Dir[File.expand_path('../importers/*.rb', __FILE__)].each do |file|
  require file
end

Trường hợp ..dải tên tệp của __FILE__.


đây dường như là cách tốt nhất và câu trả lời cập nhật nhất, sau khi thử tất cả phần còn lại, +1 choFile.expand_path
SuckerForMayhem

Tôi chắc chắn thích câu trả lời này cho câu trả lời được chấp nhận. Rails.root.joinCâu trả lời khác nhau cũng hoạt động nếu bạn đang ở trong đường ray.
nzifnab

14
Dir[File.join(__dir__, "/app/**/*.rb")].each do |file|
  require file
end

Điều này sẽ hoạt động đệ quy trên máy cục bộ của bạn và một điều khiển từ xa (Giống như Heroku) không sử dụng các đường dẫn tương đối.


8

Trong Rails, bạn có thể làm:

Dir[Rails.root.join('lib', 'ext', '*.rb')].each { |file| require file }

Cập nhật: Đã sửa với đề xuất của @Jiggneshh Gohel để xóa dấu gạch chéo.


Rails.rootlà một Pathnameví dụ, bạn có thể thực hiện việc này trong bất kỳ môi trường Ruby nào, không chỉ Rails (NB Rails.root.join('lib/ext/*.rb')đọc đẹp hơn một chút)
DMKE

Cảm ơn vì lời giới thiệu; Tôi chỉnh sửa để bao gồm nhận xét của bạn.
Dan Kohn

Sử dụng dấu gạch chéo (/) cho các thư mục con trong Rails.root, ví dụ: Rails.root.join('/lib')không tạo đường dẫn chính xác. Tôi tìm thấy cái này để hoạt động chính xác:Dir[Rails.root.join('lib', 'ext', '*.rb')].each { |file| require file }
Jignesh Gohel

@Jiggneshh Gohel Tôi đã xóa dấu gạch chéo như bạn đề xuất, cảm ơn.
Dan Kohn

3

Tôi dự tiệc muộn vài năm, nhưng tôi thích giải pháp một dòng này mà tôi đã sử dụng để có được đường ray để bao gồm mọi thứ trong ứng dụng / nhân viên / mối quan tâm:

Dir[ Rails.root.join *%w(app workers concerns *) ].each{ |f| require f }


-2

Và những gì về : require_relative *Dir['relative path']?


1
Yêu cầu người thân không lấy nhiều tệp: ruby-doc.org/core-2.1.2/Kernel.html#method-i-require_relative
Fabriciofreitag

OK, nhưng trong ví dụ của tôi thì không. '*' Thay đổi mức độ thành 1. Nó hoạt động như nhiều cuộc gọi đến allow_relative.
Aleksander

1
'*' Thay đổi mức độ thành 1 - Bạn có ý nghĩa gì với nó? require_relative *Dir['*.rb']làm việc, nếu chỉ có một tập lệnh ruby. Nhưng nếu tìm thấy nhiều tập lệnh ruby, bạn sẽ nhận đượcrequire_relative': wrong number of arguments (4 for 1) (ArgumentError)
knut
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.