Ajouter un répertoire à $ LOAD_PATH (Ruby)


96

J'ai vu deux techniques couramment utilisées pour ajouter le répertoire du fichier en cours d'exécution au $ LOAD_PATH (ou $ :). Je vois les avantages de faire cela au cas où vous ne travailleriez pas avec un bijou. L'un semble plus verbeux que l'autre, évidemment, mais y a-t-il une raison d'aller avec l'un plutôt que l'autre?

La première méthode verbeuse (pourrait être excessive):

$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__))) unless $LOAD_PATH.include?(File.expand_path(File.dirname(__FILE__)))

et le plus simple, rapide et sale:

$:.unshift File.dirname(__FILE__)

Y a-t-il une raison d'aller avec l'un sur l'autre?


2
Une version légèrement moins verbeuse de la version verbeuse est:File.expand_path(File.dirname(__FILE__)).tap {|pwd| $LOAD_PATH.unshift(pwd) unless $LOAD_PATH.include?(pwd)}
Nathan Long

qu'en est-il de la clause «à moins»? Comment les deux ci-dessus peuvent-ils être équivalents?
inger le

En tant que personne qui est venue ici pour essayer de comprendre comment l'utiliser, c'est super cryptique. Je ne vois pas d'où vient le nom du répertoire sur les exemples. J'apprécierais que quelqu'un puisse le préciser.
SlySherZ

1
L'utilisation __dir__(à partir de Ruby 2.0) peut rendre l'un de ces éléments plus concis.
Nathan Long

Réponses:


51

Je dirais $:.unshift File.dirname(__FILE__)plutôt que l'autre, simplement parce que j'en ai vu beaucoup plus d'utilisation dans le code que celui- $LOAD_PATHlà, et c'est aussi plus court!


Quand j'ai commencé avec Ruby, je pensais évidemment que $ LOAD_PATH était mieux. Mais une fois que vous êtes diplômé du statut de débutant, je n'utiliserais $ LOAD_PATH que si j'essayais de rendre mon code plus lisible pour un débutant. Meh c'est un compromis. Cela dépend de la façon dont le code est "public", tant que l'utilisation de la mémoire est la même pour chacun, ce que je suppose essentiellement.
boulder_ruby

9
Dépend du guide de style que vous suivez pour votre projet. Le populaire Ruby Style Guide dit: "Évitez d'utiliser des variables spéciales de style Perl (comme $ :, $ ;, etc.). Elles sont assez cryptiques et leur utilisation dans tout autre que les scripts à une seule ligne est déconseillée."
bobmagoo

152

Le chemin de chargement Ruby est très couramment vu écrit comme $:, mais ce n'est pas parce qu'il est court qu'il ne l'améliore pas. Si vous préférez la clarté à l'intelligence, ou si la brièveté pour elle-même vous démange, vous n'avez pas besoin de le faire simplement parce que tout le monde l'est. Dire bonjour à ...

$LOAD_PATH

... et dites au revoir à ...

# I don't quite understand what this is doing...
$:

29
De plus, il est beaucoup plus difficile pour Google d'utiliser des chaînes comme "$:" qui ne contiennent que des symboles.
DSimon

23

Je n'aime pas trop la manière «rapide et sale». Tous les nouveaux utilisateurs de Ruby se demanderont ce que $:.c'est.

Je trouve cela plus évident.

libdir = File.dirname(__FILE__)
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)

Ou si je tiens à avoir le chemin complet ...

libdir = File.expand_path(File.dirname(__FILE__))
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)

MISE À JOUR 2009/09/10

Dernièrement, j'ai fait ce qui suit:

$:.unshift(File.expand_path(File.dirname(__FILE__))) unless
    $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))

Je l'ai vu dans tout un tas de projets ruby ​​différents en naviguant sur GitHub.

Semble être la convention?


@LukeAntins, c'est vraiment génial mais où dois-je "bootstrap" load_path dans l'application?
gaussblurinc

@gaussblurinc Quelque part «près du sommet» de votre lib / application, mais cela dépend vraiment. Si vous aviez un binfichier qui était toujours relatif au vôtre codeet qu'il n'était jamais exécuté que par le binfichier ... bootstrap dans le bac. Si vous avez une bibliothèque, démarrez en haut de votre code de bibliothèque comme dans lib/code.rbpour accéder à tout ce qui se trouve sous lib/code/. J'espère que cette randonnée vous aidera!
Luke Antins

1
RuboCop m'informe qu'il __dir__peut être utilisé pour obtenir un chemin vers le répertoire du fichier courant.
Raphael

8

Si vous tapez script/consolevotre projet Rails et entrez $:, vous obtiendrez un tableau qui comprend tous les répertoires nécessaires pour charger Ruby. Ce qu'il faut retenir de ce petit exercice, c'est qu'il $:s'agit d'un tableau. Cela étant, vous pouvez exécuter des fonctions dessus, comme ajouter au début d'autres répertoires avec la unshiftméthode ou l' <<opérateur. Comme vous l'avez laissé entendre dans votre déclaration $:et $LOAD_PATHsont les mêmes.

L'inconvénient de le faire de la manière rapide et sale comme vous l'avez mentionné est le suivant: si vous avez déjà le répertoire dans votre chemin de démarrage, il se répétera.

Exemple:

J'ai un plugin que j'ai créé appelé todo. Mon annuaire est structuré comme ceci:

/---vendeur
  |
  | --- / plugins
        |
        | --- / todo
              |
              | --- / lib
                    |
                    | --- / app
                          |
                          | --- / modèles
                          | --- / contrôleurs
              |
              | --- / rails
                    |
                    | --- init.rb

Dans le fichier init.rb, j'ai entré le code suivant:

## In vendor/plugins/todo/rails/init.rb
    %w{ models controllers models }.each do |dir|
      path = File.expand_path(File.join(File.dirname(__FILE__), '../lib', 'app', dir))
      $LOAD_PATH << path
      ActiveSupport::Dependencies.load_paths << path
      ActiveSupport::Dependencies.load_once_paths.delete(path)
    end 

Notez comment je dis au bloc de code d'exécuter les actions à l'intérieur du bloc sur les chaînes 'models', 'controllers' et 'models', où je répète 'models'. (Pour info, %w{ ... }c'est juste une autre façon de dire à Ruby de contenir un tableau de chaînes). Quand je cours script/console, je tape ce qui suit:

>> puts $:

Et je tape ceci pour qu'il soit plus facile de lire le contenu de la chaîne. Le résultat que j'obtiens est:

...
...
./Users/Me/mySites/myRailsApp/vendor/plugins/todo/lib/app/models
./Users/Me/mySites/myRailsApp/vendor/plugins/todo/lib/app/controllers
./Users/Me/mySites/myRailsApp/vendor/plugins/todo/lib/app/models

Comme vous pouvez le voir, bien que ce soit un exemple aussi simple que je pourrais créer en utilisant un projet sur lequel je travaille actuellement, si vous ne faites pas attention, la manière rapide et sale conduira à des chemins répétés. Le chemin le plus long vérifiera les chemins répétés et s'assurera qu'ils ne se produisent pas.

Si vous êtes un programmeur Rails expérimenté, vous avez probablement une très bonne idée de ce que vous faites et ne faites probablement pas l'erreur de répéter les chemins. Si vous êtes un débutant, j'irais avec le chemin le plus long jusqu'à ce que vous compreniez vraiment ce que vous faites.


votre réponse très utile et également bien expliquée. Modification suggérée: la méthode load_pathset load_once_paths.deletesont obsolètes. Serait aidé à mettre à jour les lignes qui s'y réfèrent comme: ActiveSupport::Dependencies.autoload_paths << path ActiveSupport::Dependencies.autoload_once_paths.delete(path)
Uzzar

8

Le mieux que j'ai rencontré pour ajouter un répertoire via un chemin relatif lors de l'utilisation de Rspec. Je le trouve assez verbeux mais aussi toujours une belle doublure.

$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))


-2

Je sais que cela fait longtemps que cette question n'a pas été posée pour la première fois, mais j'ai une réponse supplémentaire que je veux partager.

J'ai plusieurs applications Ruby qui ont été développées par un autre programmeur sur plusieurs années, et elles réutilisent les mêmes classes dans les différentes applications bien qu'elles puissent accéder à la même base de données. Comme cela enfreint la règle DRY, j'ai décidé de créer une bibliothèque de classes à partager par toutes les applications Ruby. J'aurais pu le mettre dans la bibliothèque principale Ruby, mais cela cacherait le code personnalisé dans la base de code commune, ce que je ne voulais pas faire.

J'ai eu un problème où j'avais un conflit de nom entre un nom déjà défini "profile.rb" et une classe que j'utilisais. Ce conflit n'était pas un problème jusqu'à ce que j'essaye de créer la bibliothèque de code commune. Normalement, Ruby recherche d'abord les emplacements des applications, puis va vers les emplacements $ LOAD_PATH.

Le fichier application_controller.rb n'a pas pu trouver la classe que j'ai créée et a renvoyé une erreur sur la définition d'origine car ce n'est pas une classe. Depuis que j'ai supprimé la définition de classe de la section app / models de l'application, Ruby n'a pas pu la trouver là-bas et est allée la chercher dans les chemins Ruby.

J'ai donc modifié la variable $ LOAD_PATH pour inclure un chemin vers le répertoire de la bibliothèque que j'utilisais. Cela peut être fait dans le fichier environment.rb au moment de l'initialisation.

Même avec le nouveau répertoire ajouté au chemin de recherche, Ruby lançait une erreur car il prenait de préférence le fichier défini par le système en premier. Le chemin de recherche dans la variable $ LOAD_PATH recherche de préférence les chemins Ruby en premier.

Donc, j'avais besoin de changer l'ordre de recherche pour que Ruby trouve la classe dans ma bibliothèque commune avant de rechercher les bibliothèques intégrées.

Ce code l'a fait dans le fichier environment.rb:

Rails::Initializer.run do |config|

* * * * *

path = []
path.concat($LOAD_PATH)
$LOAD_PATH.clear
$LOAD_PATH << 'C:\web\common\lib'
$LOAD_PATH << 'C:\web\common'
$LOAD_PATH.concat(path)

* * * * *

end

Je ne pense pas que vous puissiez utiliser l'une des constructions de codage avancées données auparavant à ce niveau, mais cela fonctionne très bien si vous souhaitez configurer quelque chose au moment de l'initialisation dans votre application. Vous devez conserver l'ordre d'origine de la variable $ LOAD_PATH d'origine lorsqu'elle est ajoutée à la nouvelle variable, sinon certaines des principales classes Ruby sont perdues.

Dans le fichier application_controller.rb, j'utilise simplement un

require 'profile'
require 'etc' #etc

et cela charge les fichiers de bibliothèque personnalisés pour l'ensemble de l'application, c'est-à-dire que je n'ai pas à utiliser les commandes require dans chaque contrôleur.

Pour moi, c'était la solution que je recherchais, et j'ai pensé l'ajouter à cette réponse pour transmettre l'information.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.