web-dev-qa-db-fra.com

Chargement automatique des fichiers de lib dans Rails 4

J'utilise la ligne suivante dans un initialiseur pour charger automatiquement le code dans mon répertoire /lib pendant le développement:

config/initializers/custom.rb:

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

(de astuce rapide Rails 3: recharger automatiquement les dossiers de lib en mode de développement )

Cela fonctionne très bien, mais son utilisation en production est trop inefficace. Au lieu de charger des bibliothèques à chaque requête, je veux simplement les charger au démarrage. Le même blog a n autre article décrivant comment faire cela:

config/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/**/"]

Cependant, lorsque je passe à cela, même en développement, j'obtiens NoMethodErrors lorsque j'essaie d'utiliser les fonctions lib.

Exemple d'un de mes fichiers 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

L'appel de Time.milli_stamp lancera NoMethodError

Je me rends compte que d'autres ont répondu à des questions similaires sur SO, mais ils semblent tous traiter de conventions de dénomination et d'autres problèmes sur lesquels je n'avais pas à m'inquiéter auparavant- Mes classes de bibliothèque travaillé pour le chargement par requête, je veux juste le changer en - démarrage . Quelle est la bonne façon de faire cela?

225
Yarin

Je pense que cela peut résoudre votre problème:

  1. dans config/application.rb :

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

    et conservez la bonne convention d'appellation dans lib .

    dans lib/foo.rb :

    class Foo
    end
    

    dans lib/foo/bar.rb :

    class Foo::Bar
    end
    
  2. si vous voulez vraiment faire quelques patchs de singe dans un fichier comme lib/extensions.rb , vous pouvez le demander manuellement:

    dans config/initializers/require.rb :

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

P.S.

545
ifyouseewendy

Bien que cela ne réponde pas directement à la question, mais je pense que c'est une bonne alternative pour éviter complètement la question.

Pour éviter tous les tracas autoload_paths ou eager_load_paths, créez un répertoire "lib" ou "misc" sous le répertoire "app". Placez les codes comme vous le feriez normalement ici, et Rails chargera les fichiers de la même manière que la manière dont ils chargeront (et rechargeront) les fichiers de modèle.

33
lulalala

Cela pourrait aider quelqu'un comme moi qui trouve cette réponse à la recherche de solutions à la manière dont Rails gère le chargement de la classe ... J'ai constaté que je devais définir un module dont le nom correspond à mon nom de fichier, plutôt que de simplement définir une classe:

Dans le fichier lib/development_mail_interceptor.rb (Oui, j'utilise le code d'un 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

fonctionne, mais cela ne se charge pas si je n’avais pas mis la classe dans un module.

6
sameers

Utilisez config.to_prepare pour charger vos correctifs/extensions Monkey pour chaque requête en mode de développement.

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"

fin

0
Madx