¿La mejor manera de requerir todos los archivos de un directorio en ruby?

351

¿Cuál es la mejor manera de requerir todos los archivos de un directorio en ruby?

Gaetan Dubar
fuente
Puede crear un archivo con el mismo nombre del directorio y hacer que requiera todos los archivos. RSpec hace esto.
pedromanoel

Respuestas:

462

Qué tal si:

Dir["/path/to/directory/*.rb"].each {|file| require file }
Sam Stokes
fuente
18
Según Pickaxe, la extensión .rb es opcional. Técnicamente cambia el significado: "require 'foo.rb'" requiere foo.rb, mientras que "require 'foo'" podría requerir foo.rb, foo.so o foo.dll.
Sam Stokes
28
Hay un error sutil para no quitar la extensión. Si alguna otra parte de las llamadas de código requiere 'foo', ruby ​​volverá a cargar el mismo archivo, lo que puede provocar errores espurios. Agregué mi propia respuesta que explica eso y muestra cómo quitar la extensión.
Pete Hodgson
44
@Pete, ¿sigue siendo cierto? Ver el comentario de Rene a continuación.
Andres Riofrio
55
Esto puede ser obvio, pero vale la pena señalar que soltar el .rb también requerirá cualquier archivo que no sea .rb en el directorio, lo que podría no ser deseado.
user2398029
10
La sugerencia de @ PeteHodgson es inexacta. Ruby's requireno está confundido por la presencia o ausencia de la .rbextensión. Probado en MRI 1.8.7-p374, 2.1.5 y 2.2.0 probado. Esta leyenda urbana proviene de Rails, donde la carga automática "inteligente" exhibió el comportamiento que describe en versiones anteriores (y aún puede exhibirlo).
sheldonh
345

Si es un directorio relativo al archivo que requiere (por ejemplo, desea cargar todos los archivos en el directorio lib):

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

Editar: según los comentarios a continuación, una versión actualizada:

Dir[File.join(__dir__, 'lib', '*.rb')].each { |file| require file }
jandot
fuente
14
También puede agregar todos los directorios secundarios como esteDir[File.dirname(__FILE__) + '/support/**/*.rb'].each {|file| require file }
jspooner
6262
Probablemente sea más seguro usar File.join en lugar de hacer suposiciones sobre barras diagonales / hacia atrás:Dir[File.join(File.dirname(__FILE__), 'lib', '*.rb')].each {|file| require file }
Chris
66
También hay require_relative
maasha
30
Si está usando> = ruby ​​2.0, puede usarlo en __dir__lugar de File.dirname(__FILE__).
Christian Bankester
3
@maasha ¿Cómo utiliza require_relativepara requerir todos los archivos en un directorio?
David Moles
101

Prueba la gema require_all:

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

Te permite simplemente:

require_all 'path/to/directory'

fuente
Necesitaba incluir todos mis modelos de ActiveRecord, la gema require_all descubrió todas las dependencias y las requirió perfectamente. ¡Gracias!
panupan
2
@panupan Solo tenga en cuenta que require_allla resolución de dependencia cíclica funciona alrededor de un problema en su código fuente: tiene archivos fuente Ruby que no requieren sus dependencias. Esto cierra la puerta a la carga del bisturí, comprometiéndote a cargar todo o nada. Eso no es un problema en las bibliotecas pequeñas, pero es una decisión que debe tomar conscientemente.
sheldonh
No tiene sentido hinchar su aplicación con gemas que simplemente puede reemplazar con una línea de código. Esto aumenta el tiempo de carga de su aplicación e induce más errores a largo plazo.
Pere Joan Martorell
52
Dir[File.dirname(__FILE__) + '/../lib/*.rb'].each do |file| 
  require File.basename(file, File.extname(file))
end

Si no elimina la extensión, puede terminar requiriendo el mismo archivo dos veces (ruby no se dará cuenta de que "foo" y "foo.rb" son el mismo archivo). Requerir el mismo archivo dos veces puede generar advertencias falsas (por ejemplo, "advertencia: constante inicializada").

Pete Hodgson
fuente
11
¿Es este realmente el caso? La documentación dice: una característica no se cargará si su nombre ya aparece en $ ". El nombre del archivo se convierte en una ruta absoluta, por lo que" requiere 'a'; require './a' "no cargará a.rb dos veces. ruby-doc.org/core/classes/Kernel.html#M001418
Derek
13
Mi prueba muestra lo mismo que Derek dijo: require "foo.rb"; require "foo";se cargará foo.rbsolo una vez.
Rene Saarsoo
@ PeteHodgson- ¿Puedes respaldar esto?
Yarin
44
No. Ruby's requireno está confundido por la presencia o ausencia de la .rbextensión. Probado en MRI 1.8.7-p374, 2.1.5 y 2.2.0. Esta leyenda urbana proviene de Rails, donde la carga automática "inteligente" exhibió el comportamiento descrito en versiones anteriores (y aún puede exhibirlo).
sheldonh
45
Dir.glob(File.join('path', '**', '*.rb'), &method(:require))

o, alternativamente, si desea abarcar los archivos para cargar en carpetas específicas:

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

explicación:

Dir.glob toma un bloque como argumento.

method (: require) devolverá el método require.

& method (: require) convertirá el método en un bloque.

metakungfu
fuente
2
Este es un código hermoso. Me encanta cómo no hay bloques visibles.
Nate Symer
1
Dir.glob( File.join( File.dirname(__FILE__), '{lib,addons}', 'subfolder', '**', '*.rb' ), &method(:require) )elimina la dependencia de la plataforma (como '/' o '\'). Funciona bien. Gracias.
Ivan Black
30

La mejor manera es agregar el directorio a la ruta de carga y luego requireel nombre base de cada archivo. Esto se debe a que desea evitar accidentalmente requerir el mismo archivo dos veces, a menudo no el comportamiento previsto. Si un archivo se cargará o no depende de si requireha visto la ruta que se le ha pasado anteriormente. Por ejemplo, esta simple sesión irb muestra que puede requerir y cargar por error el mismo archivo dos veces.

$ 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

Tenga en cuenta que las dos primeras líneas vuelven, lo que truesignifica que el mismo archivo se cargó las dos veces. Cuando se utilizan rutas, incluso si las rutas apuntan a la misma ubicación, requireno sabe que el archivo ya era necesario.

Aquí, en cambio, agregamos un directorio a la ruta de carga y luego requerimos el nombre base de cada archivo * .rb dentro.

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

Si no le importa que se requiera el archivo más de una vez, o si su intención es solo cargar el contenido del archivo, quizás loaddebería usarse en lugar de hacerlo require. Use carga en este caso, porque expresa mejor lo que está tratando de lograr. Por ejemplo:

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

En lugar de concatenar rutas como en algunas respuestas, uso File.expand_path:

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

Actualizar:

En lugar de usar File.dirname, podría hacer lo siguiente:

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

Donde ..elimina el nombre de archivo de __FILE__.

Koen
fuente
Este parece ser definitivamente el camino a seguir, y la respuesta más actualizada, después de intentar todo lo demás, +1 paraFile.expand_path
SuckerForMayhem
Definitivamente prefiero esta respuesta a la aceptada. Varias Rails.root.joinrespuestas también funcionan si estás en rieles.
nzifnab
14
Dir[File.join(__dir__, "/app/**/*.rb")].each do |file|
  require file
end

Esto funcionará de forma recursiva en su máquina local y en un control remoto (como Heroku) que no utiliza rutas relativas.

shushugah
fuente
8

En Rails, puedes hacer:

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

Actualización: corregido con la sugerencia de @Jiggneshh Gohel para eliminar las barras.

Dan Kohn
fuente
Como Rails.rootes una Pathnameinstancia, puede hacer esto en cualquier entorno de Ruby, no solo en Rails (NB Rails.root.join('lib/ext/*.rb')lee un poco mejor)
DMKE
Gracias por la recomendación; Edité para incluir tu comentario.
Dan Kohn
El uso de una barra diagonal (/) para subdirectorios en Rails.root, por ejemplo Rails.root.join('/lib'), no genera la ruta correcta. Encontré que este funcionaba correctamente:Dir[Rails.root.join('lib', 'ext', '*.rb')].each { |file| require file }
Jignesh Gohel
@Jiggneshh Gohel Eliminé las barras como usted sugirió, gracias.
Dan Kohn el
3

Llegué unos años tarde a la fiesta, pero me gusta esta solución de una línea que solía tener rieles para incluir todo en aplicaciones / trabajadores / preocupaciones:

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

Jazz
fuente
-2

¿Y qué hay de require_relative *Dir['relative path']:?

Aleksander
fuente
1
Requerir que el pariente no tome varios archivos: ruby-doc.org/core-2.1.2/Kernel.html#method-i-require_relative
fabriciofreitag
OK, pero en mi ejemplo no lo es. El '*' cambia la aridad a 1. Funciona como llamada múltiple a require_relative.
Aleksander
1
El '*' cambia arity a 1 - ¿Qué quieres decir con eso? require_relative *Dir['*.rb']trabajo, si solo hay un script ruby. Pero si se encuentran varias secuencias de comandos de rubí, se obtienerequire_relative': wrong number of arguments (4 for 1) (ArgumentError)
Knut