Почему Ruby 1.9.2 удаляет «.» из LOAD_PATH, а какая альтернатива?

154

Последние изменения в Ruby 1.9.2 больше не делают текущий каталог .частью вашего LOAD_PATH. У меня есть нетривиальное количество файлов Rake, которые предполагают, что они .являются частью LOAD_PATH, поэтому это сломало их (они сообщили «нет такого файла для загрузки» для всех операторов require, основанных на пути проекта). Было ли какое-то конкретное оправдание для этого?

Что касается исправления, добавление $: << "."везде работает, но кажется невероятным, и я не хочу этого делать. Какой предпочтительный способ сделать мой Rakefiles 1.9.2+ совместимым?

Джон Феминелла
источник

Ответы:

141

Это считалось риском "безопасности".

Вы можете обойти это, используя абсолютные пути

File.expand_path(__FILE__) et al

или делать

require './filename' (ironically).

или с помощью

require_relative 'filename'

или добавление каталога «include»

ruby -I . ...

или то же самое, используя irb;

$irb -I .
rogerdpack
источник
27
Я завелась с помощью require_relative. Спасибо.
Джон Феминелла
11
Это похоже на большинство Unix-систем, не включающих текущий каталог в путь для запуска исполняемых файлов?
Эндрю Гримм
5
require './filename'работает только в том случае, если ваш скрипт выполняется с рабочим каталогом, установленным в тот же каталог, в котором находится скрипт. Это часто не относится к проектам с несколькими каталогами.
mxcl
34

Есть две причины:

  • надежность и
  • безопасность

Оба основаны на одном и том же базовом принципе: в общем, вы просто не можете знать, каков текущий каталог, когда ваш код выполняется. Это означает, что, когда вам требуется файл и вы зависите от того, находится ли он в текущем каталоге, у вас нет возможности контролировать, будет ли этот файл вообще там или это тот файл, который вы действительно ожидаете там.

Йорг Миттаг
источник
5
Я не думаю, что принудительное размещение двух файлов в одном и том же месте по отношению друг к другу - это плохое требование. Если бы это было правдой, то мы бы не использовали каталоги.
Джон Феминелла
4
@John Feminella: какое это имеет отношение к размещению файлов в путях относительно друг друга? Вопрос в том, чтобы поместить их относительно .текущего рабочего каталога. Если пользователь находится cdв другом каталоге, текущий рабочий каталог изменяется, и вы теперь require совершенно разные файлы, в зависимости от того, в каком каталоге находился пользователь, когда он вызывал ваш скрипт. Я не думаю, что это хорошая идея.
Йорг Миттаг
Таким образом, чтобы поддерживать достойный интерфейс, вы должны это сделать? $: << File.dirname(__FILE__)
Джошуа Чик
4
@ Джошуа Чик: Лично мне это не нравится. (Но, пожалуйста, не смотрите на мой старый код, потому что он завален такими вещами :-)). Я просто делаю вид, что libкаталог находится, $LOAD_PATHа затем requireвсе файлы, относящиеся к lib. Другими словами: я оставляю это администратору, чтобы выяснить, как правильно настроить $LOAD_PATH. Если вы используете RubyGems, это тривиально, потому что RubyGems автоматически делает это за вас, а если вы используете пакеты Debian, то это работа сопровождающего пакета. В целом, это, кажется, работает довольно хорошо.
Йорг Миттаг
8
@Joshua Cheek: Кроме того, в качестве своего рода противовеса удалению .из $LOAD_PATHRuby 1.9.2 вводится файл require_relative... неожиданный ... requiresa относительно местоположения исполняемого в данный момент файла (то есть относительно File.dirname(__FILE__)).
Йорг Миттаг
16

Как указывают другие ответы, это угроза безопасности, поскольку .в вашем пути загрузки указывается текущий рабочий каталог Dir.pwd, а не каталог текущего загружаемого файла. Так что, кто бы ни выполнял ваш скрипт, он может изменить это, просто cdперейдя в другой каталог. Не хорошо!

Я использовал полные пути, построенные __FILE__в качестве альтернативы.

require File.expand_path(File.join(File.dirname(__FILE__), 'filename'))

В отличие от require_relativeэтого, он обратно совместим с Ruby 1.8.7.

Джонатан Тран
источник
4
Есть также этот вариант (который я лично нахожу более читабельным): require Pathname.new(__FILE__).dirname + 'filename'
Тайлер Рик
8

использование require_relative 'file_to_require'

Добавьте это в свой код, чтобы сделать require_relative работу в 1.8.7:

unless Kernel.respond_to?(:require_relative)
  module Kernel
    def require_relative(path)
      require File.join(File.dirname(caller.first), path.to_str)
    end
  end
end
Тайлер Брок
источник
3

Я нашел, что это было смущающее изменение, пока я не понял пару вещей.

Вы можете установить RUBYLIB в свой .profile (Unix) и продолжать жить, как вы делали это раньше:

export RUBYLIB="."

Но, как упоминалось выше, это уже давно считается небезопасным.

В подавляющем большинстве случаев вы можете избежать проблем, просто вызывая свои сценарии Ruby с префиксом '.' например ./scripts/server.

Dylan
источник
3

Как отметил Йорг Миттаг, я думаю, что вы хотите использовать, require_relativeчтобы требуемый файл относился к исходному файлу requireобъявления, а не к текущему рабочему каталогу.

Ваши зависимости должны быть относительно вашего файла сборки рейка.

Мартин
источник