File.expand_path (“../../ Gemfile”, __FILE__) Як це працює? Де файл?


84

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

Я просто намагаюся отримати доступ до .rb-файлу з якогось каталогу, і підручник каже мені використовувати цей код, але я не бачу, як він знаходить файл gem.


1
Також див. Запитання stackoverflow.com/questions/4333286
Тео,

Відповіді:


194
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)
  # 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

(тут є трохи більше, він розширюється ~до домашнього каталогу тощо) - можливо, є також деякі інші проблеми з кодом вище)

Переходячи через виклик коду вище 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__)).


2
Чи є якась причина, чому ви не повинні просто використовувати 'require_relative', крім несумісності з попередньою версією Ruby 1.9.2?
Danny Andrews

9
Станом на Ruby 2.0 ви можете використовуватиFile.expand_path('../Gemfile',__dir__)
Phrogz

Цей рядок від Тео нарешті змусив мене натиснути File.expand_path assumes a directory, хоча __FILE__це не каталог. Щоб речі мали сенс, використовуйте, __dir__що насправді є каталогом.
mbigras

9

Два посилання:

  1. File :: expand_path документація до методу
  2. Як __FILE__працює Ruby

Я сьогодні натрапив на це:

фіксація boot.rb у Rails Github

Якщо ви піднімаєте два каталоги з boot.rb у дереві каталогів:

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

ви бачите Gemfile, що змушує мене вважати, що File.expand_path("../../Gemfile", __FILE__)посилання на такий файл:/path/to/this/file/../../Gemfile


Дякую за допис, але це вийшло з підручника з gembundler, тому я намагався зрозуміти, як саме вони це мали :)
thenengah
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.