Der beste Weg, um alle Dateien aus einem Verzeichnis in Ruby zu benötigen?

351

Was ist der beste Weg, um alle Dateien aus einem Verzeichnis in Ruby zu benötigen?

Gaetan Dubar
quelle
Sie können eine Datei mit demselben Namen wie das Verzeichnis erstellen, für die alle Dateien erforderlich sind. RSpec macht das.
Pedromanoel

Antworten:

462

Wie wäre es mit:

Dir["/path/to/directory/*.rb"].each {|file| require file }
Sam Stokes
quelle
18
Laut Pickaxe ist die Erweiterung .rb optional. Technisch ändert sich die Bedeutung: "require 'foo.rb'" erfordert foo.rb, während "require 'foo'" möglicherweise foo.rb, foo.so oder foo.dll erfordert.
Sam Stokes
28
Es ist ein subtiles Problem, die Erweiterung nicht zu entfernen. Wenn für einen anderen Teil der Codeaufrufe 'foo' erforderlich ist, lädt Ruby dieselbe Datei erneut, was zu falschen Fehlern führen kann. Ich habe meine eigene Antwort hinzugefügt, die das erklärt und zeigt, wie die Erweiterung entfernt wird.
Pete Hodgson
4
@Pete, ist das noch wahr? Siehe Renes Kommentar unten.
Andres Riofrio
5
Dies mag offensichtlich sein, aber es ist erwähnenswert, dass zum Löschen der .rb-Datei auch alle Nicht-.rb-Dateien im Verzeichnis erforderlich sind, was möglicherweise nicht erwünscht ist.
user2398029
10
@ PeteHodgsons Vorschlag ist ungenau. Ruby requireist nicht durch das Vorhandensein oder Fehlen der .rbErweiterung verwirrt . Getestet auf MRT 1.8.7-p374, 2.1.5 und 2.2.0 getestet. Diese urbane Legende stammt von Rails, wo "cleveres" Autoloading das Verhalten zeigte, das er in älteren Versionen beschreibt (und möglicherweise noch zeigt).
Sheldonh
345

Wenn es sich um ein Verzeichnis handelt, das sich auf die Datei bezieht, die die Anforderungen erfüllt (z. B. möchten Sie alle Dateien in das lib-Verzeichnis laden):

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

Bearbeiten: Basierend auf den Kommentaren unten, eine aktualisierte Version:

Dir[File.join(__dir__, 'lib', '*.rb')].each { |file| require file }
Jandot
quelle
14
Sie können auch alle Dir[File.dirname(__FILE__) + '/support/**/*.rb'].each {|file| require file }
untergeordneten
62
Es ist wahrscheinlich sicherer, File.join zu verwenden, als Annahmen über Schrägstriche vorwärts / rückwärts zu treffen:Dir[File.join(File.dirname(__FILE__), 'lib', '*.rb')].each {|file| require file }
Chris
6
Es gibt auch require_relative
maasha
30
Wenn Sie> = Ruby 2.0 verwenden, können Sie __dir__anstelle von verwenden File.dirname(__FILE__).
Christian Bankester
3
@maasha Wie verwenden Sie require_relative, um alle Dateien in einem Verzeichnis zu benötigen?
David Moles
101

Probieren Sie das Juwel require_all aus:

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

Sie können einfach:

require_all 'path/to/directory'

quelle
Ich musste alle meine ActiveRecord-Modelle einbeziehen, das Juwel require_all hat alle Abhängigkeiten herausgefunden und sie perfekt benötigt. Vielen Dank!
Panupan
2
@panupan Beachten Sie jedoch, dass require_alldie zyklische Abhängigkeitsauflösung ein Problem in Ihrem Quellcode umgeht : Sie haben Ruby-Quelldateien, für die keine Abhängigkeiten erforderlich sind. Dies schließt die Tür beim Laden des Skalpells und verpflichtet Sie zum Alles-oder-Nichts-Laden. In kleinen Bibliotheken ist das kein Problem, aber es ist eine Entscheidung, die Sie bewusst treffen sollten.
Sheldonh
Es macht keinen Sinn, Ihre App mit Edelsteinen aufzublähen, die Sie einfach durch eine Codezeile ersetzen können. Dies erhöht die Ladezeit Ihrer App und führt langfristig zu mehr Fehlern.
Pere Joan Martorell
52
Dir[File.dirname(__FILE__) + '/../lib/*.rb'].each do |file| 
  require File.basename(file, File.extname(file))
end

Wenn Sie die Erweiterung nicht entfernen, benötigen Sie möglicherweise zweimal dieselbe Datei (Ruby merkt nicht, dass "foo" und "foo.rb" dieselbe Datei sind). Das zweimalige Anfordern derselben Datei kann zu falschen Warnungen führen (z. B. "Warnung: bereits initialisierte Konstante").

Pete Hodgson
quelle
11
Ist das wirklich der Fall? In der Dokumentation heißt es: Ein Feature wird nicht geladen, wenn sein Name bereits in $ "erscheint. Der Dateiname wird in einen absoluten Pfad konvertiert, daher" erfordert 'a'; require './a' "lädt a.rb nicht zweimal. ruby-doc.org/core/classes/Kernel.html#M001418
Derek
13
Meine Tests zeigen dasselbe, was Derek gesagt hat: require "foo.rb"; require "foo";Wird foo.rbnur einmal geladen .
Rene Saarsoo
@ PeteHodgson- Kannst du das sichern?
Yarin
4
Ruby's requireist nicht durch das Vorhandensein oder Fehlen der .rbErweiterung verwirrt . Getestet auf MRT 1.8.7-p374, 2.1.5 und 2.2.0. Diese urbane Legende stammt von Rails, wo "cleveres" Autoloading das in älteren Versionen beschriebene Verhalten zeigte (und es möglicherweise noch zeigt).
Sheldonh
45
Dir.glob(File.join('path', '**', '*.rb'), &method(:require))

oder alternativ, wenn Sie die Dateien in bestimmte Ordner laden möchten:

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

Erläuterung:

Dir.glob nimmt einen Block als Argument.

Methode (: erfordern) gibt die require-Methode zurück.

& method (: require) konvertiert die Methode in einen Block.

Metakung Fu
quelle
2
Dies ist ein wunderschöner Code. Ich liebe es, wie es keine sichtbaren Blöcke gibt.
Nate Symer
1
Dir.glob( File.join( File.dirname(__FILE__), '{lib,addons}', 'subfolder', '**', '*.rb' ), &method(:require) )beseitigt die Abhängigkeit von der Plattform (wie '/' oder '\'). Funktioniert gut. Vielen Dank.
Ivan Black
30

Der beste Weg ist, das Verzeichnis zum requireLadepfad und dann den Basisnamen jeder Datei hinzuzufügen . Dies liegt daran, dass Sie vermeiden möchten, dass dieselbe Datei versehentlich zweimal benötigt wird - häufig nicht das beabsichtigte Verhalten. Ob eine Datei geladen wird oder nicht, hängt davon ab, ob requireder Pfad zuvor an sie übergeben wurde. Diese einfache irb-Sitzung zeigt beispielsweise, dass Sie fälschlicherweise dieselbe Datei zweimal benötigen und laden können.

$ 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

Beachten Sie, dass die ersten beiden Zeilen zurückgegeben werden, truewas bedeutet, dass dieselbe Datei beide Male geladen wurde. Wenn Pfade verwendet werden, auch wenn die Pfade auf denselben Ort verweisen,require ist nicht bekannt, dass die Datei bereits erforderlich war.

Hier fügen wir stattdessen dem Ladepfad ein Verzeichnis hinzu und benötigen dann den Basisnamen jeder darin enthaltenen * .rb-Datei.

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

Wenn Sie sich nicht darum kümmern, dass die Datei mehr als einmal benötigt wird, oder wenn Sie nur den Inhalt der Datei laden möchten, loadsollten Sie sie möglicherweise anstelle von verwenden require. Verwenden Sie in diesem Fall load, da es besser ausdrückt, was Sie erreichen möchten. Zum Beispiel:

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

Anstatt Pfade wie in einigen Antworten zu verketten, verwende ich File.expand_path:

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

Aktualisieren:

Anstatt zu verwenden, können File.dirnameSie Folgendes tun:

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

Wobei ..der Dateiname entfernt wird __FILE__.

Koen.
quelle
Dies scheint definitiv der File.expand_path
richtige
Ich ziehe diese Antwort definitiv der akzeptierten vor. Verschiedene Rails.root.joinAntworten funktionieren auch, wenn Sie in Schienen sind.
Nzifnab
14
Dir[File.join(__dir__, "/app/**/*.rb")].each do |file|
  require file
end

Dies funktioniert rekursiv auf Ihrem lokalen Computer und einer Fernbedienung (wie Heroku), die keine relativen Pfade verwendet.

Shushugah
quelle
8

In Rails können Sie Folgendes tun:

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

Update: Korrigiert mit dem Vorschlag von @Jiggneshh Gohel, Schrägstriche zu entfernen.

Dan Kohn
quelle
Da Rails.rootes sich um eine PathnameInstanz handelt, können Sie dies in jeder Ruby-Umgebung tun, nicht nur in Rails (NB Rails.root.join('lib/ext/*.rb')liest sich etwas
besser
Danke für die Empfehlung; Ich habe bearbeitet, um Ihren Kommentar aufzunehmen.
Dan Kohn
Die Verwendung eines Schrägstrichs (/) für Unterverzeichnisse unter Rails.root Rails.root.join('/lib')generiert beispielsweise keinen korrekten Pfad. Ich fand, dass dieses richtig funktioniert:Dir[Rails.root.join('lib', 'ext', '*.rb')].each { |file| require file }
Jignesh Gohel
@ Jiggneshh Gohel Ich habe Schrägstriche entfernt, wie Sie vorgeschlagen haben, danke.
Dan Kohn
3

Ich bin ein paar Jahre zu spät zur Party, aber ich mag diese einzeilige Lösung, mit der ich Schienen dazu gebracht habe, alles in App / Worker / Anliegen aufzunehmen:

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

Jazz
quelle
-2

Und was ist mit : require_relative *Dir['relative path']?

Aleksander
quelle
1
Erfordern relativ nimmt nicht mehr Dateien: ruby-doc.org/core-2.1.2/Kernel.html#method-i-require_relative
fabriciofreitag
OK, aber in meinem Beispiel ist es nicht. Das '*' ändert die Arität in 1. Es funktioniert als Mehrfachaufruf von require_relative.
Aleksander
1
Das '*' ändert die Arität in 1 - Was meinst du damit? require_relative *Dir['*.rb']Arbeit, wenn es nur ein Ruby-Skript gibt. Aber wenn mehrere Ruby-Skripte gefunden werden, erhalten Sierequire_relative': wrong number of arguments (4 for 1) (ArgumentError)
knut