Chargement automatique des fichiers lib dans Rails 4

229

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

config / initializers / custom.rb:

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

(à partir de Rails 3 Quicktip: Rechargement automatique des dossiers lib en mode développement )

Cela fonctionne très bien, mais il est trop inefficace pour être utilisé en production. Au lieu de charger des bibliothèques à chaque demande, je veux juste les charger au démarrage. Le même blog contient un autre article décrivant comment procéder:

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 cours de 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 Time.milli_stamplancera NoMethodError

Je me rends compte que d'autres ont répondu à des questions similaires sur SO, mais ils semblent tous traiter des conventions de dénomination et d'autres problèmes dont je n'avais pas à m'inquiéter auparavant - Mes classes de lib ont déjà fonctionné pour le chargement par demande, je veux juste le changer au chargement par démarrage . Quelle est la bonne façon de procéder?

Yarin
la source
Le dossier config / initializers est-il automatiquement chargé au démarrage d'une application Rails?
Jwan622

Réponses:

548

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

  1. dans config / application.rb :

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

    et conserver la bonne convention de dénomination dans lib .

    dans lib / foo.rb :

    class Foo
    end
    

    dans lib / foo / bar.rb :

    class Foo::Bar
    end
    
  2. si vous voulez vraiment faire des correctifs de singe dans un fichier comme lib / extensions.rb , vous pouvez l'exiger manuellement:

    dans config / initializers / require.rb :

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

PS

ifyouseewendy
la source
1
@ ifyouseewendy - Vous avez parfaitement raison - car extensions.rb ne suivait pas les conventions de dénomination de Rails, Rails ne l'inclurait pas dans le processus de chargement. Je l'ai fait fonctionner en l'exigeant manuellement.
Yarin
@ifyouseewendy comment puis-je inclure des fichiers avant le chargement des modèles? ajouter le chemin vers le chargement automatique, c'est cool, mais comment contrôler l'ordre d'inclusion? thx
Matrix
@Matrix "inclure les fichiers avant le chargement des modèles", vous pouvez avoir besoin manuellement de votre fichier sans utiliser la fonction de chargement automatique.
ifyouseewendy
@ifyouseewendy si j'en ai besoin dans l'initialiseur mais que le fichier est dans autoload_path, il sera rechargé (chargé 2 fois) ou pas? theire est un "require_once" comme en php?
Matrix
5
Cela ne semble pas fonctionner dans l'API Rails 5 en production (mais fonctionne en développement). Je crois que vous devez utiliser config.eager_load_paths << Rails.root.join('lib'). Cependant, cela a un inconvénient majeur en ce qu'il eager_load_pathscharge également tout dans les tâches. Je pense que la solution de lulalala est meilleure. Voici un article de blog avec plus de détails: blog.arkency.com/2014/11/…
hirowatari
33

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 tout autoload_pathsou eager_load_pathstracas, créez un répertoire "lib" ou "misc" sous le répertoire "app". Placez les codes comme vous le feriez normalement, et Rails chargera les fichiers comme il chargera (et rechargera) les fichiers de modèle.

lulalala
la source
3
Je suis dans Rails 4.2. et il ne charge pas automatiquement les fichiers sous app, je dois le faire manuellement ...... ou besoin de le mettre dans le chemin d'auto-chargement ..
Arup Rakshit
6
Vous vous trompez, Arup, tous les sous-répertoires du répertoire d'application sont automatiquement dans le tableau autoload_paths dans Rails 4.2 .
Dr.Strangelove
Sauf le app/viewsrépertoire qui n'est pas ajouté; ou plutôt est explicitement supprimé.
James B.Byrne
1
Très bonne réponse. La seule chose qui a fonctionné pour moi sur les rails 5 / api.
jstafford
6
N'oubliez pas que cela libest destiné au code qui peut être appliqué à plusieurs projets et qui peut éventuellement être extrait vers un joyau. Si ce n'est pas le cas, créez un dossier plus approprié sous la recherche d'application en tant que services/ou presenters/et même sous-dérivez-les.
PhilT
6

Cela pourrait aider quelqu'un comme moi qui trouve cette réponse lors de la recherche de solutions sur la façon dont Rails gère le chargement de la classe ... J'ai trouvé que je devais définir un moduledont le nom correspondait à mon nom de fichier de manière appropriée, 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 il ne se charge pas si je n'avais pas mis la classe dans un module.

même
la source
1
En ruby, "correspondance appropriée" signifie que le fichier se trouve dans le système de fichiers à LOAD_PATH/module/class.rb(souligné) où se LOAD_PATHtrouve le chemin de chargement utilisé par l'application Ruby (autoload_paths dans le cas de Rails). libest passé du chargement automatique par Rails au non chargement automatique, et dans les versions récentes (> = Rails 3.x), il n'est pas chargé automatiquement. Quelle que soit la magie qui rend ce travail pour vous n'est pas recommandée. C'est peut-être un vieux Railscast?
Peter H. Boling du
0

Utilisez config.to_prepare pour charger vos correctifs / extensions de singe pour chaque demande en mode 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

Madx
la source