Ruby: require vs require_relative - рекомендуется использовать обходные пути для Ruby <1.9.2 и> = 1.9.2 - PullRequest
152 голосов
/ 02 декабря 2010

Что лучше, если я хочу require относительный файл в Ruby и Я хочу, чтобы он работал как в 1.8.x, так и> = 1.9.2?

Я вижу несколько вариантов:

  • просто сделай $LOAD_PATH << '.' и забудь все
  • до $LOAD_PATH << File.dirname(__FILE__)
  • require './path/to/file'
  • проверьте, если RUBY_VERSION <1.9.2, затем определите <code>require_relative как require, затем используйте require_relative везде, где это необходимо
  • проверьте, если require_relative уже существует, если это так, попробуйте продолжить, как в предыдущем случае
  • использовать странные конструкции, такие как <pre><code>require File.join(File.dirname(__FILE__), 'path/to/file') - увы, они не работают должным образом в Ruby 1.9, потому что, например:
    <code>$ 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>'</code>
  • Даже более странная конструкция: <pre><code>require File.join(File.expand_path(File.dirname(__FILE__)), 'path/to/file'), кажется, работает, но это странно и не совсем хорошо выглядит.
  • Используйте backports gem - он довольно тяжелый, требует инфраструктуры rubygems и включает в себя множество других обходных путей, а я просто хочу, чтобы require работал с относительными файлами.

В StackOverflow есть тесно связанный вопрос *1045*, который дает еще несколько примеров, но не дает четкого ответа - что является наилучшей практикой.

Есть ли какое-нибудь достойное, общепризнанное, универсальное решение для запуска моего приложения на Ruby <1.9.2 и> = 1.9.2?

* * ОБНОВЛЕНИЕ тысячи сорок-девять

Разъяснение: я не хочу просто отвечать, как "вы можете сделать X" - на самом деле, я уже упомянул большинство рассматриваемых вариантов. Я хочу обоснование , т. Е. почему это лучшая практика, каковы ее плюсы и минусы и почему ее следует выбирать среди других.

Ответы [ 11 ]

63 голосов
/ 18 января 2011

Обходной путь для этого был только что добавлен к жемчужине '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 так же, как в ruby ​​1.9.2 в ruby ​​1.8 и 1.9.1.

45 голосов
/ 02 декабря 2010

До того, как я сделал переход на 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, так что я бы сказал, что это, по крайней мере, достаточно распространенная идиома.

6 голосов
/ 25 октября 2011
$LOAD_PATH << '.'

$LOAD_PATH << File.dirname(__FILE__)

Это не очень хорошая привычка безопасности: зачем вам выставлять весь каталог?

require './path/to/file'

Это не работает, если RUBY_VERSION <1.9.2 </p>

используйте странные конструкции, такие как

require File.join(File.dirname(__FILE__), 'path/to/file')

Еще более странная конструкция:

require File.join(File.expand_path(File.dirname(__FILE__)), 'path/to/file')

Используйте гем бэкпортов - он тяжелый, требует рубинов инфраструктура и включает в себя множество других обходных путей, в то время как я просто хочу требовать работы с относительными файлами.

Вы уже ответили, почему это не лучший вариант.

проверить, если RUBY_VERSION <1.9.2, а затем определить require_relative как require, используйте require_relative везде, где это необходимо впоследствии </p>

проверить, если 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
6 голосов
/ 02 декабря 2010

У Кирки есть фрагмент кода для 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.

5 голосов
/ 04 октября 2012

Я фанат использования гема rbx-require -lative ( source ).Первоначально он был написан для 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, и камень не такой тяжелый, как бэкпорты..

4 голосов
/ 08 марта 2013

Драгоценный камень backports теперь позволяет индивидуальную загрузку бэкпортов.

Тогда вы можете просто:

require 'backports/1.9.1/kernel/require_relative'
# => Now require_relative works for all versions of Ruby

Это require не повлияет на более новые версиии не будет обновлять любые другие встроенные методы.

3 голосов
/ 15 августа 2013

Одна проблема, на которую я не обращал внимания в решениях на основе __FILE__, заключается в том, что они ломаются в отношении символических ссылок. Например, у меня есть:

~/Projects/MyProject/foo.rb
~/Projects/MyProject/lib/someinclude.rb

Основной скрипт, точка входа, приложение foo.rb. Этот файл связан с ~ / Scripts / foo, который находится в моем $ PATH. Этот оператор require прерывается, когда я выполняю 'foo':

require File.join(File.dirname(__FILE__), "lib/someinclude")

Поскольку __FILE__ - это ~ / Scripts / foo, поэтому в приведенном выше заявлении требуется поиск ~ / Scripts / foo / lib / someinclude.rb, который явно не существует. Решение простое. Если __FILE__ является символической ссылкой, необходимо разыменовать ее. Pathname # realpath поможет нам в этой ситуации:

require "pathname"
require File.join(File.dirname(Pathname.new(__FILE__).realpath), "lib/someinclude")
3 голосов
/ 30 марта 2013

Другой вариант - указать интерпретатору, какие пути искать

ruby -I /path/to/my/project caller.rb
2 голосов
/ 01 июля 2011

Если бы вы строили драгоценный камень, вы бы не хотели загрязнять путь загрузки.

Но в случае автономного приложения очень удобно просто добавить текущий каталог в путь загрузки, как вы это делали в первых 2 примерах.

Мой голос переходит к первому варианту в списке.

Я хотел бы увидеть какую-нибудь солидную литературу по лучшим практикам Ruby.

1 голос
/ 02 декабря 2010

Я бы определил свой собственный relative_require, если он не существует (т. Е. Ниже 1.8), а затем использовал бы везде одинаковый синтаксис.

...