File.expand_path ("../../ Gemfile", __FILE__) Comment ça marche? Où est le fichier?

84

ENV["BUNDLE_GEMFILE"] = File.expand_path("../../Gemfile", __FILE__)

J'essaie juste d'accéder à un fichier .rb à partir du répertoire some et un tutoriel me dit d'utiliser ce code mais je ne vois pas comment il trouve le fichier gem.

alorsengah
la source
1
Voir aussi la question stackoverflow.com/questions/4333286
Theo

Réponses:

194
File.expand_path('../../Gemfile', __FILE__)

est un idiome Ruby quelque peu laid pour obtenir le chemin absolu d'un fichier lorsque vous connaissez le chemin relatif au fichier actuel. Une autre façon de l'écrire est la suivante:

File.expand_path('../Gemfile', File.dirname(__FILE__))

les deux sont laids, mais la première variante est plus courte. La première variante est, cependant, également très peu intuitive jusqu'à ce que vous compreniez bien. Pourquoi le supplément ..? (mais la deuxième variante peut donner une idée de la raison pour laquelle elle est nécessaire).

Voici comment cela fonctionne: File.expand_pathrenvoie le chemin absolu du premier argument, par rapport au deuxième argument (qui par défaut est le répertoire de travail courant). __FILE__est le chemin vers le fichier dans lequel se trouve le code. Puisque le deuxième argument dans ce cas est un chemin vers un fichier, et File.expand_pathsuppose un répertoire, nous devons coller un supplément ..dans le chemin pour obtenir le chemin correct. Voilà comment cela fonctionne:

File.expand_pathest fondamentalement implémenté comme ceci (dans le code suivant pathaura la valeur ../../Gemfileet relative_toaura la valeur de /path/to/file.rb):

def File.expand_path(path, relative_to=Dir.getwd)
  # first the two arguments are concatenated, with the second argument first
  absolute_path = File.join(relative_to, path)
  while absolute_path.include?('..')
    # remove the first occurrence of /<something>/..
    absolute_path = absolute_path.sub(%r{/[^/]+/\.\.}, '')
  end
  absolute_path
end

(il y a un peu plus à cela, il s'étend ~au répertoire personnel et ainsi de suite - il y a probablement aussi d'autres problèmes avec le code ci-dessus)

Passer en revue un appel au code ci absolute_path- dessus obtiendra d'abord la valeur /path/to/file.rb/../../Gemfile, puis pour chaque tour de la boucle, le premier ..sera supprimé, avec le composant de chemin avant lui. Le premier /file.rb/..est supprimé, puis le tour suivant /to/..est supprimé, et nous obtenons /path/Gemfile.

Pour faire une histoire courte, File.expand_path('../../Gemfile', __FILE__)c'est une astuce pour obtenir le chemin absolu d'un fichier lorsque vous connaissez le chemin relatif au fichier courant. Le plus ..dans le chemin relatif est d'éliminer le nom du fichier dans __FILE__.

Dans Ruby 2.0, il existe une Kernelfonction appelée __dir__qui est implémentée en tant que File.dirname(File.realpath(__FILE__)).

Théo
la source
2
Y a-t-il une raison pour laquelle vous ne devriez pas simplement utiliser 'require_relative' autre que l'incompatibilité avec la version antérieure à Ruby 1.9.2?
Danny Andrews
9
À partir de Ruby 2.0, vous pouvez utiliserFile.expand_path('../Gemfile',__dir__)
Phrogz
Cette ligne de Theo m'a finalement permis de cliquer pour moi File.expand_path assumes a directory, même si ce __FILE__n'est pas un répertoire. Pour que les choses aient un sens, utilisez __dir__qui est en fait un répertoire.
mbigras
9

Deux références:

  1. Documentation de la méthode File :: expand_path
  2. Comment ça __FILE__marche dans Ruby

Je suis tombé sur ceci aujourd'hui:

boot.rb commit dans Rails Github

Si vous remontez de deux répertoires depuis boot.rb dans l'arborescence de répertoires:

/ railties / lib / rails / générateurs / rails / app / templates

vous voyez Gemfile, ce qui me porte à croire que les File.expand_path("../../Gemfile", __FILE__)références au fichier suivant:/path/to/this/file/../../Gemfile

Patrick Klingemann
la source
Merci pour le message, mais cela vient du tutoriel de gembundler, donc j'essayais de comprendre exactement comment ils l'avaient :)
thenengah