В чём разница между require и require_relative в Ruby?
require ищет файл по $LOAD_PATH и загружает его один раз по абсолютному имени. require_relative ищет относительно текущего файла и не зависит от $LOAD_PATH.
require
require 'name' ищет файл name.rb (или нативное расширение name.so/name.bundle) в директориях массива $LOAD_PATH. Ruby обходит их слева направо и загружает первый найденный файл. Загруженные пути записываются в $LOADED_FEATURES — повторный require того же пути возвращает false и не выполняет файл снова.
$LOAD_PATH.unshift('./lib')
require 'my_gem' # ищет lib/my_gem.rb
require_relative
require_relative 'path' строит абсолютный путь относительно файла, содержащего вызов (__FILE__). $LOAD_PATH при этом не используется. Это удобно для внутренних зависимостей внутри проекта.
# lib/app/models/user.rb
require_relative 'address' # lib/app/models/address.rb
require_relative '../services/auth' # lib/app/services/auth.rb
Ключевые отличия
- Поиск:
require— через$LOAD_PATH;require_relative— относительно__FILE__. - Переносимость:
require_relativeвсегда найдёт файл независимо от того, откуда запущен скрипт;requireможет не найти, если директория не добавлена в$LOAD_PATH. - Гемы: публичный API гема пишет
require 'gemname'; внутренние файлы используютrequire_relative. - eval / irb:
require_relativeвeval/irbбросаетLoadError, потому что__FILE__там равно(eval)— нет файловой базы.
Пример структуры гема
# lib/mylib.rb — точка входа (публичный require 'mylib')
require_relative 'mylib/version'
require_relative 'mylib/client'
require_relative 'mylib/errors'
module Mylib
# ...
end
autoload как альтернатива
Ruby также предоставляет autoload :ClassName, 'path' — файл загружается лениво при первом обращении к константе. Удобно для ускорения старта, но создаёт потенциальные проблемы с потокобезопасностью в Ruby < 2.0.
module Mylib
autoload :Client, 'mylib/client'
autoload :VERSION, 'mylib/version'
end
Подводные камни
- Если запустить скрипт через
ruby script.rbиз другой директории,require './lib/foo'сломается, аrequire_relative 'lib/foo'— нет. $LOADED_FEATURESхранит абсолютные пути, поэтому один и тот же файл можно загрузить дважды, еслиrequireиrequire_relativeразрешились в разные пути (например, через симлинк).- Порядок в
$LOAD_PATHважен: Bundler prepend'ит пути гемов в начало, что может перекрыть системные версии. - В Ruby 1.9
.(текущая директория) была убрана из$LOAD_PATHпо соображениям безопасности —require './foo'больше не работает без явного добавления. - Циклические зависимости через
require/require_relativeне вызывают ошибку, но константы могут оказатьсяnilв момент первой загрузки. require_relativeнельзя использовать в-eоднострочниках командной строки.
Common mistakes
- Сводить require vs require relative к названию метода без lifecycle и failure path.
- Игнорировать модель runtime: объектная динамическая модель Ruby, где почти всё является объектом, а методы ищутся через ancestor chain.
- Не отделять validation, authorization, transaction boundary и business logic.
- Менять похожие API местами без учёта семантики ошибок и ownership.
What the interviewer is testing
- Объясняет require vs require relative через конкретную точку lifecycle в Ruby.
- Приводит корректный минимальный пример без вымышленных методов или callbacks.
- Называет edge cases: пустые значения, ошибки, транзакции, безопасность или concurrency.