测试目录下使用紧凑模块类写法触发NameError错误的问题咨询
uninitialized constant Clients with Compact Class Syntax & Zeitwerk Hey there, let's break down why this is happening and how to fix it—this is a common gotcha with Zeitwerk's autoloading rules!
The Issue Recap
You've got Dry::Validation contracts in test/fixtures/schemas for validating JSON responses. The nested module syntax works perfectly:
module Clients class Show < Dry::Validation::Contract json do required(:id).filled(:string) required(:state).filled(:string) # ... other validation rules end end end
But when you switch to the compact class syntax your Rubocop rules enforce:
class Clients::Show < Dry::Validation::Contract json do required(:id).filled(:string) required(:state).filled(:string) # ... other validation rules end end
You get a NameError: uninitialized constant Clients. You tried adding an empty clients.rb module file but no luck, and couldn't find answers in the Zeitwerk docs.
What's Going On Here?
Zeitwerk's autoloader relies strictly on file and directory naming conventions to map files to constants. When you use Clients::Show, two things need to be true:
- There's a file that defines the
Clientsmodule (typicallyclients.rbin the parent directory of yourshow.rbfile) - The
Showclass lives in aclientssubdirectory (which you already have:test/fixtures/schemas/clients/show.rb)
The problem with the compact syntax is that Ruby tries to look up the Clients constant right away, before Zeitwerk has had a chance to autoload it from your empty clients.rb file. The nested module Clients; class Show... syntax doesn't have this problem because it creates the Clients module inline if it doesn't already exist.
How to Fix It
Let's walk through the steps to get this working with the compact syntax:
1. Make Sure the Clients Module is Defined & Discoverable
First, confirm your test/fixtures/schemas/clients.rb file exists with the explicit module definition:
module Clients # Empty module is fine—we just need Zeitwerk to register this constant end
2. Add the Schemas Directory to Zeitwerk's Autoload Paths
This is probably why your initial empty clients.rb didn't work: Zeitwerk wasn't scanning the test/fixtures/schemas directory.
In a Rails app, add this to your config/application.rb:
# Add the schemas directory to Zeitwerk's autoload paths config.autoload_paths << Rails.root.join("test/fixtures/schemas") # Or if you're using Zeitwerk directly: Rails.autoloaders.main.push_dir(Rails.root.join("test/fixtures/schemas"))
If this isn't a Rails app, make sure your Zeitwerk loader is configured to include the test/fixtures/schemas path when initializing.
3. Explicitly Require the Module (If Needed)
If you still see the error, you can force the Clients module to load before defining the Show class by adding a require at the top of show.rb:
require_relative "../clients" class Clients::Show < Dry::Validation::Contract # ... your validation rules here end
Alternative: Dynamically Create the Module
If you don't want to add a require statement, you can check if the Clients module exists and create it if it doesn't:
# Ensure the Clients module exists before defining the class Clients ||= Module.new class Clients::Show < Dry::Validation::Contract # ... your validation rules here end
Wrap-Up
The core issue was that Zeitwerk wasn't aware of your clients.rb file because the schemas directory wasn't in its autoload paths. Once you fix that, the compact class syntax will work just like the nested module version.
内容的提问来源于stack exchange,提问作者Luiz E.

