Лучший способ требовать все файлы из каталога в ruby?

351

Каков наилучший способ требовать все файлы из каталога в ruby?

Гаэтан дубар
источник
Вы можете создать файл с тем же именем каталога, и для него требуются все файлы. RSpec делает это.
Pedromanoel

Ответы:

462

Как насчет:

Dir["/path/to/directory/*.rb"].each {|file| require file }
Сэм Стоукс
источник
18
Согласно кирке, расширение .rb не является обязательным. Технически это меняет значение: «require 'foo.rb'» требует foo.rb, тогда как «require 'foo'» может потребовать foo.rb, foo.so или foo.dll.
Сэм Стоукс
28
Есть тонкое замечание, чтобы не снимать расширение. Если какая-то другая часть вызовов кода требует 'foo', тогда ruby ​​снова загрузит тот же файл, что может привести к ложным ошибкам. Я добавил свой собственный ответ, который объясняет это и показывает, как удалить расширение.
Пит Ходжсон
4
@ Пит, это все еще правда? Смотрите комментарий Рене ниже.
Андрес Риофрио
5
Это может быть очевидно, но стоит отметить, что для удаления .rb также потребуются любые файлы не-.rb в каталоге, что может быть нежелательно.
user2398029
10
Предложение @ PeteHodgson является неточным. Ruby's requireне смущает наличие или отсутствие .rbрасширения. Проверено на МРТ 1.8.7-p374, 2.1.5 и 2.2.0. Эта городская легенда происходит от Rails, где «умная» автозагрузка демонстрирует поведение, которое он описывает в более старых версиях (и может все еще демонстрировать его).
Шелдон
345

Если это каталог относительно файла, который требует (например, вы хотите загрузить все файлы в каталоге lib):

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

Изменить: на основе комментариев ниже, обновленная версия:

Dir[File.join(__dir__, 'lib', '*.rb')].each { |file| require file }
jandot
источник
14
Вы также можете добавить все дочерние каталоги, как этоDir[File.dirname(__FILE__) + '/support/**/*.rb'].each {|file| require file }
jspooner
62
Вероятно, безопаснее использовать File.join, чем делать предположения о прямом / обратном слэше:Dir[File.join(File.dirname(__FILE__), 'lib', '*.rb')].each {|file| require file }
Крис
6
Также есть require_relative
Мааша
30
Если вы используете> = ruby ​​2.0, вы можете использовать __dir__вместо File.dirname(__FILE__).
Кристиан Банкестер
3
@maasha Как вы используете, require_relativeчтобы требовать все файлы в каталоге?
Дэвид Моулз
101

Попробуйте гем require_all:

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

Это позволяет вам просто:

require_all 'path/to/directory'

источник
Мне нужно было включить все мои модели ActiveRecord, гем require_all выяснил все зависимости и отлично их потребовал. Спасибо!
Панупан
2
@panupan Просто имейте в виду, что require_allразрешение циклических зависимостей решает проблему в вашем исходном коде: у вас есть исходные файлы Ruby, которые не требуют своих зависимостей. Это закрывает дверь при загрузке скальпеля, заставляя вас загружать все или ничего. Это не проблема в небольших библиотеках, но это решение, которое вы должны принимать осознанно.
Шелдон
Нет смысла раздувать ваше приложение гемами, которые вы можете просто заменить строкой кода. Это увеличивает время загрузки вашего приложения и вызывает больше ошибок в долгосрочной перспективе.
Пере Джоан Марторел
52
Dir[File.dirname(__FILE__) + '/../lib/*.rb'].each do |file| 
  require File.basename(file, File.extname(file))
end

Если вы не удалите расширение, вам может понадобиться один и тот же файл дважды (ruby не поймет, что «foo» и «foo.rb» - это один и тот же файл). Двойной запрос одного и того же файла может привести к ложным предупреждениям (например, «предупреждение: уже инициализированная константа»).

Пит Ходжсон
источник
11
Это действительно так? Документация гласит: функция не будет загружена, если ее имя уже отображается в $ ". Имя файла преобразуется в абсолютный путь, поэтому" require 'a'; require './a' "не будет загружать a.rb дважды. ruby-doc.org/core/classes/Kernel.html#M001418
Дерек
13
Мое тестирование показывает то же, что сказал Дерек: require "foo.rb"; require "foo";загрузится foo.rbтолько один раз.
Рене Саарсоо
@ PeteHodgson- Ты можешь это подтвердить?
Ярин
4
Нет. Ruby's requireне смущает наличие или отсутствие .rbрасширения. Протестировано на МРТ 1.8.7-p374, 2.1.5 и 2.2.0. Эта городская легенда происходит от Rails, где «умная» автозагрузка демонстрировала поведение, описанное в более старых версиях (и может все еще демонстрировать его).
Шелдон
45
Dir.glob(File.join('path', '**', '*.rb'), &method(:require))

или, в качестве альтернативы, если вы хотите разместить файлы для загрузки в определенные папки:

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

объяснение:

Dir.glob принимает блок в качестве аргумента.

Метод (: require) вернет метод require.

& method (: require) преобразует метод в блок.

metakungfu
источник
2
Это красивый код. Мне нравится, как нет видимых блоков.
Нейт Симер,
1
Dir.glob( File.join( File.dirname(__FILE__), '{lib,addons}', 'subfolder', '**', '*.rb' ), &method(:require) )устраняет зависимость от платформы (например, «/» или «\»). Работает хорошо. Спасибо.
Иван Блэк
30

Лучший способ - добавить каталог в путь загрузки, а затем requireуказать базовое имя каждого файла. Это потому, что вы хотите избежать случайного запроса одного и того же файла дважды - часто это не является предполагаемым поведением. Будет ли файл загружен или нет, зависит от того require, видел ли ранее переданный ему путь. Например, этот простой сеанс irb показывает, что вы можете по ошибке потребовать и загрузить один и тот же файл дважды.

$ 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

Обратите внимание, что первые две строки возвращают, что trueозначает, что один и тот же файл был загружен оба раза. Когда используются пути, даже если пути указывают на одно и то же местоположение, requireон не знает, что файл уже был необходим.

Здесь вместо этого мы добавляем каталог в путь загрузки, а затем запрашиваем базовое имя каждого файла * .rb внутри.

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

Если вам не нужно, чтобы файл требовался более одного раза, или вы просто хотите загрузить содержимое файла, возможно, loadследует использовать вместо require. В этом случае используйте load, потому что она лучше отражает то, что вы пытаетесь выполнить. Например:

Dir["/path/to/directory/*.rb"].each {|file| load file }
Райан МакГири
источник
14

Вместо объединения путей, как в некоторых ответах, я использую File.expand_path:

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

Обновить:

Вместо использования File.dirnameвы можете сделать следующее:

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

Где ..полосы имя файла __FILE__.

Koen.
источник
это, безусловно, правильный путь, и самый актуальный ответ, после попытки всех остальных, +1 заFile.expand_path
SuckerForMayhem
Я определенно предпочитаю этот ответ принятому. Различные Rails.root.joinответы также работают, если вы находитесь в рельсах.
nzifnab
14
Dir[File.join(__dir__, "/app/**/*.rb")].each do |file|
  require file
end

Это будет работать рекурсивно на вашем локальном компьютере и удаленном компьютере (например, Heroku), который не использует относительные пути.

shushugah
источник
8

В Rails вы можете сделать:

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

Обновление: исправлено с предложением @Jiggneshh Gohel для удаления слешей.

Дэн Кон
источник
Поскольку Rails.rootэто Pathnameпример, вы можете сделать это в любой среде Ruby, а не только в Rails (NB Rails.root.join('lib/ext/*.rb')читает немного лучше)
DMKE
Спасибо за рекомендацию; Я отредактировал, чтобы включить ваш комментарий.
Дэн Кон
Использование косой черты (/) для подкаталогов в Rails.root, например Rails.root.join('/lib'), не генерирует правильный путь. Я нашел, что это работает правильно:Dir[Rails.root.join('lib', 'ext', '*.rb')].each { |file| require file }
Jignesh Gohel
@Jiggneshh Gohel Я удалил косые черты, как ты и предложил, спасибо.
Дэн Кон,
3

Я опоздал на вечеринку на несколько лет, но мне нравится это однострочное решение, которое я использовал, чтобы получить рельсы для включения всего в приложение / работники / проблемы:

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

Джаз
источник
-2

А как насчет require_relative *Dir['relative path']?

Александр
источник
1
Обязательный родственник не принимает несколько файлов: ruby-doc.org/core-2.1.2/Kernel.html#method-i-require_relative
fabriciofreitag
ОК, но в моем примере это не так. '*' Изменяет арность на 1. Он работает как множественный вызов require_relative.
Александр
1
«*» Меняет арность на 1 - что вы имеете в виду? require_relative *Dir['*.rb']работать, если есть только один ruby-скрипт. Но если найдено несколько скриптов ruby, вы получитеrequire_relative': wrong number of arguments (4 for 1) (ArgumentError)
knut