Cara terbaik untuk meminta semua file dari direktori di ruby?

351

Apa cara terbaik untuk meminta semua file dari direktori di ruby?

Gaetan Dubar
sumber
Anda dapat membuat file dengan nama direktori yang sama dan meminta semua file. RSpec melakukan ini.
pedromanoel

Jawaban:

462

Bagaimana tentang:

Dir["/path/to/directory/*.rb"].each {|file| require file }
Sam Stokes
sumber
18
Menurut Beliung, ekstensi .rb adalah opsional. Secara teknis ini mengubah arti: "memerlukan 'foo.rb'" memerlukan foo.rb, sedangkan "membutuhkan 'foo'" mungkin memerlukan foo.rb, foo.so atau foo.dll.
Sam Stokes
28
Ada gotcha yang halus untuk tidak membuka ekstensi. Jika beberapa bagian lain dari panggilan kode memerlukan 'foo' maka ruby ​​akan memuat file yang sama lagi, yang dapat menyebabkan kesalahan palsu. Saya menambahkan jawaban saya sendiri yang menjelaskan hal itu dan menunjukkan cara menghapus ekstensi.
Pete Hodgson
4
@Pete, apakah ini masih benar? Lihat komentar Rene di bawah ini.
Andres Riofrio
5
Ini mungkin jelas, tetapi perlu dicatat bahwa menjatuhkan .rb juga akan memerlukan file non-.rb dalam direktori, yang mungkin tidak diinginkan.
user2398029
10
Saran @ PeteHodgson tidak akurat. Ruby requiretidak bingung dengan ada atau tidaknya .rbekstensi. Diuji pada MRI 1.8.7-p374, 2.1.5 dan 2.2.0 diuji. Legenda urban ini berasal dari Rails, di mana autoloading "pintar" menunjukkan perilaku yang ia gambarkan dalam versi yang lebih lama (dan mungkin masih menunjukkannya).
sheldonh
345

Jika itu adalah direktori relatif terhadap file yang memerlukan (misalnya Anda ingin memuat semua file di direktori lib):

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

Sunting: Berdasarkan komentar di bawah, versi yang diperbarui:

Dir[File.join(__dir__, 'lib', '*.rb')].each { |file| require file }
jandot
sumber
14
Anda juga dapat menambahkan semua direktori anak seperti iniDir[File.dirname(__FILE__) + '/support/**/*.rb'].each {|file| require file }
jspooner
62
Mungkin lebih aman menggunakan File.join daripada membuat asumsi tentang garis miring / maju:Dir[File.join(File.dirname(__FILE__), 'lib', '*.rb')].each {|file| require file }
Chris
6
Ada juga require_relative
maasha
30
Jika Anda menggunakan> = ruby ​​2.0, Anda dapat menggunakan __dir__sebagai gantinya File.dirname(__FILE__).
Christian Bankester
3
@maasha Bagaimana Anda menggunakan require_relativesemua file dalam direktori?
David Moles
101

Coba permata require_all:

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

Ini memungkinkan Anda untuk:

require_all 'path/to/directory'

sumber
Saya perlu memasukkan semua model ActiveRecord saya, permata require_all menemukan semua dependensi dan meminta mereka dengan sempurna. Terima kasih!
panupan
2
@panupan Ketahuilah bahwa require_allresolusi dependensi siklik mengatasi masalah dalam kode sumber Anda: Anda memiliki file sumber Ruby yang tidak memerlukan dependensinya. Ini menutup pintu pada pemuatan pisau bedah, membuat Anda melakukan pemuatan semua atau tidak sama sekali. Itu bukan masalah di perpustakaan kecil, tapi itu keputusan yang harus Anda buat secara sadar.
sheldonh
Tidak masuk akal untuk mengasapi aplikasi Anda dengan permata yang bisa Anda ganti dengan sederet kode. Ini meningkatkan waktu buka aplikasi Anda dan menginduksi lebih banyak bug dalam jangka panjang.
Pere Joan Martorell
52
Dir[File.dirname(__FILE__) + '/../lib/*.rb'].each do |file| 
  require File.basename(file, File.extname(file))
end

Jika Anda tidak menghapus ekstensi, maka Anda mungkin membutuhkan file yang sama dua kali (ruby tidak akan menyadari bahwa "foo" dan "foo.rb" adalah file yang sama). Membutuhkan file yang sama dua kali dapat menyebabkan peringatan palsu (mis. "Peringatan: sudah diinisialisasi konstan").

Pete Hodgson
sumber
11
Benarkah ini masalahnya? Dokumentasi mengatakan: Fitur tidak akan dimuat jika namanya sudah muncul dalam $ ". Nama file dikonversi ke jalur absolut, jadi" memerlukan 'a'; membutuhkan './a' "tidak akan memuat a.rb dua kali. ruby-doc.org/core/classes/Kernel.html#M001418
Derek
13
Pengujian saya menunjukkan hal yang sama dengan yang dikatakan Derek: require "foo.rb"; require "foo";akan dimuat foo.rbsekali saja.
Rene Saarsoo
@ PeteHodgson- Bisakah Anda mendukungnya?
Yarin
4
Tidak. Ruby requiretidak bingung dengan ada atau tidaknya .rbekstensi. Diuji pada MRI 1.8.7-p374, 2.1.5 dan 2.2.0. Legenda urban ini berasal dari Rails, di mana autoloading "pintar" menunjukkan perilaku yang dijelaskan dalam versi lama (dan mungkin masih menunjukkannya).
sheldonh
45
Dir.glob(File.join('path', '**', '*.rb'), &method(:require))

atau sebagai alternatif, jika Anda ingin mengatur ruang lingkup file untuk dimuat ke folder tertentu:

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

penjelasan:

Dir.glob mengambil blok sebagai argumen.

metode (: wajib) akan mengembalikan metode yang diperlukan.

& metode (: wajib) akan mengonversi metode menjadi blok.

metakungfu
sumber
2
Ini adalah kode yang indah. Saya suka bagaimana tidak ada blok yang terlihat.
Nate Symer
1
Dir.glob( File.join( File.dirname(__FILE__), '{lib,addons}', 'subfolder', '**', '*.rb' ), &method(:require) )menghilangkan ketergantungan pada platform (seperti '/' atau '\'). Bekerja dengan baik. Terima kasih.
Ivan Black
30

Cara terbaik adalah menambahkan direktori ke path memuat dan kemudian requirenama dasar setiap file. Ini karena Anda ingin menghindari secara tidak sengaja memerlukan file yang sama dua kali - sering kali bukan perilaku yang diinginkan. Apakah suatu file akan dimuat atau tidak tergantung pada apakah requiretelah melihat jalur yang dilewati sebelumnya. Misalnya, sesi irb sederhana ini menunjukkan bahwa Anda dapat secara keliru meminta dan memuat file yang sama dua kali.

$ 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

Perhatikan bahwa dua baris pertama kembali yang trueberarti file yang sama dimuat dua kali. Saat jalur digunakan, bahkan jika jalur menunjuk ke lokasi yang sama, requiretidak tahu bahwa file itu sudah diperlukan.

Di sini, sebagai gantinya, kami menambahkan direktori ke jalur pemuatan dan kemudian memerlukan nama dasar setiap file * .rb di dalamnya.

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

Jika Anda tidak peduli dengan file yang diminta lebih dari satu kali, atau niat Anda hanya untuk memuat konten file, mungkin loadsebaiknya digunakan saja require. Gunakan beban dalam kasus ini, karena lebih baik mengekspresikan apa yang Anda coba capai. Sebagai contoh:

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

Alih-alih menyatukan jalur seperti dalam beberapa jawaban, saya menggunakan File.expand_path:

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

Memperbarui:

Alih-alih menggunakan File.dirnameAnda bisa melakukan hal berikut:

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

Di mana ..strip nama file __FILE__.

Koen.
sumber
ini sepertinya cara yang harus ditempuh, dan sebagian besar menjawab, setelah mencoba yang lainnya, +1 untukFile.expand_path
SuckerForMayhem
Saya lebih suka jawaban ini daripada jawaban yang diterima. Berbagai Rails.root.joinjawaban juga berfungsi jika Anda berada di rel.
nzifnab
14
Dir[File.join(__dir__, "/app/**/*.rb")].each do |file|
  require file
end

Ini akan bekerja secara rekursif pada mesin lokal Anda dan remote (Suka Heroku) yang tidak menggunakan jalur relatif.

shushugah
sumber
8

Di Rails, Anda dapat melakukan:

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

Pembaruan: Diperbaiki dengan saran dari @Jiggneshh Gohel untuk menghapus garis miring.

Dan Kohn
sumber
Karena Rails.rootini adalah Pathnamecontoh, Anda dapat melakukan ini di lingkungan Ruby apa pun, bukan hanya Rails (NB Rails.root.join('lib/ext/*.rb')sedikit lebih bagus)
DMKE
Terima kasih untuk rekomendasinya; Saya diedit untuk memasukkan komentar Anda.
Dan Kohn
Menggunakan garis miring (/) untuk sub-direktori di bawah Rails.root, untuk misalnya Rails.root.join('/lib')tidak menghasilkan jalur yang benar. Saya menemukan ini bekerja dengan benar:Dir[Rails.root.join('lib', 'ext', '*.rb')].each { |file| require file }
Jignesh Gohel
@Jiggneshh Gohel Saya menghapus garis miring seperti yang Anda sarankan, terima kasih.
Dan Kohn
3

Saya beberapa tahun terlambat ke pesta, tapi saya agak suka solusi satu baris ini yang saya gunakan untuk mendapatkan kereta api untuk memasukkan semua yang ada di app / pekerja / masalah:

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

Jazz
sumber
-2

Dan bagaimana dengan require_relative *Dir['relative path']:?

Aleksander
sumber
1
Membutuhkan relatif tidak mengambil beberapa file: ruby-doc.org/core-2.1.2/Kernel.html#method-i-require_relative
fabriciofreitag
OKE, tetapi dalam contoh saya tidak. The '*' berubah arity menjadi 1. Ia berfungsi sebagai beberapa panggilan ke require_relative.
Aleksander
1
'*' Berubah menjadi 1 - Apa maksud Anda dengan itu? require_relative *Dir['*.rb']bekerja, jika hanya ada satu skrip ruby. Tetapi jika beberapa skrip ruby ​​ditemukan, Anda dapatrequire_relative': wrong number of arguments (4 for 1) (ArgumentError)
knut