Чем load отличается от require в Ruby?

Ответы:

98

requireвыполняет поиск библиотеки по всем заданным путям поиска, а также добавляет .rb или .so к имени файла, которое вы вводите. Это также гарантирует, что библиотека включается только один раз. Поэтому, если вашему приложению требуются библиотеки A и B и библиотека B, библиотека A тоже A будет загружена только один раз.

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

Николаус Градволь
источник
Спасибо, сэр, поэтому мы можем сказать, что .rb не требуется для загрузки. И "require" не вызывает все время, если он в памяти?
Arpit Vaishnav
4
requireотслеживает то, что уже было загружено через глобальный массив $LOADED_FEATURES( $"), который loadигнорирует.
Чиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
39

Еще одно различие между Kernel#requireи Kernel#loadзаключается в том, что он Kernel#loadпринимает необязательный второй аргумент, позволяющий обернуть загруженный код в анонимный пустой модуль.

К сожалению, это не очень полезно. Во-первых, loadредактируемому коду легко вырваться из модуля, просто получив доступ к глобальному пространству имен, то есть они все еще могут запрограммировать что-то вроде class ::String; def foo; end end. А во-вторых, loadне возвращает модуль, в который заключен код, поэтому вам в основном придется выудить его ObjectSpace::each_object(Module)вручную.

Йорг В. Миттаг
источник
Очень приятно знать! Понятия не Kernel#loadимел, есть еще один аргумент
Джош Бода
К сожалению, это не очень полезно. Во-первых, loadредактируемому коду легко выйти из модуля, просто получив доступ к глобальному пространству имен, то есть они все еще могут запрограммировать что-то вроде class ::String; def foo; end end. А во-вторых, loadне возвращает модуль, в который заключен код, поэтому вам в основном придется выудить его ObjectSpace::each_object(Module)вручную.
Jörg W Mittag
4

Я запускал приложение Rails, и в Gemfile у меня был специальный гем, который я создал с опцией «require: false». Теперь, когда я загрузил сервер rails или консоль rails, я смог потребовать гем в инициализаторе, и гем был загружен. Однако, когда я провел тест функции спецификации с помощью rspec и capybara, у меня возникла ошибка загрузки. И я был совершенно сбит с толку, почему Gem не был найден в $ LOAD_PATH при запуске теста.

Итак, я рассмотрел все различные способы взаимодействия load, require, rubygems и Bundler. И это краткое изложение моих выводов, которые помогли мне найти решение моей конкретной проблемы:

грузить

1) Вы можете передать ему абсолютный путь к файлу ruby, и он выполнит код в этом файле.

load('/Users/myuser/foo.rb')

2) Вы можете передать относительный путь для загрузки. Если вы находитесь в том же каталоге, что и файл, он найдет его:

> load('./foo.rb')
foo.rb loaded!
=> true

Но если вы попытаетесь загрузить файл из другого каталога с помощью load (), он не найдет его с относительным путем, основанным на текущем рабочем каталоге (например, ./):

> load('./foo.rb')
LoadError: cannot load such file -- foo.rb

3) Как показано выше, load всегда возвращает true (если файл не может быть загружен, возникает a LoadError).

4) Импортируются глобальные переменные, классы, константы и методы, но не локальные переменные.

5) Двойной вызов load для одного и того же файла приведет к двойному выполнению кода в этом файле. Если указанный файл определяет константу, он будет определять эту константу дважды, что вызывает предупреждение.

6) $ LOAD_PATH - массив абсолютных путей. Если вы передадите load только имя файла, он будет перебирать $ LOAD_PATH и искать файл в каждом каталоге.

> $LOAD_PATH.push("/Users/myuser")
> load('foo.rb')
foo.rb loaded!
 => true

требовать

1) Вызов require для одного и того же файла дважды выполнит его только один раз. Также достаточно умен, чтобы не загружать один и тот же файл дважды, если вы обращаетесь к нему один раз с относительным путем и один раз с абсолютным путем.

2) require возвращает true, если файл был выполнен, и false, если нет.

3) require отслеживает, какие файлы уже были загружены в глобальную переменную $ LOADED_FEATURES.

4) Вам не нужно указывать расширение файла:

require 'foo'

5) require будет искать foo.rb, а также файлы динамической библиотеки, такие как foo.so, foo.o или foo.dll. Вот как вы можете вызвать код C из ruby.

6) require не проверяет текущий каталог, поскольку текущий каталог по умолчанию не находится в $ LOAD_PATH.

7) require_relative принимает путь относительно текущего файла, а не рабочего каталога процесса.

Рубигемы

1) Rubygems - это менеджер пакетов, предназначенный для простого управления установкой библиотек Ruby, называемых гемами.

2) Он упаковывает свое содержимое в виде zip-файла, содержащего кучу файлов ruby ​​и / или файлов динамической библиотеки, которые могут быть импортированы вашим кодом вместе с некоторыми метаданными.

3) Rubygems заменяет метод require по умолчанию собственной версией. Эта версия будет просматривать ваши установленные гемы в дополнение к каталогам в $ LOAD_PATH. Если Rubygems найдет файл в ваших гемах, он добавит этот гем в ваш $ LOAD_PATH.

4) Команда gem install определяет все зависимости драгоценного камня и устанавливает их. Фактически, он устанавливает все зависимости гема перед установкой самого гема.

Bundler

1) Bundler позволяет вам указать все драгоценные камни, необходимые вашему проекту, и, при необходимости, какие версии этих драгоценных камней. Затем команда bundle устанавливает все эти драгоценные камни и их зависимости.

2) Вы указываете, какие драгоценные камни вам нужны в файле Gemfile.

3) Команда bundle также устанавливает все драгоценные камни, перечисленные в Gemfile.lock, в конкретных перечисленных версиях.

4) Размещение пакета exec перед командой, например, bundle exec rspec, гарантирует, что require загрузит версию гема, указанную в вашем Gemfile.lock.

Рельсы и бандлер

1) В config / boot.rb запускается require 'bundler / setup'. Bundler гарантирует, что Ruby сможет найти все драгоценные камни в Gemfile (и все их зависимости). require 'bundler / setup' автоматически обнаружит ваш Gemfile и сделает все драгоценные камни в вашем Gemfile доступными для Ruby (с технической точки зрения он помещает драгоценные камни "на путь загрузки"). Вы можете думать об этом как о добавлении некоторых дополнительных способностей, требующих «рубиновых камней».

ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])

2) Теперь, когда ваш код доступен для Ruby, вы можете потребовать драгоценные камни, которые вам нужны. Например, вы можете потребовать «синатру». Если у вас много зависимостей, вы можете сказать «требовать все драгоценные камни в моем Gemfile». Для этого поместите следующий код сразу после require 'bundler / setup':

Bundler.require(:default)

3) По умолчанию для вызова Bundler.require потребуется каждый гем в вашем Gemfile. Если в строке Gemfile написано gem 'foo',: require => false, тогда он будет удостовериться, что foo установлен, но не будет вызывать require. Вам нужно будет вызвать require ('foo'), если вы хотите использовать драгоценный камень.

Итак, учитывая такой объем знаний, я вернулся к проблеме моего теста и понял, что мне нужно явно требовать драгоценный камень в rails_helper.rb, поскольку Bundler.setup добавил его в $ LOAD_PATH, но require: false не позволял Bundler.require явно требовать его . И тогда вопрос был решен.

Донато
источник