Mind Dump, Tech And Life Blog
written by Ivan Alenko
published under license CC4-BY
posted at 01. Apr '22

When to_prepare Hook Is Too Late

When I upgraded to Ruby on Rails 6.1, I had to deal with preload problems:

damon@rapthalia:~/oni_sorceress> bin/rails s
=> Booting Puma
=> Rails 6.1.4.1 application starting in development 
=> Run `bin/rails server --help` for more startup options
Exiting
/home/damon/.rbenv/versions/3.0.3/lib64/ruby/gems/3.0.0/gems/actionpack-6.1.4.1/lib/action_dispatch/middleware/stack.rb:144:in `push': can't modify frozen Array: [Webpacker::DevServerProxy, ActionDispatch::HostAuthorization, Rack::Sendfile, ActionDispatch::Static, ActionDispatch::Executor, ActiveSupport::Cache::Strategy::LocalCache::Middleware, Rack::Runtime, Rack::MethodOverride, ActionDispatch::RequestId, ActionDispatch::RemoteIp, Sprockets::Rails::QuietAssets, Rails::Rack::Logger, ActionDispatch::ShowExceptions, WebConsole::Middleware, ActionDispatch::DebugExceptions, ActionDispatch::ActionableExceptions, ActionDispatch::Reloader, ActionDispatch::Callbacks, ActiveRecord::Migration::CheckPending, ActionDispatch::Cookies, ActionDispatch::Session::CookieStore, ActionDispatch::Flash, ActionDispatch::ContentSecurityPolicy::Middleware, ActionDispatch::PermissionsPolicy::Middleware, Rack::Head, Rack::ConditionalGet, Rack::ETag, Rack::TempfileReaper] (FrozenError)



DEPRECATION WARNING: Initialization autoloaded the constants ApplicationRecord and AnalyticsHttpRequest.

Being able to do this is deprecated. Autoloading during initialization is going
to be an error condition in future versions of Rails.

Reloading does not reboot the application, and therefore code executed during
initialization does not run again. So, if you reload ApplicationRecord, for example,
the expected changes won't be reflected in that stale Class object.

These autoloaded constants have been unloaded.

In order to autoload safely at boot time, please wrap your code in a reloader
callback this way:

    Rails.application.reloader.to_prepare do
      # Autoload classes and modules needed at boot time here.
    end

That block runs when the application boots, and every time there is a reload.
For historical reasons, it may run twice, so it has to be idempotent.

Check the "Autoloading and Reloading Constants" guide to learn more about how
Rails autoloads and reloads.
 (called from <top (required)> at /home/damon/oni_sorceress/config/environment.rb:5)
.........

The problem with to_prepare hook was that it was too late. I got cannot add middleware to a frozen array error.

I won’t lie, it took me months to fix that, main motivation was upgrade to Ruby on Rails 7.

Fortunately there are more hooks - https://guides.rubyonrails.org/configuring.html#initialization-events and before_initialize worked just fine. I have to manually load model file which is used by middleware though.

require Rails.root.join('app', 'models', 'application_record').to_s
require Rails.root.join('app', 'models', 'oni_sorceress_record').to_s
require Rails.root.join('app', 'models', 'oni_sorceress', 'analytics').to_s
require Rails.root.join('app', 'models', 'oni_sorceress', 'analytics', 'request').to_s

Rails.configuration.before_initialize do
  Rails.application.config.middleware.use(
    RackRequestObjectLogger,
    ::OniSorceress::Analytics::Request
  )
end

So…that’s it.

Add Comment