Подтвердить что ты не робот

Как я могу сохранить конфигурацию инициализации от потери в режиме разработки?

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

# config/initializers/my_engine.rb
MyEngine::SomeController.after_filter proc {
  # Do something in the host app
}, :only => :update

Это отлично работает в процессе производства, но в режиме разработки proc вызывается только при первом запросе. Это связано с тем, что классы перезагружаются, и эта конфигурация теряется, поскольку она хранилась в переменной класса. (Например, MyEngine::SomeController перезагружается из файла, в котором он находится, а поскольку after_filter не объявлен там, он не добавляется обратно.)

Некорректный фон Rails

В режиме разработки Rails использует следующую стратегию загрузки:

  • Код в каталоге app перезагружается по каждому запросу, исходя из предположения, что вы активно меняете его.
  • Код в каталоге lib вместе с config/initializer файлами загружается один раз, когда приложение загружается.

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

Как двигатели меняют вещи

Тем не менее, у двигателей Rails есть код в каталоге app: контроллеры, модели и т.д. Эти файлы перезагружаются в режиме разработки по каждому запросу. Поэтому конфигурация, подобная приведенному выше примеру, теряется.

Введите в_prepare

Rails предоставляет config.to_prepare специально для решения этой проблемы: он запускается один раз в процессе производства и по каждому запросу в разработке.

Например, мы имеем это в application.rb, который отлично работает:

config.to_prepare do
  # set up class variables (after_filters, etc)
end

Однако, если мне нужно сконфигурировать конфигурацию всех моих двигателей в application.rb, это победит точку config/initializers, если все будет организовано.

Итак, для любой конфигурации классов в каталогах моих двигателей app я хочу поместить этот код в файлы под config/initializers.

Вот мои вопросы.

  • Непонятно, как получить config в область видимости в файле инициализации. Я думаю, что это будет Rails.application.config. Это правильно?
  • Можно ли добавить несколько блоков to_prepare? Боюсь, что вызов его несколько раз перезапишет предыдущие блоки.

Update

Как отметил @Frederick Cheung, Rails.application.config.to_prepare работает в файлах config/initializer, и можно использовать столько из них, сколько необходимо в различных файлах; каждый вызов добавляет свой блок к массиву, поэтому ничего не перезаписывается.

Итак, решение этой проблемы:

# config/initializers/my_engine.rb
Rails.application.config.to_prepare do
  MyEngine::SomeController.after_filter proc {
    # Do something in the host app
  }, :only => :update
end

Одна вещь, которая по-прежнему кажется странной: я ожидал, что блок to_prepare будет вызываться в каждом запросе в режиме разработки, но вместо этого он называется случайным образом каждый третий запрос или так. Я добавил блок:

Rails.application.config.to_prepare do
  Rails.logger.info "Running the prepare block!"
end

... перезапустил мое приложение и обновил страницу девять раз. Я видел сообщение только на 1-м, 5-м, 7-м и 9-м запросах. Я не уверен, что объясняет это поведение, но объясняет, почему мой код без to_prepare работал с перерывами в разработке.

4b9b3361

Ответ 1

Вы можете добавить столько блоков to_prepare, сколько хотите - когда вы делаете config.to_prepare, Rails делает (в configuration.rb в railties)

def to_prepare(&blk)
  to_prepare_blocks << blk if blk
end

а затем итерации по этим блокам передают их на ActionDispatch::Reloader, где to_prepare реализуется с использованием ActiveSupport::Callbacks (т.е. то же самое, что используется для before_save и т.д.). Несколько блоков to_prepare - это нормально.

В настоящее время похоже, что Rails выполняет итерацию через to_prepare_blocks после чтения инициализаторов приложений, поэтому добавление к Rails.application.configuration.to_prepare должно работать. Вы можете использовать ActionDispatch::Reloader.to_prepare.

Ответ 2

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

например

class MyClass
  def self.run_me_when_the_class_is_loaded
  end
end

MyClass.run_me_when_the_class_is_loaded

MyClass.run_me... будет запускаться при загрузке класса... что мы хотим, правильно?

Не уверен, что его Rails-путь... но он очень прост и не зависит от сдвигающихся ветров Rails.