File.expand_path('../../Gemfile', __FILE__)
это несколько уродливая идиома Ruby для получения абсолютного пути к файлу, когда вы знаете путь относительно текущего файла. Другой способ записать это:
File.expand_path('../Gemfile', File.dirname(__FILE__))
оба уродливы, но первый вариант короче. Однако первый вариант также очень неинтуитивен, пока вы не освоите его. Почему лишнее ..
? (но второй вариант может подсказать, зачем он нужен).
Вот как это работает: File.expand_path
возвращает абсолютный путь первого аргумента относительно второго аргумента (который по умолчанию соответствует текущему рабочему каталогу). __FILE__
- это путь к файлу, в котором находится код. Поскольку второй аргумент в этом случае - это путь к файлу и File.expand_path
предполагает наличие каталога, мы должны добавить в путь дополнительный аргумент, ..
чтобы получить правильный путь. Вот как это работает:
File.expand_path
в основном реализован следующим образом (в следующем коде path
будет иметь значение ../../Gemfile
и relative_to
будет иметь значение /path/to/file.rb
):
def File.expand_path(path, relative_to=Dir.getwd)
absolute_path = File.join(relative_to, path)
while absolute_path.include?('..')
absolute_path = absolute_path.sub(%r{/[^/]+/\.\.}, '')
end
absolute_path
end
(есть ~
еще кое- что, он расширяется до домашнего каталога и так далее - вероятно, есть и другие проблемы с приведенным выше кодом)
При пошаговом вызове приведенного выше кода absolute_path
сначала будет получено значение /path/to/file.rb/../../Gemfile
, затем для каждого раунда в цикле первый ..
будет удален вместе с компонентом пути перед ним. Сначала /file.rb/..
снимается, потом на следующем раунде /to/..
снимается, и мы получаем /path/Gemfile
.
Короче говоря, File.expand_path('../../Gemfile', __FILE__)
это уловка, позволяющая получить абсолютный путь к файлу, когда вы знаете путь относительно текущего файла. Дополнительное значение ..
в относительном пути - исключить имя файла в __FILE__
.
В Ruby 2.0 есть Kernel
функция, __dir__
которая реализована как File.dirname(File.realpath(__FILE__))
.