Автоматическая загрузка файлов lib в Rails 4

229

Я использую следующую строку в инициализаторе для автозагрузки кода в моем /libкаталоге во время разработки:

конфиг / Инициализаторы / custom.rb:

RELOAD_LIBS = Dir[Rails.root + 'lib/**/*.rb'] if Rails.env.development?

(из Rails 3 Quicktip: автоматическая перезагрузка папок lib в режиме разработки )

Он отлично работает, но слишком неэффективен для использования в производстве. Вместо того, чтобы загружать библиотеки при каждом запросе, я просто хочу загрузить их при запуске. В этом же блоге есть еще одна статья, описывающая, как это сделать:

конфиг / application.rb:

# Custom directories with classes and modules you want to be autoloadable.
config.autoload_paths += %W(#{config.root}/lib)
config.autoload_paths += Dir["#{config.root}/lib/**/"]

Однако, когда я переключаюсь на это, даже в процессе разработки, я получаю NoMethodErrors при попытке использовать функции lib.

Пример одного из моих файлов lib:

Библиотека / extensions.rb:

Time.class_eval do
  def self.milli_stamp
    Time.now.strftime('%Y%m%d%H%M%S%L').to_i
  end
end

При Time.milli_stampвызове выбрасывается NoMethodError

Я понимаю, что другие отвечали на аналогичные вопросы по SO, но все они, похоже, имеют дело с соглашениями об именах и другими проблемами, о которых мне раньше не приходилось беспокоиться - мои классы lib уже работали для загрузки по запросу, я просто хочу изменить это для загрузки при запуске . Какой правильный способ сделать это?

Ярина
источник
Папка config / initializers автоматически загружается при запуске приложения Rails?
Jwan622

Ответы:

548

Я думаю, что это может решить вашу проблему:

  1. в config / application.rb :

    config.autoload_paths << Rails.root.join('lib')

    и придерживайтесь правильного соглашения об именах в lib .

    в lib / foo.rb :

    class Foo
    end

    в lib / foo / bar.rb :

    class Foo::Bar
    end
  2. если вы действительно хотите сделать какие-то обезьяньи патчи в файле, например lib / extensions.rb , вам может потребоваться это вручную:

    в config / initializer / require.rb :

    require "#{Rails.root}/lib/extensions" 

PS

ifyouseewendy
источник
1
@ ifyouseewendy - Вы совершенно правы - поскольку extensions.rb не следовал соглашениям об именах Rails, Rails не включил бы его в процесс загрузки. Я получил это, работая вручную, требуя этого.
Ярин
@ifyouseewendy, как я могу включить файлы перед загрузкой моделей? добавить путь к автозагрузке это круто, но как контролировать порядок включения? THX
Matrix
@Matrix "включить файлы до загрузки моделей", вы можете вручную запросить файл без использования функции автозагрузки.
ifyouseewendy
@ifyouseewendy, если мне потребуется это в инициализаторе, но файл находится в autoload_path, он будет перезагружен (загружен 2 раза) или нет? Theire является "require_once", как в PHP?
Матрица
5
Похоже, что это не работает в Rails 5 API в производстве (но в разработке). Я считаю, что вам нужно использовать config.eager_load_paths << Rails.root.join('lib'). Однако, это имеет большой недостаток в том, что eager_load_pathsзагружает все и в задачи. Я думаю, что решение Лулалала лучше. Вот сообщение в блоге с более подробной информацией: blog.arkency.com/2014/11/…
hirowatari
33

Хотя это не дает прямого ответа на вопрос, но я думаю, что это хорошая альтернатива, чтобы вообще избежать вопроса.

Чтобы избежать всех хлопот autoload_pathsили eager_load_pathsпроблем, создайте каталог «lib» или «misc» в каталоге «app». Размещайте коды, как обычно, и Rails будет загружать файлы так же, как он будет загружать (и перезагружать) файлы модели.

lulalala
источник
3
Я в Rails 4.2. и он не загружает файлы автоматически app, мне нужно сделать это вручную ...... или нужно поместить его в путь автозагрузки ..
Arup Rakshit
6
Вы не правы, Arup, все подкаталоги каталога приложения автоматически находятся в массиве autoload_paths в Rails 4.2. См. Edgeguides.rubyonrails.org/…
Dr.Strangelove
Исключая app/viewsкаталог, который не добавляется; или, скорее, явно удаляется.
Джеймс Б. Бирн
1
Отличный ответ. Единственное что у меня работало на рельсах 5 / api.
Jstafford
6
Просто помните, что libон предназначен для кода, который может быть применен к нескольким проектам и может быть извлечен в гем. Если нет, создайте более подходящую папку под приложением, ищите как services/или presenters/даже подкаталоги из них.
PhilT
6

Это может помочь кому-то вроде меня, который найдет этот ответ при поиске решений о том, как Rails обрабатывает загрузку классов ... Я обнаружил, что мне нужно было определить, moduleчье имя соответствовало моему имени файла, а не просто определить класс:

В файле lib / development_mail_interceptor.rb (да, я использую код из Railscast :))

module DevelopmentMailInterceptor
  class DevelopmentMailInterceptor
    def self.delivering_email(message)
      message.subject = "intercepted for: #{message.to} #{message.subject}"
      message.to = "myemail@mydomain.org"
    end
  end
end

работает, но не загружается, если бы я не поместил класс в модуль.

sameers
источник
1
В ruby ​​«подходящее соответствие» означает, что файл находится в файловой системе в LOAD_PATH/module/class.rb(подчеркнутом) месте LOAD_PATHв путях загрузки, используемых приложением Ruby (autoload_paths в случае Rails). libколебался от автоматической загрузки Rails до отсутствия автоматической загрузки, а в последних версиях (> = Rails 3.x) он не загружался автоматически. Какая бы магия ни делала для вас эту работу, не рекомендуется. Возможно, это старый Railscast?
Питер Х. Болинг
0

Используйте config.to_prepare для загрузки патчей / расширений обезьян для каждого запроса в режиме разработки.

config.to_prepare do |action_dispatcher|
 # More importantly, will run upon every request in development, but only once (during boot-up) in production and test.
 Rails.logger.info "\n--- Loading extensions for #{self.class} "
 Dir.glob("#{Rails.root}/lib/extensions/**/*.rb").sort.each do |entry|
   Rails.logger.info "Loading extension(s): #{entry}"
   require_dependency "#{entry}"
 end
 Rails.logger.info "--- Loaded extensions for #{self.class}\n"

конец

Madx
источник