Bug Report Checklist
Description
When moduleName is not the camelized gemName (e.g. gemName=things_api, moduleName=Things), none of the generated constants load. For a single-level moduleName, gem.mustache sets Zeitwerk up with Zeitwerk::Loader.for_gem, which expects lib/things_api/** to define constants under ThingsApi. The generated files define them under Things, so Things::Client is never autoloaded and eager_load fails on ThingsApi.
gem.mustache already has a loader that works (Zeitwerk::Loader.new + push_dir(..., namespace: self)), but it only emits it when moduleName is nested.
openapi-generator version
master, 7.26.0-SNAPSHOT, 05b61f34d7fb0199330e1d6c57e6159f72427837
OpenAPI declaration file content or url
openapi: 3.0.3
info:
title: zeitwerk
version: 1.0.0
paths:
/things:
get:
operationId: listThings
responses:
'200':
description: OK
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Thing'
components:
schemas:
Thing:
type: object
properties:
name:
type: string
Generation Details
java -jar openapi-generator-cli.jar generate -g ruby-nextgen -i spec.yaml -o out \
--additional-properties=gemName=things_api,moduleName=Things
Generated lib/things_api.rb (comments trimmed):
module Things
class << self
attr_reader :loader
end
@loader = Zeitwerk::Loader.for_gem
@loader.setup
def self.eager_load!
@loader.eager_load
end
end
Steps to reproduce
$LOAD_PATH.unshift 'out/lib'
require 'things_api'
Things::Client.new(base_url: 'http://localhost')
Actual (ruby 3.3, zeitwerk 2.8.3):
uninitialized constant Things::Client (NameError)
Things.eager_load! raises uninitialized constant ThingsApi (NameError).
Expected: the client loads, as it does with gemName=things.
Related issues/PRs
None found.
Suggest a fix
Always use the explicit loader. push_dir(namespace: self) doesn't depend on gemName matching moduleName. With this change the snippet works for both gemName=things_api and gemName=things, and Things::VERSION still resolves.
--- a/modules/openapi-generator/src/main/resources/ruby-nextgen/gem.mustache
+++ b/modules/openapi-generator/src/main/resources/ruby-nextgen/gem.mustache
@@ -21,10 +21,9 @@
attr_reader :loader
end
-{{#moduleNameNested}}
- # Nested moduleName: for_gem can't target a nested namespace, so drive Zeitwerk
- # explicitly. The parent module(s) above are pre-defined so `module {{moduleName}}`
- # (here and in every generated file) reopens rather than raising NameError.
+ # for_gem derives the namespace from gemName, which need not match moduleName (and
+ # can't target a nested one), so drive Zeitwerk explicitly. Any parent module(s) above
+ # are pre-defined so `module {{moduleName}}` reopens rather than raising NameError.
@loader = Zeitwerk::Loader.new
@loader.push_dir("#{__dir__}/{{gemName}}", namespace: self)
# version.rb defines VERSION (not a `Version` class). for_gem's GemInflector special-cases
@@ -32,10 +31,6 @@
# {{moduleName}}::VERSION available.
@loader.ignore("#{__dir__}/{{gemName}}/version.rb")
require_relative "{{gemName}}/version"
-{{/moduleNameNested}}
-{{^moduleNameNested}}
- @loader = Zeitwerk::Loader.for_gem
-{{/moduleNameNested}}
{{#zeitwerkInflections.0}}
# Model names keep their acronym casing (e.g. HTTPConfig), but Zeitwerk's default
# inflector would expect HttpConfig from the file name. Register the exceptions so
Generated with Claude Code
Bug Report Checklist
Description
When
moduleNameis not the camelizedgemName(e.g.gemName=things_api,moduleName=Things), none of the generated constants load. For a single-levelmoduleName,gem.mustachesets Zeitwerk up withZeitwerk::Loader.for_gem, which expectslib/things_api/**to define constants underThingsApi. The generated files define them underThings, soThings::Clientis never autoloaded andeager_loadfails onThingsApi.gem.mustachealready has a loader that works (Zeitwerk::Loader.new+push_dir(..., namespace: self)), but it only emits it whenmoduleNameis nested.openapi-generator version
master, 7.26.0-SNAPSHOT,
05b61f34d7fb0199330e1d6c57e6159f72427837OpenAPI declaration file content or url
Generation Details
Generated
lib/things_api.rb(comments trimmed):Steps to reproduce
Actual (ruby 3.3, zeitwerk 2.8.3):
Things.eager_load!raisesuninitialized constant ThingsApi (NameError).Expected: the client loads, as it does with
gemName=things.Related issues/PRs
None found.
Suggest a fix
Always use the explicit loader.
push_dir(namespace: self)doesn't depend ongemNamematchingmoduleName. With this change the snippet works for bothgemName=things_apiandgemName=things, andThings::VERSIONstill resolves.Generated with Claude Code