fxn/zeitwerk

Zeitwerk not autoload the middleware directory

iAaquibjawed opened this issue · 2 comments

i have created a fresh app and then in app directory i created a folder name middleware inside it i created a file name "catch_json_parse_errors.rb"

the code of that file is below
class CatchJsonParseErrors
def initialize(app)
@app = app
end

def call(env)
byebug
begin
@app.call(env)
rescue ActionDispatch::Http::Parameters::ParseError => error
if env['HTTP_ACCEPT'] =~ /application/json/
log.warn("Invalid JSON submitted to app: #{error}")
return [
400,
{ "Content-Type" => "application/json" },
[ { status: 400, error: "Error in submitted JSON: #{error}" }.to_json ]
]
else
raise error
end
end
end
end

and the application.rb file is below

require_relative "boot"

require "rails/all"

Require the gems listed in Gemfile, including any gems

you've limited to :test, :development, or :production.

Bundler.require(*Rails.groups)

module Mynewapp
class Application < Rails::Application
# config.middleware.use CatchJsonParseErrors

# Initialize configuration defaults for originally generated Rails version.
config.middleware.insert_before Rack::Head, CatchJsonParseErrors
config.load_defaults 7.0

# Configuration for the application, engines, and railties goes here.
#
# These settings can be overridden in specific environments using the files
# in config/environments, which are processed later.
#
# config.time_zone = "Central Time (US & Canada)"
# config.eager_load_paths << Rails.root.join("extras")

end
end
when i start my rails app i get the below error

/home/sam/Desktop/practice/mynewapp/config/application.rb:14:in `class:Application': uninitialized constant Mynewapp::Application::Middleware (NameError)

config.middleware.insert_before Rack::Head, Middleware::CatchJsonParseErrors
                                            ^^^^^^^^^^

but if i add the below lines in application.rb file i won't get the above error.

require_relative "../app/middleware/catch_json_parse_errors"

now question here is why does zeitwerk not autoload the middleware directory.
Screenshot from 2023-09-09 00-34-32

fxn commented

The key observation is that by storing the file under app, you've made it reloadable.

Reloadable code, by design, is not available until the application has booted. Because usage of reloadable objects before the application has booted would not reflect reloaded versions on edits.

In your case, if you could autoload so early and push the middleware to the stack, if you edited app/middleware/catch_json_parse_errors.rb and reloaded, the middleware object in the stack would not reflect the changes. The middleware stack is not reloaded (in general, the objects in the stack are not even reloadable).

So, this is working as intended.

You need to put the file in a directory that does not belong to the autoload paths, and issue a regular require or require_relative to load it before config.middleware.use.

fxn commented

For example, if the app does not add lib to the autoload paths, then you can put the middleware in lib/middleware, for example.

If it does, you can still put it there and tell the loader to ignore it

# Somewhere in config/application.rb.
Rails.autoloaders.main.ignore("#{Rails.root}/lib/middleware")`

You see the idea.

The command

bin/rails r 'pp ActiveSupport::Dependencies.autoload_paths'

prints the autoload paths.

If the middleware is edited, the application needs to be restarted to have the stack updated.