Добавление каталога в $ LOAD_PATH (Ruby)

96

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

Первый подробный метод (может быть излишним):

$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__))) unless $LOAD_PATH.include?(File.expand_path(File.dirname(__FILE__)))

и более простой, быстрый и грязный:

$:.unshift File.dirname(__FILE__)

Есть ли причина выбрать одно вместо другого?

Марк W
источник
2
Чуть менее многословным версия многословный является:File.expand_path(File.dirname(__FILE__)).tap {|pwd| $LOAD_PATH.unshift(pwd) unless $LOAD_PATH.include?(pwd)}
Nathan Long
как насчет оговорки "если"? Как эти два могут быть эквивалентными?
inger
Как человек, который пришел сюда, чтобы попытаться понять, как это использовать, это супер загадочно. Я не вижу, откуда взялось имя каталога в примерах. Я был бы признателен, если бы кто-нибудь мог прояснить это.
SlySherZ 01
1
Использование __dir__(начиная с Ruby 2.0) может сделать любой из них более кратким.
Натан Лонг,

Ответы:

52

Я бы посоветовал $:.unshift File.dirname(__FILE__)использовать другой вариант просто потому, что я видел гораздо больше его использования в коде, чем $LOAD_PATHтот, и он тоже короче!

Райан Бигг
источник
Когда я только начал работать с Ruby, я, очевидно, думал, что $ LOAD_PATH лучше. Но как только вы выйдете из статуса новичка, я бы использовал $ LOAD_PATH только в том случае, если бы пытался сделать свой код более читабельным для новичка. Мех, это компромисс. Это зависит от того, насколько «общедоступным» является код, при условии, что использование памяти одинаково для каждого, что, как я предполагаю, по сути так и есть.
boulder_ruby
9
Зависит от руководства по стилю, которому вы следуете в своем проекте. В популярном Руководстве по стилю Ruby говорится: «Избегайте использования специальных переменных в стиле Perl (например, $ :, $; и т. Д.). Они довольно загадочны, и их использование в любых сценариях, кроме однострочных, не рекомендуется».
bobmagoo
153

Путь загрузки Ruby очень часто обозначается как $:, но то, что он короткий, не делает его лучше. Если вы предпочитаете ясность уму или если краткость сама по себе вызывает у вас зуд, вам не нужно делать это только потому, что все остальные. Сказать привет ...

$LOAD_PATH

... и попрощайся с ...

# I don't quite understand what this is doing...
$:

источник
29
Кроме того, Google намного сложнее использовать такие строки, как «$:», которые содержат только символы.
DSimon
23

Я не слишком люблю «быстрый и грязный» путь. Любой новичок в Ruby будет задумываться о том, что это $:.такое.

Я считаю это более очевидным.

libdir = File.dirname(__FILE__)
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)

Или, если мне важен полный путь ...

libdir = File.expand_path(File.dirname(__FILE__))
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)

ОБНОВЛЕНИЕ 2009/09/10

В последнее время я делаю следующее:

$:.unshift(File.expand_path(File.dirname(__FILE__))) unless
    $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))

Я видел это во множестве различных проектов Ruby, просматривая GitHub.

Кажется, условность?

Люк Антинс
источник
@LukeAntins, это действительно здорово, но где я должен "загружать" load_path в приложении?
gaussblurinc 06
@gaussblurinc Где-то «в верхней части» вашей библиотеки / приложения, но это действительно зависит. Если у вас был binфайл, который всегда относился к вашему, codeи он когда-либо запускался только binфайлом ... bootstrap в корзине. Если у вас есть библиотека, то начните загрузку в верхней части кода библиотеки, например, lib/code.rbчтобы получить доступ ко всему, что находится ниже lib/code/. Надеюсь, эта прогулка поможет!
Люк Антинс,
1
RuboCop сообщает мне, что __dir__можно использовать для получения пути к каталогу текущего файла.
Рафаэль
8

Если вы script/consoleвведете свой проект Rails и войдете $:, вы получите массив, включающий все каталоги, необходимые для загрузки Ruby. Вывод из этого небольшого упражнения $:- это массив. В этом случае вы можете выполнять с ним функции, такие как добавление других каталогов с помощью unshiftметода или <<оператора. Как вы подразумевали в своем заявлении, так $:и $LOAD_PATHостались.

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

Пример:

У меня есть созданный мной плагин под названием todo. Моя директория устроена так:

/ --- поставщик
  |
  | --- / плагины
        |
        | --- / todo
              |
              | --- / lib
                    |
                    | --- / приложение
                          |
                          | --- / модели
                          | --- / контроллеры
              |
              | --- / рельсы
                    |
                    | --- init.rb

В файле init.rb я ввел следующий код:

## In vendor/plugins/todo/rails/init.rb
    %w{ models controllers models }.each do |dir|
      path = File.expand_path(File.join(File.dirname(__FILE__), '../lib', 'app', dir))
      $LOAD_PATH << path
      ActiveSupport::Dependencies.load_paths << path
      ActiveSupport::Dependencies.load_once_paths.delete(path)
    end 

Обратите внимание, как я говорю блоку кода выполнять действия внутри блока со строками «модели», «контроллеры» и «модели», где я повторяю «модели». (К вашему сведению, %w{ ... }это еще один способ сказать Ruby, что нужно хранить массив строк). Когда я бегу script/console, я набираю следующее:

>> puts $:

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

...
...
./Users/Me/mySites/myRailsApp/vendor/plugins/todo/lib/app/models
./Users/Me/mySites/myRailsApp/vendor/plugins/todo/lib/app/controllers
./Users/Me/mySites/myRailsApp/vendor/plugins/todo/lib/app/models

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

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

Дыба
источник
Ваш ответ очень полезен и хорошо объяснен. Предлагаемое изменение: метод load_pathsи load_once_paths.deleteустарели. Было бы ActiveSupport::Dependencies.autoload_paths << path ActiveSupport::Dependencies.autoload_once_paths.delete(path)
полезно
8

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

$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
Дэйв Робертсон
источник
1

Есть жемчужина, которая позволит вам настроить путь загрузки с более красивым и чистым кодом. Проверьте это: https://github.com/nayyara-samuel/load-path .

Также есть хорошая документация

Рубист
источник
-2

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

У меня есть несколько приложений Ruby, которые были разработаны другим программистом в течение нескольких лет, и они повторно используют одни и те же классы в разных приложениях, хотя могут обращаться к одной и той же базе данных. Поскольку это нарушает правило DRY, я решил создать библиотеку классов, которая будет использоваться всеми приложениями Ruby. Я мог бы поместить его в основную библиотеку Ruby, но это скроет пользовательский код в общей кодовой базе, чего я не хотел делать.

У меня возникла проблема, связанная с конфликтом имен между уже определенным именем «profile.rb» и классом, который я использовал. Этот конфликт не был проблемой, пока я не попытался создать общую библиотеку кода. Обычно Ruby сначала ищет местоположения приложений, а затем переходит в местоположения $ LOAD_PATH.

Application_controller.rb не смог найти созданный мною класс и выдал ошибку в исходном определении, потому что это не класс. Поскольку я удалил определение класса из раздела app / models приложения, Ruby не смог найти его там и стал искать его в путях Ruby.

Итак, я изменил переменную $ LOAD_PATH, включив в нее путь к каталогу библиотеки, который я использовал. Это можно сделать в файле environment.rb во время инициализации.

Даже с добавлением нового каталога в путь поиска Ruby выдавал ошибку, потому что сначала предпочтительно брал файл, определенный системой. Путь поиска в переменной $ LOAD_PATH в первую очередь ищет пути Ruby.

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

Этот код сделал это в файле environment.rb:

Rails::Initializer.run do |config|

* * * * *

path = []
path.concat($LOAD_PATH)
$LOAD_PATH.clear
$LOAD_PATH << 'C:\web\common\lib'
$LOAD_PATH << 'C:\web\common'
$LOAD_PATH.concat(path)

* * * * *

end

Я не думаю, что вы можете использовать какие-либо расширенные конструкции кодирования, представленные ранее на этом уровне, но они отлично работают, если вы хотите что-то настроить во время инициализации в своем приложении. Вы должны сохранить исходный порядок исходной переменной $ LOAD_PATH, когда она добавляется обратно в новую переменную, иначе некоторые из основных классов Ruby будут потеряны.

В файле application_controller.rb я просто использую

require 'profile'
require 'etc' #etc

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

Для меня это было решение, которое я искал, и я подумал, что добавлю его к этому ответу, чтобы передать информацию.

Тимоти Дулинг
источник