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.
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
.
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.