A melhor maneira de exigir todos os arquivos de um diretório em ruby?

351

Qual é a melhor maneira de exigir todos os arquivos de um diretório em ruby?

Gaetan Dubar
fonte
Você pode criar um arquivo com o mesmo nome do diretório e solicitar todos os arquivos. O RSpec faz isso.
pedromanoel

Respostas:

462

E se:

Dir["/path/to/directory/*.rb"].each {|file| require file }
Sam Stokes
fonte
18
De acordo com o Pickaxe, a extensão .rb é opcional. Tecnicamente, ele muda o significado: "require 'foo.rb'" requer foo.rb, enquanto "require 'foo'" pode exigir foo.rb, foo.so ou foo.dll.
9339 Sam Stokes
28
Há uma pegadinha sutil para não retirar a extensão. Se alguma outra parte das chamadas de código exigir 'foo', o ruby ​​carregará o mesmo arquivo novamente, o que pode levar a erros espúrios. Eu adicionei minha própria resposta, que explica isso e mostra como remover a extensão.
Pete Hodgson
4
@Pete, isso ainda é verdade? Veja o comentário de Rene abaixo.
Andres Riofrio
5
Isso pode ser óbvio, mas vale a pena notar que a remoção do .rb também exigirá arquivos não-.rb no diretório, o que pode não ser o desejado.
user2398029
10
A sugestão de @ PeteHodgson é imprecisa. Ruby requirenão se confunde com a presença ou ausência da .rbextensão. Testado em ressonância magnética 1.8.7-p374, 2.1.5 e 2.2.0 testado. Essa lenda urbana vem do Rails, onde o carregamento automático "inteligente" exibia o comportamento que ele descreve em versões mais antigas (e ainda pode exibi-lo).
sheldonh
345

Se é um diretório relativo ao arquivo que requer (por exemplo, você deseja carregar todos os arquivos no diretório lib):

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

Editar: com base nos comentários abaixo, uma versão atualizada:

Dir[File.join(__dir__, 'lib', '*.rb')].each { |file| require file }
jandot
fonte
14
Você também pode adicionar todos os diretórios filho como esteDir[File.dirname(__FILE__) + '/support/**/*.rb'].each {|file| require file }
jspooner
62
Provavelmente é mais seguro usar File.join em vez de fazer suposições sobre barras frente / trás:Dir[File.join(File.dirname(__FILE__), 'lib', '*.rb')].each {|file| require file }
Chris
6
Há também require_relative
maasha
30
Se você estiver usando> = ruby ​​2.0, poderá usar em __dir__vez de File.dirname(__FILE__).
Christian Bankester
3
@maasha Como você costuma require_relativeexigir todos os arquivos em um diretório?
David Moles
101

Experimente a gema require_all:

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

Permite simplesmente:

require_all 'path/to/directory'

fonte
Eu precisava incluir todos os meus modelos do ActiveRecord, a gema require_all descobriu todas as dependências e as exigia perfeitamente. Obrigado!
panupan
2
@panupan Esteja ciente de que require_alla resolução de dependência cíclica soluciona um problema no seu código-fonte: você tem arquivos de origem Ruby que não requerem suas dependências. Isso fecha a porta no carregamento do bisturi, comprometendo-o a carregar tudo ou nada. Isso não é um problema em pequenas bibliotecas, mas é uma decisão que você deve tomar conscientemente.
sheldonh
Não faz sentido inchar seu aplicativo com gemas que você pode simplesmente substituir por uma linha de código. Isso aumenta o tempo de carregamento do seu aplicativo e induz mais erros a longo prazo.
Pere Joan Martorell
52
Dir[File.dirname(__FILE__) + '/../lib/*.rb'].each do |file| 
  require File.basename(file, File.extname(file))
end

Se você não remover a extensão, poderá acabar solicitando o mesmo arquivo duas vezes (o ruby ​​não perceberá que "foo" e "foo.rb" são o mesmo arquivo). Exigir o mesmo arquivo duas vezes pode levar a avisos falsos (por exemplo, "aviso: constante já inicializada").

Pete Hodgson
fonte
11
É este realmente o caso? A documentação diz: Um recurso não será carregado se o nome já aparecer em $ ". O nome do arquivo é convertido em um caminho absoluto; portanto," exige 'a'; require './a' "não carregará o a.rb duas vezes. ruby-doc.org/core/classes/Kernel.html#M001418
Derek
13
Meus testes mostram o mesmo que Derek disse: require "foo.rb"; require "foo";será carregado foo.rbapenas uma vez.
Rene Saarsoo
@ PeteHodgson- Você pode fazer backup disso?
Yarin
4
Não. Ruby requirenão se confunde com a presença ou ausência da .rbextensão. Testado na ressonância magnética 1.8.7-p374, 2.1.5 e 2.2.0. Essa lenda urbana vem do Rails, onde o carregamento automático "inteligente" exibia o comportamento descrito em versões mais antigas (e ainda pode exibi-lo).
sheldonh
45
Dir.glob(File.join('path', '**', '*.rb'), &method(:require))

ou, alternativamente, se você deseja definir o escopo dos arquivos para carregar em pastas específicas:

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

explicação:

Dir.glob aceita um bloco como argumento.

O método (: require) retornará o método require.

& method (: require) converterá o método em um bloco.

metakungfu
fonte
2
Este é um código bonito. Eu amo como não há blocos visíveis.
Nate Symer #
11
Dir.glob( File.join( File.dirname(__FILE__), '{lib,addons}', 'subfolder', '**', '*.rb' ), &method(:require) )elimina a dependência da plataforma (como '/' ou '\'). Funciona bem. Obrigado.
Ivan Black
30

A melhor maneira é adicionar o diretório ao caminho de carregamento e, em seguida, requireo nome da base de cada arquivo. Isso ocorre porque você deseja evitar exigir acidentalmente o mesmo arquivo duas vezes - geralmente não o comportamento pretendido. Se um arquivo será carregado ou não, depende se requireo caminho já foi passado antes. Por exemplo, esta sessão irb simples mostra que você pode exigir e carregar por engano o mesmo arquivo duas vezes.

$ 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

Observe que as duas primeiras linhas retornam, o que truesignifica que o mesmo arquivo foi carregado nas duas vezes. Quando caminhos são usados, mesmo que apontem para o mesmo local, requirenão se sabe que o arquivo já foi necessário.

Aqui, em vez disso, adicionamos um diretório ao caminho de carregamento e, em seguida, exigimos o nome da base de cada arquivo * .rb.

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

Se você não se importa com o arquivo ser solicitado mais de uma vez, ou sua intenção é apenas carregar o conteúdo do arquivo, talvez loadseja melhor usá-lo em vez derequire . Use load neste caso, porque expressa melhor o que você está tentando realizar. Por exemplo:

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

Em vez de concatenar caminhos, como em algumas respostas, eu uso File.expand_path:

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

Atualizar:

Em vez de usar, File.dirnamevocê pode fazer o seguinte:

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

Onde ..retira o nome do arquivo __FILE__.

Koen.
fonte
este parece definitivamente o caminho a percorrer, e mais até a resposta data, depois de tentar tudo o resto, +1 paraFile.expand_path
SuckerForMayhem
Definitivamente, prefiro essa resposta à aceita. Várias Rails.root.joinrespostas também funcionam se você estiver nos trilhos.
Nzifnab
14
Dir[File.join(__dir__, "/app/**/*.rb")].each do |file|
  require file
end

Isso funcionará recursivamente na sua máquina local e em um controle remoto (como o Heroku) que não usa caminhos relativos.

shushugah
fonte
8

No Rails, você pode fazer:

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

Atualização: corrigida com a sugestão de @Jiggneshh Gohel para remover barras.

Dan Kohn
fonte
Desde Rails.rooté um Pathnameexemplo, você pode fazer isso em qualquer ambiente Ruby, não apenas Trilhos (NB Rails.root.join('lib/ext/*.rb')lê um pouco mais agradável)
DMKE
Obrigado pela recomendação; Eu editei para incluir seu comentário.
Dan Kohn
Usar uma barra (/) para subdiretórios no Rails.root, por exemplo Rails.root.join('/lib'), não gera o caminho correto. Eu encontrei este para funcionar corretamente:Dir[Rails.root.join('lib', 'ext', '*.rb')].each { |file| require file }
Jignesh Gohel 17/07/2015
@Jiggneshh Gohel Eu removi barras como você sugeriu, obrigado.
Dan Kohn
3

Estou alguns anos atrasado para a festa, mas eu meio que gosto dessa solução de uma linha que costumava usar trilhos para incluir tudo no aplicativo / trabalhadores / preocupações:

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

Jazz
fonte
-2

E o que dizer require_relative *Dir['relative path']:?

Aleksander
fonte
11
Exigir relação não leva vários arquivos: ruby-doc.org/core-2.1.2/Kernel.html#method-i-require_relative
fabriciofreitag
OK, mas no meu exemplo não é. O '*' altera a aridade para 1. Ele funciona como uma chamada múltipla para require_relative.
Aleksander
11
O '*' altera a aridade para 1 - O que você quer dizer com isso? require_relative *Dir['*.rb']trabalho, se houver apenas um script ruby. Mas se vários scripts ruby ​​forem encontrados, você obtémrequire_relative': wrong number of arguments (4 for 1) (ArgumentError)
knut