File.expand_path (“../../ Gemfile”, __FILE__) ¿Cómo funciona esto? ¿Dónde está el archivo?

84

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

Solo estoy tratando de acceder a un archivo .rb desde el directorio some y un tutorial me dice que use este código, pero no veo cómo está encontrando el archivo gem.

thenengah
fuente
1
También vea la pregunta stackoverflow.com/questions/4333286
Theo

Respuestas:

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

es un modismo de Ruby algo feo para obtener la ruta absoluta a un archivo cuando conoce la ruta relativa al archivo actual. Otra forma de escribirlo es esta:

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

ambos son feos, pero la primera variante es más corta. Sin embargo, la primera variante tampoco es muy intuitiva hasta que la domines. ¿Por qué el extra ..? (pero la segunda variante puede dar una pista de por qué se necesita).

Así es como funciona: File.expand_pathdevuelve la ruta absoluta del primer argumento, en relación con el segundo argumento (que por defecto es el directorio de trabajo actual). __FILE__es la ruta al archivo en el que se encuentra el código. Dado que el segundo argumento en este caso es una ruta a un archivo, y File.expand_pathasume un directorio, tenemos que colocar un extra ..en la ruta para obtener la ruta correcta. Así es como funciona:

File.expand_pathse implementa básicamente así (en el siguiente código pathtendrá el valor de ../../Gemfiley relative_totendrá el valor 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

(hay un poco más, se expande ~al directorio de inicio y así sucesivamente; probablemente también haya otros problemas con el código anterior)

Al realizar una llamada al código anterior absolute_path, primero obtendrá el valor /path/to/file.rb/../../Gemfile, luego, para cada ronda del ciclo .., se eliminará la primera , junto con el componente de ruta anterior. Primero /file.rb/..se elimina, luego en la siguiente ronda /to/..se elimina y obtenemos /path/Gemfile.

En resumidas cuentas, File.expand_path('../../Gemfile', __FILE__)es un truco obtener la ruta absoluta de un archivo cuando se conoce la ruta relativa al archivo actual. El extra ..en la ruta relativa es eliminar el nombre del archivo en formato __FILE__.

En Ruby 2.0 hay una Kernelfunción llamada __dir__que se implementa como File.dirname(File.realpath(__FILE__)).

Theo
fuente
2
¿Hay alguna razón por la que no debería usar 'require_relative' que no sea la incompatibilidad con Ruby 1.9.2 anterior?
Danny Andrews
9
A partir de Ruby 2.0 puede usarFile.expand_path('../Gemfile',__dir__)
Phrogz
Esta línea de Theo finalmente consiguió hacer clic para mí File.expand_path assumes a directory, aunque __FILE__no es un directorio. Para que las cosas tengan sentido, use __dir__que en realidad es un directorio.
mbigras
9

Dos referencias:

  1. Archivo :: documentación del método expand_path
  2. ¿Cómo __FILE__funciona en Ruby?

Me encontré con esto hoy:

boot.rb confirmado en Rails Github

Si sube dos directorios desde boot.rb en el árbol de directorios:

/ railties / lib / rails / generators / rails / app / templates

ves Gemfile, lo que me lleva a creer que hace File.expand_path("../../Gemfile", __FILE__)referencia al siguiente archivo:/path/to/this/file/../../Gemfile

Patrick Klingemann
fuente
Gracias por la publicación, pero esto vino del tutorial de gembundler, así que estaba tratando de entenderlo exactamente como lo tenían :)
thenengah