1

I have been getting the familiar DEPRECATION WARNING: Initialization autoloaded the constant <foo> error message for one of my apps.

I have a Rails6 app that uses the dragonfly gem which comes with image resizing processors, and I use them for making thumbnails. I also have a custom processor used for making opengraph versions of images, and as such I need to pass in that configuration to dragonfly on start-up. The use of this specific gem is not strictly relevant, because it could be like any gem where it requires an initializer with a config block to set up on app boot.

The trimmed version of the initializer app/config/initializers/dragonfly.rb:

Dragonfly.app.configure do
  plugin :imagemagick
  processor :open_graph, OpenGraphProcessor.new
  # ... lots more configuration ...
end

The OpenGraphProcessor class lives within app/lib.

I can easily resolve this using the standard approach:

Rails.application.reloader.to_prepare do
  Dragonfly.app.configure do
    plugin :imagemagick
    processor :open_graph, OpenGraphProcessor.new
    # ... lots more configuration ... 
  end
end

My question relates to whether there is a better/more canonical way of handling situations like this where initializers reference classes from within the app itself? Several of my initializers are basically now wrapped in Rails.application.reloader.to_prepare do ... end yet it feels like a hack and perhaps I'm simply missing something.

andyroberts
  • 3,458
  • 2
  • 37
  • 40

1 Answers1

1

There are several ways to address this.

First, let's assume the processor is reloadable.

The gem could ask for a processor class name in its configuration, instead of a processor class instance. When the gem needs the processor, it constantizes the class name. That way, if there is a reload, the gem always gets the current class. Gems that are defined to play well with reloading do this, one case is Devise. If Dragonfly does not support such interface, we need to look for other options.

Because, you know, the problem with the original snippet is that Dragonfly was getting an instance of the OpenGraphProcessor class by the time the app boots. If your class is reloadable, you expect changes to the class to be effective, but the processor in Dragonfly was already instantiated from the previous version of the class and won't reflect these changes.

So, the second way to address this is what you are doing. You configure when the application boots and also when there is a reload. That way, Dragonfly has the current processor. That is what the to_prepare block is doing.

This is not hacky, it is what logically you have to do. The name to_prepare is obscure for my taste, on_boot_and_reload would be more clear. But, in any case, this is what you logically want to do.

It has to be seen if Dragonfly supports being configured in mutliple rounds. If it does not support this, this solution would not be valid for Dragonfly in particular. If that was the case, basically the gem would not designed to work with reloading.

Then, one final option that works in all scenarios is to make the processor non-reloadable. In that case, you can leave the original initializer as it was, because you don't expect reloads to change the processor.

There is two basic ways to do this. One is to have processors in app/processors and add that directory to config.autoload_once_paths in config/application.rb. The other one is moving this particular class to lib (assuming it is not in the autoload paths), and in the initializer issue an ordinary require for the file.

Your application had an inconsistency with reloading this is uncovering. This is an error condition in Rails 7 to prevent such inconsistency from being possible.

Xavier Noria
  • 1,640
  • 1
  • 8
  • 10