Как подключиться к Rails * после того, как * файлы перезагружаются по каждому запросу в режиме разработки?

Я работаю над gem, который динамически устанавливает свойства на моделях ActiveRecord (например, table_name) на основе пользовательской конфигурации.

У меня есть инициализатор, который достигает этого. Однако моя проблема заключается в том, что в режиме dev эти классы перезагружаются, поэтому они не поддерживают эти значения.

Итак, я подумал, что я использую railtie, чтобы подключиться к точке, где эти файлы перезагружены, и снова запустите мою конфигурацию на моделях. Моя проблема, однако, в том, что config.to_prepare в railtie, кажется, работает до того, как reload! действительно имеет место. Я могу доказать это с небольшим количеством протоколирования:

module MyMod
  class Railtie < Rails::Railtie

    config.to_prepare do
      Rails.logger.debug("Contact object_id: #{Contact.object_id}")
    end
  end
end

Если я загружу свою консоль, я получаю первый журнал:

Contact object_id: 2202692040

Если я проверяю Contact.object_id, это соответствует:

Contact.object_id  #=> 2202692040

Тогда я reload!

reload!

Логгер Rails из моих журналов to_prepare:

Contact object_id: 2202692040

Итак, у него все еще есть старый объект_ид, но когда я проверяю его на консоли:

Contact.object_id  #=> 2197355080

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

Итак, как мне получить to_prepare для запуска после перезагрузки файлов? Используя Rails 3.0.10

Обновление

Я также попытался вручную связать это действие с обратным вызовом after_prepare на ActionDispatch::Callbacks следующим образом:

initializer "apartment.init" do
  ActionDispatch::Callbacks.set_callback(:prepare, :after) do
    Rails.logger.debug("Contact object_id: #{Contact.object_id}")
  end
end

Он действительно выполняет обратный вызов после config.to_prepare, но по-прежнему происходит before файлы перезагружаются... Я получаю то же поведение, что и выше.

Ответ 1

Напишите инициализатор, который, если cache_classes равен false, использует ActionDispatch::Reloader для установки обратного вызова to_prepare который запускает вашу процедуру установки gem.

initializer 'foobar.install' do
  if Rails.configuration.cache_classes
    FooBar.install!
  else
    ActionDispatch::Reloader.to_prepare do
      FooBar.install!
    end
  end
end

Это будет работать как в консоли с reload! метод и в стойке сервера приложений.

Ответ 2

Я считаю, что перезагрузщик Rails только отцепляет константы. Модели перезагружаются автозагрузкой, когда константы ссылаются в вашем приложении.

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

model_names.each { |model_name| model_name.constantize }

Вы можете создать список self.included:

module MyGem
  self.included(base)
    @model_names ||= Set.new
    @model_names += base.to_s
  end
end