La meilleure façon de charger le module / classe à partir du dossier lib dans Rails 3?

273

Étant donné que la dernière version de Rails 3 n'est plus à chargement automatique des modules et des classes de lib, quelle serait la meilleure façon de les charger?

De github:

A few changes were done in this commit:

Do not autoload code in *lib* for applications (now you need to explicitly 
require them). This makes an application behave closer to an engine 
(code in lib is still autoloaded for plugins);
Vincent
la source

Réponses:

251

Depuis Rails 2.3.9 , il existe un paramètre dans config/application.rblequel vous pouvez spécifier les répertoires contenant les fichiers que vous souhaitez charger automatiquement.

Depuis application.rb:

# Custom directories with classes and modules you want to be autoloadable.
# config.autoload_paths += %W(#{config.root}/extras)
Slobodan Kovacevic
la source
7
Notez également la réponse de @ thanksful si vous cherchez à charger automatiquement tout le sous-arbre de app/lib.
Tom Harrison
199
# Autoload lib/ folder including all subdirectories
config.autoload_paths += Dir["#{config.root}/lib/**/"]

Source: Rails 3 Quicktip: Autoload lib directory incluant tous les sous-répertoires, évitez le chargement paresseux

Veuillez noter que les fichiers contenus dans le dossier lib ne sont chargés qu'au démarrage du serveur. Si vous souhaitez avoir la possibilité de recharger automatiquement ces fichiers, lisez: Rails 3 Quicktip: Recharger automatiquement les dossiers lib en mode développement . Sachez que cela n'est pas destiné à un environnement de production car le rechargement permanent ralentit la machine.

reconnaissant
la source
Les liens sont morts
Besi
84

La magie du chargement automatique

Je pense que l'option contrôlant les dossiers à partir desquels se fait le chargement automatique a été suffisamment couverte dans d'autres réponses. Cependant, dans le cas où quelqu'un d'autre a des problèmes de chargement alors qu'ils ont vu leurs chemins de chargement automatique modifiés comme requis, cette réponse essaie d'expliquer quelle est la magie derrière ce chargement automatique.

Donc, quand il s'agit de charger des choses à partir de sous-répertoires, il y a un piège ou une convention que vous devez connaître. Parfois, la magie Ruby / Rails (cette fois principalement Rails) peut rendre difficile de comprendre pourquoi quelque chose se passe. Tout module déclaré dans les chemins de chargement automatique ne sera chargé que si le nom du module correspond au nom du répertoire parent. Donc, si vous essayez de mettre lib/my_stuff/bar.rbquelque chose comme:

module Foo
  class Bar
  end
end

Il ne sera pas chargé automatiquement. Là encore , si vous renommez le répertoire parent pour fooainsi l' hébergement de votre module à la voie: lib/foo/bar.rb. Ce sera là pour vous. Une autre option consiste à nommer le fichier que vous souhaitez charger automatiquement par le nom du module. De toute évidence, il ne peut y avoir qu'un seul fichier de ce nom. Dans le cas où vous avez besoin de diviser vos fichiers en plusieurs fichiers, vous pouvez bien sûr utiliser ce fichier pour exiger d'autres fichiers, mais je ne le recommande pas, car alors en mode développement et que vous modifiez ces autres fichiers, Rails ne peut pas le faire automatiquement. rechargez-les pour vous. Mais si vous voulez vraiment, vous pouvez avoir un fichier par le nom du module qui spécifie alors les fichiers réels nécessaires pour utiliser le module. Vous pouvez donc avoir deux fichiers: lib/my_stuff/bar.rbet lib/my_stuff/foo.rbet le premier étant le même que ci-dessus et le dernier contenant une seule ligne:require "bar"et cela fonctionnerait tout de même.

PS Je me sens obligé d'ajouter une chose plus importante. Ces derniers temps, chaque fois que je veux avoir quelque chose dans le répertoire lib qui doit être chargé automatiquement, j'ai tendance à penser que si c'est quelque chose que je développe spécifiquement pour ce projet (ce qui est généralement le cas, cela pourrait un jour se transformer en un extrait de code "statique" utilisé dans de nombreux projets ou un sous-module git, etc. auquel cas il devrait certainement se trouver dans le dossier lib) alors peut-être que sa place ne se trouve pas du tout dans le dossier lib. Peut-être que cela devrait être dans un sous-dossier sous le dossier de l'application · J'ai le sentiment que c'est la nouvelle façon de faire les rails. De toute évidence, la même magie opère partout dans les chemins de chargement automatique dans lesquels vous mettez vos trucs, donc c'est bon pour ces choses. Quoi qu'il en soit, ce ne sont que mes réflexions sur le sujet. Vous êtes libre d'être en désaccord. :)


MISE À JOUR: À propos du type de magie ..

Comme l'a souligné Severin dans son commentaire, le noyau "mécanisme de chargement automatique d'un module" fait bien sûr partie de Ruby, mais les trucs de chemins de chargement automatique ne le sont pas. Vous n'avez pas besoin de Rails pour faireautoload :Foo, File.join(Rails.root, "lib", "my_stuff", "bar"). Et lorsque vous essayez de référencer le module Foo pour la première fois, il est chargé pour vous. Cependant, ce que fait Rails, c'est qu'il nous donne un moyen d'essayer de charger automatiquement des choses à partir de dossiers enregistrés et cela a été implémenté de telle sorte qu'il doit supposer quelque chose sur les conventions de dénomination. S'il n'avait pas été implémenté de cette façon, chaque fois que vous faites référence à quelque chose qui n'est pas actuellement chargé, il devra parcourir tous les fichiers de tous les dossiers de chargement automatique et vérifier si l'un d'eux contient ce que vous essayez de référencer. À son tour, cela irait à l'encontre de l'idée du chargement automatique et du chargement automatique. Cependant, avec ces conventions en place, il peut déduire du module / classe que vous essayez de charger où cela pourrait être défini et de le charger.

Timo
la source
1
Pourquoi cette magie Ruby? Ruby fournit simplement la fonction de chargement automatique du module # que vous pouvez utiliser pour commander un fichier en cours de chargement lors de l'accès à une constante (non définie) (voir ruby-doc.org/core-1.9.3/Module.html#method-i-autoload ). La correspondance des noms de module / classe avec les répertoires / fichiers est à mon avis effectuée dans Rails / ActiveSupport (par exemple ici: github.com/rails/rails/blob/… ). Ai-je tort?
severin
Oui, je pense que vous avez raison. J'étais trop pressé pour "corriger" ma réponse originale lorsque Zabba a souligné son "défaut". Permettez-moi de mettre à jour ma réponse un peu plus pour clarifier ce problème.
Timo
1
J'ai passé une demi-heure à déblayer. J'avais besoin (je voulais) de charger automatiquement Sprockets :: JSRender :: Processor. Le chemin pour cela peut être trouvé en entrant dans la console des rails et en faisant "Sprockets :: JSRender :: Processor" .underscore et en disant que c'est "sprockets / js_render / processor" (avec .rb ajouté) HTH quelqu'un.
pedz
Vous venez de sauver ma raison. ~ profond soupir de soulagement ~ merci beaucoup pour le partage :)
Brenden
Merci pour ce commentaire des plus utiles. Je n'ai pas compris pourquoi certains modules se comportaient comme ils l'ont fait avant d'avoir lu votre commentaire. Bénédictions sur vous!
mjnissim
41

Attention: si vous voulez charger le 'patch singe' ou la 'classe ouverte' depuis votre dossier 'lib', n'utilisez pas l' approche 'autoload' !!!

  • Approche " config.autoload_paths ": ne fonctionne que si vous chargez une classe définie uniquement en UN endroit. Si une classe a déjà été définie ailleurs, vous ne pouvez pas la charger à nouveau par cette approche.

  • Approche " config / initializer / load_rb_file.rb ": fonctionne toujours! quelle que soit la classe cible est une nouvelle classe ou une "classe ouverte" ou "patch singe" pour la classe existante, cela fonctionne toujours!

Pour plus de détails, voir: https://stackoverflow.com/a/6797707/445908

Siwei Shen 申思维
la source
6
Il s'agit d'une distinction critique à comprendre. Merci pour cela.
Tyler Collier
28

Très similaire, mais je pense que c'est un peu plus élégant:

config.autoload_paths += Dir["#{config.root}/lib", "#{config.root}/lib/**/"]
Brian Armstrong
la source
18

Dans mon cas, j'essayais simplement de charger un fichier directement sous le répertoire lib.

Dans application.rb ...

require '/lib/this_file.rb' 

ne fonctionnait pas, même dans la console, puis quand j'ai essayé

require './lib/this_file.rb' 

et rails charge parfaitement le fichier.

Je suis toujours assez noble et je ne sais pas pourquoi cela fonctionne, mais cela fonctionne. Si quelqu'un veut m'expliquer, j'apprécierais: DI j'espère que cela aide quelqu'un de toute façon.

Nick Res
la source
2
C'est parce que ./lib/this_file.rb recherche dans le répertoire courant (dans la console Rails, ce serait votre racine Rails), et /lib/this_file.rb le recherche comme un chemin absolu. Exemple: ./lib/this_file.rb = /var/www/myrailsapp/lib/this_file.rb, /lib/this_file.rb = /lib/this_file.rb
Jason
7

J'ai eu le même problème. Voici comment je l'ai résolu. La solution charge le répertoire lib et tous les sous-répertoires (pas seulement le direct). Bien sûr, vous pouvez l'utiliser pour tous les répertoires.

# application.rb
config.autoload_paths += %W(#{config.root}/lib)
config.autoload_paths += Dir["#{config.root}/lib/**/"]
hjuskewycz
la source
5
Cela a pour effet secondaire désagréable d'altérer totalement les conventions d'espaces de noms de Rails. Si lib / bar / foo.rb définissant Bar :: Foo apparaît avant que lib / foo.rb définisse Foo dans la recherche de chargement automatique, vous obtiendrez des erreurs déroutantes comme Expected lib/bar/foo.rb to define constant Foosi vous essayez de charger lib / foo.rb en vous référant à Foo constant.
Jacob
5

config.autoload_paths ne fonctionne pas pour moi. Je le résous autrement

Ruby on rails 3 ne recharge pas automatiquement le code (chargement automatique) du dossier / lib. Je le résous en le mettant à l'intérieurApplicationController

Dir["lib/**/*.rb"].each do |path|
  require_dependency path
end 
msa.im
la source
4

Si seuls certains fichiers ont besoin d'accéder aux modules dans lib, ajoutez simplement une instruction require aux fichiers qui en ont besoin. Par exemple, si un modèle doit accéder à un module, ajoutez:

require 'mymodule'

en haut du fichier model.rb.

Mike Fischer
la source
50
Vous ne devez pas l'utiliser requiredans une application rails, car cela empêche ActiveSupport::Dependenciesde [dé] charger correctement ce code. Au lieu de cela, vous devez utiliser config.autoload_pathscomme la réponse ci-dessus, puis inclure / étendre au besoin.
ben_h
13
Merci @Mike, j'allais faire ce que tu as fait, c'était bien de voir une explication de pourquoi c'est mauvais, merci de ne pas avoir supprimé la réponse.
pupeno
qu'en est-il d'inclure 'mymodule' si vous voulez juste charger un module?
Mike
1
@ben_h Ne devriez-vous pas requirede n'importe où dans une application Rails? Dans une tâche de râteau Je suis actuellement require-ment et include-ment un module qui vit lib/. Ne devrais-je pas faire ça?
Dennis
@ben_h Ma recherche révèle qu'il est commun à requirevotre lib/code (par exemple ce billet de blog , cette réponse SO ). Je ne sais toujours pas tout. Pouvez-vous donner plus de preuves derrière la réclamation pour non-utilisation require?
Dennis
2

Épelez le nom de fichier correctement.

Sérieusement. J'ai lutté avec une classe pendant une heure car la classe était Governance :: ArchitectureBoard et le fichier était dans lib / gouvernance / architecture_baord.rb (transposé O et A dans "board")

Cela semble évident rétrospectivement, mais c'est le diable qui l'a suivi. Si la classe n'est pas définie dans le fichier dans lequel Rails s'attend à ce qu'elle soit basée sur la fusion du nom de classe, elle ne le trouvera tout simplement pas.

David Hempy
la source
2

À partir de Rails 5, il est recommandé de mettre le dossier lib sous le répertoire de l' application ou au lieu de créer d' autres espaces de noms significatifs pour le dossier que services, presenters, featuresetc et le mettre sous le répertoire d'applications pour le chargement automatique par des rails.

Veuillez également consulter ce lien de discussion GitHub .

Ashik Salman
la source