Skip to content

[BUG][RUBY-NEXTGEN] Nothing autoloads when moduleName is not the camelized gemName #24997

Description

@wiebren

Bug Report Checklist

  • Have you provided a full/minimal spec to reproduce the issue?
  • Have you validated the input using an OpenAPI validator?
  • Have you tested with the latest master to confirm the issue still exists?
  • Have you searched for related issues/PRs?
  • What's the actual output vs expected output?
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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions