Ruby: const_missing, autoload, Zeitwerk и просветление в require

Разбираетесь, как Ruby загружает классы и модули? В этой статье мы детально разберём механизмы автозагрузки в Ruby — от базовых require и load до продвинутых техник вроде const_missing и современного autoload_paths в Rails. Вы узнаете, почему require_relative может быть опасен, как работает ленивая загрузка через autoload, и почему Zeitwerk стал золотым стандартом в Rails-приложениях. Мы ответим на частые вопросы разработчиков: «Почему Ruby не видит мой класс?», «Нужно ли писать require в Rails?» и «Как правильно организовать автозагрузку в больших проектах?» — с примерами кода и лучшими практиками.


🧱 Как Ruby находит классы

Обычно ты пишешь:

require 'json'
puts JSON.parse("{}")

Ruby ищет файл json.rb или json.so в путях из LOAD_PATH.


🧨 require vs require_relative

  • require ищет в глобальном $LOAD_PATH
  • require_relative — от текущего файла
require_relative '../lib/my_tool'

📌 require_relative быстрее и надёжнее в библиотеках и скриптах.


🧪 autoload: отложенная загрузка

autoload :Foo, "foo"

# Класс Foo будет загружен **только при первом обращении**
Foo.new

Ruby отложит загрузку файла foo.rb, пока Foo реально не понадобится. 💡 Устаревает в пользу Zeitwerk (см. ниже).


🧠 const_missing — магия при поиске константы

class Object
  def self.const_missing(name)
    puts "Ищу #{name}..."
    require name.to_s.downcase
    const_get(name)
  end
end

User.new  # => загружается user.rb

Работает в связке с require, но легко сломать всё (например, если в имени ошибка).


🚆 Zeitwerk: автозагрузчик в Rails

Начиная с Rails 6, Zeitwerk — это стандартный загрузчик:

  • Использует const_missing
  • Работает по правилам convention over configuration
  • Требует соблюдения соглашений об именах и путях
# app/models/user.rb
# содержит class User

User.new  # => автозагрузится

🧹 Требования Zeitwerk

  1. Имена классов и файлов должны совпадать

    class SuperBot  # => в super_bot.rb
    
  2. Модули и директории должны совпадать

    module Admin
      class Report  # => в admin/report.rb
    end
    
  3. Не использовать require вручную внутри app/


📦 Как добавить свой путь в автозагрузку

Rails.autoloaders.main.push_dir("#{Rails.root}/lib")

Или создать lib/ с правильными путями и именами классов.


🧨 Вопрос с подвохом

class App
  autoload :Config, "config"
end

App::Config  # Что произойдёт?

✅ Ruby попытается загрузить config.rb, определить Config, и всё будет работать. 💥 Если config.rb определяет Configuration, а не ConfigNameError.


🚨 Почему нельзя просто require 'user' в Rails?

Потому что:

  • Rails использует Zeitwerk
  • Ты рискуешь загрузить вручную, нарушив lifecycle
  • Проблемы при reload в dev-режиме

Используй Rails.autoloaders, а не require.


🔚 Вывод: Ruby умеет находить классы, но только если ты не мешаешь. Знай, что делает const_missing, зачем Zeitwerk, и почему autoload уже не торт. Это проверяют — потому что непонимание механики загрузки ведёт к uninitialized constant, CircularDependencyError и орущему проду.

🗓 Дата публикации: 17.11.2021, но это не точно...

Ruby автозагрузка require load Rails Zeitwerk