Автозагрузка файлов lib в Rails 4

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

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

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

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

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

конфиг /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, но все они, похоже, имеют дело с соглашениями об именах и другими проблемами, о которых мне не нужно было беспокоиться before- Мои классы lib уже работали для загрузки по запросу, я просто хочу изменить это для загрузки при запуске. Какой правильный способ сделать это?

Ответ 1

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

  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/initializers/require.rb:

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

PS

Ответ 2

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

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

Ответ 3

Это может помочь кому-то вроде меня найти этот ответ при поиске решений о том, как 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 = "[email protected]"
    end
  end
end

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

Ответ 4

Используйте 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"

конец