Вероятно, это результат перехода на новый автозагрузчик Zeitwerk. В вашем коде обнаружена ошибка, из-за которой работал старый автозагрузчик Rails.
class Site::BlockPage
has_one :metadata, dependent: :destroy, class_name: "Site::Page::Metadata", foreign_key: "page_id"
end
Здесь Site::Page::Metadata
неоднозначно. Это может означать Site::BlockPage::Site::Page::Metadata
или Site::Page::Metadata
.
Если Site::Page::Metadata
уже загружен, у вас все хорошо.
Если это не так, автозагрузчик Rails вступает во владение.
До Zeitwerk это работало из-за работы автозагрузчика Rails. Это будет ловить исключение, когда константа отсутствует, и затем искать путь к файлу, который соответствует. В этом случае он попытается Site::BlockPage::Site::Page::Metadata
, получит исключение и будет искать site/block_page/site/page/metadata.rb
. Этого не существует Этого не существует, поэтому он пытается Site::Page::Metadata
и ищет site/page/metadata.rb
. Это существует, и это то, что загружается.
Zeitwerk делает обратное. Когда Rails запускается, он сканирует ваши каталоги, выводит имя класса из имени файла и регистрирует его для автоматической загрузки из этого файла. Например, если он находит app/models/site/page/metadata.rb
, он запускает Site::Page.autoload(:Metadata, app/models/site/page/metadata.rb)
, регистрируя ссылки на Site::Page::Metadata
для загрузки app/models/site/page/metadata.rb
. Константа Site::Page::Metadata
существует, но ее содержимое еще не загружено.
Когда Rails 6 ищет Site::BlockPage::Site::Page::Metadata
, он получает исключение и все. Zeitwerk не улавливает исключение и продолжает поиск, как это делал старый автозагрузчик.
Поскольку Zeitwerk создает константы для каждого класса при запуске, это намного надежнее. Это не зависит от порядка загрузки классов. Но он не совпадает с особенностями старого автозагрузчика Rails.
По этой и другим причинам предпочтительно указывать вложенные классы и модули. И очень важно, чтобы путь к файлу соответствовал ожиданиям Rails.
# app/models/site/block_page.rb
module Site
class BlockPage < ApplicationRecord
has_one :metadata, dependent: :destroy, class_name: "Site::Page::Metadata", foreign_key: "page_id"
end
end
# app/models/site/page/metadata.rb
module Site
class Page
class Metadata < ApplicationRecord
belongs_to :page, class_name: "Site::BlockPage"
end
end
end
Вы можете увидеть, как Zeitwerk загружает ваши классы с помощью Rails.autoloaders.main
или бросая Rails.autoloaders.log!
в config/application.rb
. См. Загрузка и перезагрузка констант (режим Zeitwerk) - Устранение неполадок .
tl; dr
См. Понимание Zeitwerk в Rails 6 Марсело Касираги.