La meilleure façon d'exiger tous les fichiers d'un répertoire en ruby?


351

Quelle est la meilleure façon d'exiger tous les fichiers d'un répertoire dans ruby?


Vous pouvez créer un fichier portant le même nom que le répertoire et lui demander tous les fichiers. RSpec fait cela.
pedromanoel

Réponses:


462

Que diriez-vous:

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

18
Selon le Pickaxe, l'extension .rb est facultative. Techniquement, cela change la signification: "require 'foo.rb'" requires foo.rb, alors que "require 'foo'" pourrait nécessiter foo.rb, foo.so ou foo.dll.
Sam Stokes

28
Il y a un subtil piège à ne pas supprimer l'extension. Si une autre partie des appels de code nécessite «foo», ruby ​​chargera à nouveau le même fichier, ce qui peut entraîner des erreurs parasites. J'ai ajouté ma propre réponse qui explique cela et montre comment supprimer l'extension.
Pete Hodgson

4
@Pete, est-ce toujours vrai? Voir le commentaire de René ci-dessous.
Andres Riofrio

5
Cela peut être évident, mais il vaut la peine de noter que la suppression du .rb nécessitera également tous les fichiers non .rb dans le répertoire, ce qui peut ne pas être souhaité.
user2398029

10
La suggestion de @ PeteHodgson est inexacte. Ruby's requiren'est pas confondu par la présence ou l'absence de l' .rbextension. Testé sur IRM 1.8.7-p374, 2.1.5 et 2.2.0 testé. Cette légende urbaine vient de Rails, où le chargement automatique "intelligent" a montré le comportement qu'il décrit dans les anciennes versions (et peut toujours l'exposer).
sheldonh

345

S'il s'agit d'un répertoire relatif au fichier qui fait l'exigence (par exemple, vous voulez charger tous les fichiers dans le répertoire lib):

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

Modifier: sur la base des commentaires ci-dessous, une version mise à jour:

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

14
Vous pouvez également ajouter tous les répertoires enfants comme celui-ciDir[File.dirname(__FILE__) + '/support/**/*.rb'].each {|file| require file }
jspooner

62
Il est probablement plus sûr d'utiliser File.join plutôt que de faire des hypothèses sur les barres obliques avant / arrière:Dir[File.join(File.dirname(__FILE__), 'lib', '*.rb')].each {|file| require file }
Chris

6
Il y a aussi require_relative
maasha

30
Si vous utilisez> = ruby ​​2.0, vous pouvez utiliser à la __dir__place de File.dirname(__FILE__).
Christian Bankester

3
@maasha Comment utilisez-vous require_relativepour exiger tous les fichiers d'un répertoire?
David Moles

101

Essayez la gemme require_all:

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

Il vous permet simplement:

require_all 'path/to/directory'

J'avais besoin d'inclure tous mes modèles ActiveRecord, la gemme require_all a compris toutes les dépendances et les a parfaitement requises. Merci!
panupan

2
@panupan Sachez simplement que require_allla résolution des dépendances cycliques contourne un problème dans votre code source: vous avez des fichiers source Ruby qui ne nécessitent pas leurs dépendances. Cela ferme la porte au chargement du scalpel, vous engageant au chargement tout ou rien. Ce n'est pas un problème dans les petites bibliothèques, mais c'est une décision que vous devriez prendre consciemment.
sheldonh

Cela n'a pas de sens de gonfler votre application avec des gemmes que vous pouvez simplement remplacer par une ligne de code. Cela augmente le temps de chargement de votre application et induit plus de bugs à long terme.
Pere Joan Martorell

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

Si vous ne supprimez pas l'extension, vous pourriez finir par exiger le même fichier deux fois (ruby ne réalisera pas que "foo" et "foo.rb" sont le même fichier). Le fait d'exiger deux fois le même fichier peut entraîner de faux avertissements (par exemple "avertissement: constante déjà initialisée").


11
Est-ce vraiment le cas? La documentation dit: Une fonctionnalité ne sera pas chargée si son nom apparaît déjà dans $ ". Le nom du fichier est converti en un chemin absolu, donc" require 'a'; require './a' "ne chargera pas a.rb deux fois. ruby-doc.org/core/classes/Kernel.html#M001418
Derek

13
Mes tests montrent la même chose que Derek a dit: require "foo.rb"; require "foo";ne se chargera foo.rbqu'une seule fois.
Rene Saarsoo

@ PeteHodgson- Pouvez-vous sauvegarder cela?
Yarin

4
Non. Ruby requiren'est pas dérouté par la présence ou l'absence de l' .rbextension. Testé sur IRM 1.8.7-p374, 2.1.5 et 2.2.0. Cette légende urbaine vient de Rails, où le chargement automatique "intelligent" a montré le comportement décrit dans les anciennes versions (et peut encore l'exposer).
sheldonh

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

ou bien, si vous souhaitez étendre les fichiers à charger dans des dossiers spécifiques:

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

explication:

Dir.glob prend un bloc comme argument.

La méthode (: require) renverra la méthode require.

& method (: require) convertira la méthode en bloc.


2
Ceci est un beau code. J'adore la façon dont il n'y a pas de blocs visibles.
Nate Symer

1
Dir.glob( File.join( File.dirname(__FILE__), '{lib,addons}', 'subfolder', '**', '*.rb' ), &method(:require) )élimine la dépendance à la plate-forme (comme «/» ou «\»). Fonctionne bien. Merci.
Ivan Black

30

La meilleure façon est d'ajouter le répertoire au chemin de chargement puis requirele nom de base de chaque fichier. En effet, vous voulez éviter d'exiger accidentellement le même fichier deux fois - souvent pas le comportement souhaité. Le chargement ou non d'un fichier dépend du fait que requirele chemin lui a déjà été transmis auparavant. Par exemple, cette simple session IRB montre que vous pouvez par erreur exiger et charger deux fois le même fichier.

$ 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

Notez que les deux premières lignes renvoient truece qui signifie que le même fichier a été chargé les deux fois. Lorsque des chemins sont utilisés, même si les chemins pointent vers le même emplacement, requirene sait pas que le fichier était déjà requis.

Ici, à la place, nous ajoutons un répertoire au chemin de chargement et demandons ensuite le nom de base de chaque fichier * .rb qu'il contient.

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

Si vous ne vous souciez pas que le fichier soit requis plusieurs fois, ou si votre intention est simplement de charger le contenu du fichier, vous loaddevriez peut -être utiliser à la place de require. Utilisez la charge dans ce cas, car elle exprime mieux ce que vous essayez d'accomplir. Par exemple:

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

14

Au lieu de concaténer des chemins comme dans certaines réponses, j'utilise File.expand_path:

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

Mise à jour:

Au lieu d'utiliser, File.dirnamevous pouvez effectuer les opérations suivantes:

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

..supprime le nom de fichier de __FILE__.


cela semble définitivement la voie à suivre, et la réponse la plus à jour, après avoir essayé tout le reste, +1 pourFile.expand_path
SuckerForMayhem

Je préfère définitivement cette réponse à la réponse acceptée. Diverses Rails.root.joinréponses fonctionnent également si vous êtes dans les rails.
nzifnab

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

Cela fonctionnera récursivement sur votre machine locale et une télécommande (comme Heroku) qui n'utilise pas de chemins relatifs.


8

Dans Rails, vous pouvez faire:

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

Mise à jour: corrigé avec la suggestion de @Jiggneshh Gohel pour supprimer les barres obliques.


Puisque Rails.rootc'est une Pathnameinstance, vous pouvez le faire dans n'importe quel environnement Ruby, pas seulement Rails (NB Rails.root.join('lib/ext/*.rb')est un peu plus agréable)
DMKE

Merci pour la recommandation; J'ai modifié pour inclure votre commentaire.
Dan Kohn

Utiliser une barre oblique (/) pour les sous-répertoires sous Rails.root, par exemple, Rails.root.join('/lib')ne génère pas le chemin correct. J'ai trouvé que celui-ci fonctionnait correctement:Dir[Rails.root.join('lib', 'ext', '*.rb')].each { |file| require file }
Jignesh Gohel

@Jiggneshh Gohel J'ai supprimé les barres obliques comme vous l'avez suggéré, merci.
Dan Kohn

3

J'ai quelques années de retard pour la fête, mais j'aime un peu cette solution à une ligne que j'utilisais pour que les rails incluent tout dans l'application / les travailleurs / les préoccupations:

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


-2

Et qu'en est-il de require_relative *Dir['relative path']:?


1
Exiger que le parent ne prenne pas plusieurs fichiers: ruby-doc.org/core-2.1.2/Kernel.html#method-i-require_relative
fabriciofreitag

D'accord, mais dans mon exemple, ce n'est pas le cas. Le '*' change l'arité en 1. Il fonctionne comme un appel multiple à require_relative.
Aleksander

1
Le '*' change l'arité en 1 - Que voulez-vous dire par là? require_relative *Dir['*.rb']fonctionne, s'il n'y a qu'un seul ruby-script. Mais si plusieurs scripts rubis sont trouvés, vous obtenezrequire_relative': wrong number of arguments (4 for 1) (ArgumentError)
knut
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.