Ruby: требуется vs require_relative - наилучшая практика для работы в Ruby <1.9.2 и >= 1.9.2
Какова наилучшая практика, если я хочу require
относительный файл в Ruby, и я хочу, чтобы он работал как в файлах 1.8.x и >= 1.9.2?
Я вижу несколько вариантов:
- просто сделайте
$LOAD_PATH << '.'
и забудьте все
- do
$LOAD_PATH << File.dirname(__FILE__)
-
require './path/to/file'
- проверьте, если
RUBY_VERSION
< 1.9.2, затем определите require_relative
как require
, используйте require_relative
всюду, где это необходимо впоследствии
- проверьте, существует ли
require_relative
, если это так, попробуйте продолжить, как в предыдущем случае
- использовать такие странные конструкции, как
require File.join(File.dirname(__FILE__), 'path/to/file')
- увы, они, похоже, не работают в Ruby 1.9, потому что, например: $ cat caller.rb
require File.join(File.dirname(__FILE__), 'path/to/file')
$ cat path/to/file.rb
puts 'Some testing'
$ ruby caller
Some testing
$ pwd
/tmp
$ ruby /tmp/caller
Some testing
$ ruby tmp/caller
tmp/caller.rb:1:in 'require': no such file to load -- tmp/path/to/file (LoadError)
from tmp/caller.rb:1:in '<main>'
- Даже более сложная конструкция:
require File.join(File.expand_path(File.dirname(__FILE__)), 'path/to/file')
, похоже, работает, но это странно и не очень хорошо выглядит.
- Используйте backports gem - он тяжелый, он требует инфраструктуры rubygems и включает тонны других обходных решений, в то время как я просто хочу
require
для работы с относительными файлами.
Там qaru.site/info/32400/..., который дает еще несколько примеров, но он не дает четкого ответа - что является лучшей практикой.
Есть ли какое-либо достойное универсальное решение, принятое всеми, чтобы мое приложение выполнялось на Ruby < 1.9.2 и >= 1.9.2?
UPDATE
Уточнение: я не хочу просто ответов, таких как "вы можете сделать X" - на самом деле, я уже упомянул большинство вариантов, о которых идет речь. Я хочу обоснования, то есть, почему это лучшая практика, каковы ее плюсы и минусы и почему ее следует выбирать среди других.
Ответы
Ответ 1
Обходной путь для этого был просто добавлен в жемчужину 'aws', поэтому я подумал, что буду делиться этим, поскольку он был вдохновлен этим сообщением.
https://github.com/appoxy/aws/blob/master/lib/awsbase/require_relative.rb
unless Kernel.respond_to?(:require_relative)
module Kernel
def require_relative(path)
require File.join(File.dirname(caller[0]), path.to_str)
end
end
end
Это позволяет использовать require_relative
, как в рубине 1.9.2 в ruby 1.8 и 1.9.1.
Ответ 2
Прежде чем я сделал переход к 1.9.2, я использовал следующее для относительного:
require File.expand_path('../relative/path', __FILE__)
Это немного странно в первый раз, когда вы его видите, потому что в начале кажется, что есть дополнительные "..". Причина в том, что expand_path
будет расширять путь относительно второго аргумента, а второй аргумент будет интерпретироваться так, как если бы он был каталогом. __FILE__
, очевидно, не является каталогом, но это не имеет значения, так как expand_path
не имеет значения, существуют ли файлы или нет, он просто применит некоторые правила для расширения таких вещей, как ..
, .
и ~
. Если вы можете преодолеть первоначальный "waitaminute", там нет дополнительного ..
? ". Я думаю, что линия выше работает достаточно хорошо.
Предполагая, что __FILE__
является /absolute/path/to/file.rb
, происходит следующее: expand_path
построит строку /absolute/path/to/file.rb/../relative/path
, а затем применит правило, в котором говорится, что ..
должен удалить перед ним компонент пути (file.rb
в этом случае), возвращая /absolute/path/to/relative/path
.
Это лучшая практика? В зависимости от того, что вы подразумеваете под этим, но это похоже на всю базу кода Rails, поэтому я бы сказал, что это, по крайней мере, достаточно распространенная идиома.
Ответ 3
В Pickaxe есть фрагмент для 1.8. Вот он:
def require_relative(relative_feature)
c = caller.first
fail "Can't parse #{c}" unless c.rindex(/:\d+(:in `.*')?$/)
file = $`
if /\A\((.*)\)/ =~ file # eval, etc.
raise LoadError, "require_relative is called in #{$1}"
end
absolute = File.expand_path(relative_feature, File.dirname(file))
require absolute
end
В основном он использует то, что ответил Тео, но так что вы все еще можете использовать require_relative
.
Ответ 4
$LOAD_PATH << '.'
$LOAD_PATH << File.dirname(__FILE__)
Это не хорошая привычка к безопасности: зачем вам раскрывать всю свою директорию?
require './path/to/file'
Это не работает, если RUBY_VERSION < 1.9.2
используйте странные конструкции, такие как
require File.join(File.dirname(__FILE__), 'path/to/file')
Даже более сложная конструкция:
require File.join(File.expand_path(File.dirname(__FILE__)), 'path/to/file')
Используйте backports gem - он тяжелый, он требует рубигем инфраструктуры и включает тонны других обходных решений, в то время как я просто требуется работать с относительными файлами.
Вы уже ответили, почему это не лучшие варианты.
проверьте, что RUBY_VERSION < 1.9.2, затем определите require_relative как требуют, используйте require_relative везде, где это необходимо впоследствии
проверьте, существует ли require_relative, если это так, попробуйте продолжить как в предыдущем случае
Это может работать, но там безопаснее и быстрее: для устранения исключения LoadError:
begin
# require statements for 1.9.2 and above, such as:
require "./path/to/file"
# or
require_local "path/to/file"
rescue LoadError
# require statements other versions:
require "path/to/file"
end
Ответ 5
Я поклонник использования rbx-require-relative gem (источник). Он был первоначально написан для Rubinius, но он также поддерживает MRI 1.8.7 и ничего не делает в 1.9.2. Требование жемчужины прост, и мне не нужно бросать фрагменты кода в мой проект.
Добавьте его в свой Gemfile:
gem "rbx-require-relative"
Затем require 'require_relative'
перед вами require_relative
.
Например, один из моих тестовых файлов выглядит следующим образом:
require 'rubygems'
require 'bundler/setup'
require 'minitest/autorun'
require 'require_relative'
require_relative '../lib/foo'
Это самое чистое решение из любой из этих IMO, и драгоценный камень не так тяжел, как backports.
Ответ 6
backports
gem теперь позволяет индивидуальную загрузку backports.
Вы могли бы просто:
require 'backports/1.9.1/kernel/require_relative'
# => Now require_relative works for all versions of Ruby
Этот require
не будет влиять на новые версии и не будет обновлять какие-либо другие встроенные методы.
Ответ 7
Другой вариант - указать интерпретатору, какие пути к поиску
ruby -I /path/to/my/project caller.rb
Ответ 8
Одна из проблем, которые я не видел с решениями, основанными на __FILE__, заключается в том, что они ломаются относительно символических ссылок. Например, у меня есть:
~/Projects/MyProject/foo.rb
~/Projects/MyProject/lib/someinclude.rb
Основная script, точка входа, приложение - foo.rb. Этот файл связан с ~/Scripts/foo, который находится в моем $PATH. Это требование должно быть нарушено, когда я выполняю 'foo':
require File.join(File.dirname(__FILE__), "lib/someinclude")
Потому что __FILE__ is ~/Scripts/foo, поэтому инструкция require ищет ~/Scripts/foo/lib/someinclude.rb, которая, очевидно, не существует. Решение прост. Если __FILE__ - это символическая ссылка, она должна быть разыменована. Путь # realpath поможет нам в этой ситуации:
require "pathname"
require File.join(File.dirname(Pathname.new(__FILE__).realpath), "lib/someinclude")
Ответ 9
Если вы строили драгоценный камень, вы не захотите загрязнять путь загрузки.
Но, в случае автономного приложения очень удобно просто добавить текущий каталог к пути загрузки, как в первых двух примерах.
Мое голосование переходит к первому варианту в списке.
Мне бы хотелось увидеть какую-нибудь солидную рубиновскую литературу.
Ответ 10
Я бы определил свой собственный relative_require
, если он не существует (т.е. под 1.8), а затем везде использовать тот же синтаксис.
Ответ 11
Ruby on Rails:
config_path = File.expand_path("../config.yml", __FILE__)