ENV["BUNDLE_GEMFILE"] = File.expand_path("../../Gemfile", __FILE__)
Я просто намагаюся отримати доступ до .rb-файлу з якогось каталогу, і підручник каже мені використовувати цей код, але я не бачу, як він знаходить файл gem.
ENV["BUNDLE_GEMFILE"] = File.expand_path("../../Gemfile", __FILE__)
Я просто намагаюся отримати доступ до .rb-файлу з якогось каталогу, і підручник каже мені використовувати цей код, але я не бачу, як він знаходить файл gem.
Відповіді:
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__))
.
File.expand_path('../Gemfile',__dir__)
File.expand_path assumes a directory
, хоча __FILE__
це не каталог. Щоб речі мали сенс, використовуйте, __dir__
що насправді є каталогом.
Два посилання:
Я сьогодні натрапив на це:
фіксація boot.rb у Rails Github
Якщо ви піднімаєте два каталоги з boot.rb у дереві каталогів:
/ railties / lib / rails / generators / rails / app / templates
ви бачите Gemfile, що змушує мене вважати, що File.expand_path("../../Gemfile", __FILE__)
посилання на такий файл:/path/to/this/file/../../Gemfile