Skip to content

[ruby-nextgen] fix: autoload broken when moduleName differs from camelized gemName - #25545

Open
Inf1mum wants to merge 5 commits into
OpenAPITools:masterfrom
Inf1mum:fix/ruby-nextgen-zeitwerk-loader
Open

Inf1mum wants to merge 5 commits into
OpenAPITools:masterfrom
Inf1mum:fix/ruby-nextgen-zeitwerk-loader

Conversation

@Inf1mum

@Inf1mum Inf1mum commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #24997.

ruby-nextgen used Zeitwerk::Loader.for_gem when moduleName is a single level (not nested). for_gem derives its root namespace from the gem name, so with gemName=things_api, moduleName=Things it targeted ThingsApi while the generated files define constants under Things — nothing autoloaded, require 'things_api' then Things::Client raised NameError, and Things.eager_load! failed on uninitialized constant ThingsApi.

What changed

  • ruby-nextgen/gem.mustache: always drive Zeitwerk explicitly (Zeitwerk::Loader.new + push_dir(..., namespace: self)), removing the moduleNameNested branch that fell back to for_gem. The explicit loader never depended on gemName matching moduleName, and the nested case already used it — so this only changes behavior for the previously broken single-level case. Suggested in the issue by the reporter.

Verification

  • New unit test testZeitwerkLoaderWhenModuleNameDiffersFromGemName generates with gemName=things_api, moduleName=Things and asserts the explicit push_dir(namespace: self) loader is emitted and for_gem is not. RubyNextgenClientCodegenTest: 24 tests, 0 failures.
  • Regenerated both ruby-nextgen sample configs (matching gemName/moduleName, so only the loader lines change); ruby -c syntax check passes on the regenerated gems.
  • The issue reporter verified the same change restores require 'things_api' + Things::Client.new(...) for the mismatching pair while gemName=things keeps working.

PR checklist

  • Read the contribution guidelines.
  • Regenerated the affected samples via ./bin/generate-samples.sh (both ruby-nextgen configs); no files deleted.
  • Pull request is created against the master branch.

Summary by cubic

Fixes ruby-nextgen autoloading when moduleName differs from the camelized gemName, URI-encodes Dart-dio path parameters, and pins source_helper below 1.4.0 to fix the freezed variant's build_runner builds.

Bug Fixes

  • ruby-nextgen now always drives Zeitwerk explicitly with push_dir(namespace: self) instead of Zeitwerk::Loader.for_gem, which derived the namespace from gemName and autoloaded nothing when it didn't match moduleName.
  • Dart-dio path parameters are now wrapped in Uri.encodeComponent via a new api/path_param partial for all three serialization libraries, so values containing /, spaces, or reserved characters no longer break the request path.
  • The freezed variant pins source_helper at >=1.3.8 <1.4.0: 1.4.0's extension members clash with json_serializable 6.x and break build_runner builds, and the 1.3.8 lower bound keeps 1.3.9+ (which requires Dart 3.9) resolvable for the variant's 3.8 baseline.

Written for commit 685e5df. Summary will update on new commits.

View guided diff

The query_param helpers pass strings through unchanged (built_value)
or interpolate raw values (freezed, json_serializable), so path
parameters containing '/', spaces or other reserved characters broke
the request path. Substitute path params through a new api/path_param
partial that wraps the value in Uri.encodeComponent for all three
serialization libraries.

Fixes OpenAPITools#25527
…me namespace

Zeitwerk::Loader.for_gem derives its namespace from gemName, so a
single-level moduleName that is not the camelized gemName (e.g.
gemName=things_api, moduleName=Things) autoloaded nothing. Drive
Zeitwerk explicitly with push_dir(namespace: self) in all cases -
the nested branch already did, and it never depended on the names
matching.

Fixes OpenAPITools#24997

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 21 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/built_value/api/path_param.mustache">

<violation number="1" location="modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/built_value/api/path_param.mustache:2">
P2: This treats every path parameter as a scalar. For an array path parameter, the built_value query helper returns `ListParam`, so this encodes its object representation rather than the `style: simple` value (for example, `a,b`), producing an invalid request path; add container-specific path serialization that encodes each element while preserving the delimiter.</violation>
</file>

<file name="modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/json_serializable/api/path_param.mustache">

<violation number="1" location="modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/json_serializable/api/path_param.mustache:2">
P2: Array path parameters are encoded as Dart’s bracketed list string instead of an OpenAPI path serialization. Serialize and encode each path value according to its array/map style, preserving required delimiters such as the simple-style comma.</violation>
</file>

Reply with feedback, questions, or to request a fix.

View guided diff | Re-trigger cubic

@@ -0,0 +1,3 @@
Uri.encodeComponent(
{{#includeLibraryTemplate}}api/query_param{{/includeLibraryTemplate}}.toString(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This treats every path parameter as a scalar. For an array path parameter, the built_value query helper returns ListParam, so this encodes its object representation rather than the style: simple value (for example, a,b), producing an invalid request path; add container-specific path serialization that encodes each element while preserving the delimiter.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/built_value/api/path_param.mustache, line 2:

<comment>This treats every path parameter as a scalar. For an array path parameter, the built_value query helper returns `ListParam`, so this encodes its object representation rather than the `style: simple` value (for example, `a,b`), producing an invalid request path; add container-specific path serialization that encodes each element while preserving the delimiter.</comment>

<file context>
@@ -0,0 +1,3 @@
+Uri.encodeComponent(
+  {{#includeLibraryTemplate}}api/query_param{{/includeLibraryTemplate}}.toString(),
+)
</file context>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 5301a77 — the template now renders on a single line (no trailing newline), so the generated path substitution keeps the previous single-line style:

final _path = r'/pet/{petId}'.replaceAll('{' r'petId' '}', Uri.encodeComponent(encodeQueryParameter(_serializers, petId, const FullType(int)).toString()),);

All dart-dio samples regenerated.

@@ -0,0 +1,3 @@
Uri.encodeComponent(
{{#includeLibraryTemplate}}api/query_param{{/includeLibraryTemplate}}.toString(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Array path parameters are encoded as Dart’s bracketed list string instead of an OpenAPI path serialization. Serialize and encode each path value according to its array/map style, preserving required delimiters such as the simple-style comma.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/json_serializable/api/path_param.mustache, line 2:

<comment>Array path parameters are encoded as Dart’s bracketed list string instead of an OpenAPI path serialization. Serialize and encode each path value according to its array/map style, preserving required delimiters such as the simple-style comma.</comment>

<file context>
@@ -0,0 +1,3 @@
+Uri.encodeComponent(
+  {{#includeLibraryTemplate}}api/query_param{{/includeLibraryTemplate}}.toString(),
+)
</file context>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same fix (single-line template, no trailing newline) — applied to all three serialization variants in 5301a77.

source_helper 1.4.0 (published 2026-10-19) added extension members
that clash with json_serializable 6.x's internal copies, breaking
compilation of the build_runner build script ('toStringNonNullable is
defined in multiple extensions'). The json_serializable variant is
unaffected because copy_with_extension_gen's source_gen constraint
holds source_helper below 1.4.0; the freezed variant has no such
guard and floated to 1.4.0.

Verified with Dart 3.9: pub get resolves source_helper 1.3.13,
build_runner builds, dart test passes (190 tests).

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 2 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="modules/openapi-generator/src/main/resources/dart/libraries/dio/pubspec.mustache">

<violation number="1" location="modules/openapi-generator/src/main/resources/dart/libraries/dio/pubspec.mustache:61">
P2: This pin does not cover `serializationLibrary=json_serializable` with `skipCopyWith=true`. That mode omits `copy_with_extension_gen`, so it also lacks the source-gen constraint that prevents `source_helper 1.4.0` and can fail with the same duplicate extension errors; apply the compatible upper bound to that generated variant as well.</violation>
</file>

Reply with feedback, questions, or to request a fix.

View guided diff | Re-trigger cubic

# whose source_gen constraint would hold source_helper back; source_helper 1.4.0
# added extension members that clash with json_serializable 6.x's internal
# copies and break the build_runner build script, so hold it below 1.4.0
source_helper: '>=1.3.13 <1.4.0'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This pin does not cover serializationLibrary=json_serializable with skipCopyWith=true. That mode omits copy_with_extension_gen, so it also lacks the source-gen constraint that prevents source_helper 1.4.0 and can fail with the same duplicate extension errors; apply the compatible upper bound to that generated variant as well.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At modules/openapi-generator/src/main/resources/dart/libraries/dio/pubspec.mustache, line 61:

<comment>This pin does not cover `serializationLibrary=json_serializable` with `skipCopyWith=true`. That mode omits `copy_with_extension_gen`, so it also lacks the source-gen constraint that prevents `source_helper 1.4.0` and can fail with the same duplicate extension errors; apply the compatible upper bound to that generated variant as well.</comment>

<file context>
@@ -54,5 +54,10 @@ dev_dependencies:
+  # whose source_gen constraint would hold source_helper back; source_helper 1.4.0
+  # added extension members that clash with json_serializable 6.x's internal
+  # copies and break the build_runner build script, so hold it below 1.4.0
+  source_helper: '>=1.3.13 <1.4.0'
 {{/useFreezed}}
   test: '^1.16.0'
</file context>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid catch — widened in 594a4aa to source_helper: '>=1.3.8 <1.4.0':

  • on Dart 3.9+ the resolver picks 1.3.13 (verified: dart pub get → source_helper 1.3.13, build_runner builds, dart test 190/190 pass);
  • on Dart 3.8 the resolver falls back to 1.3.8 (source_gen >=3.1.0 <5.0.0, SDK ^3.7.0), which predates the conflicting extension members and resolves against this variant's sdk >=3.8.0.

Sample regenerated.

- render Uri.encodeComponent on one line so generated paths keep the
  previous single-line style
- assert in testPathParamsAreUriEncoded that the number of
  Uri.encodeComponent calls equals the number of path substitutions in
  every generated api class, not just that one occurrence exists

Addresses review feedback in OpenAPITools#25544.
source_helper 1.3.9+ requires Dart 3.9, but the freezed variant
advertises sdk >=3.8.0 - 1.3.8 (source_gen >=3.1.0 <5.0.0, Dart
^3.7.0) keeps 3.8 users able to resolve while 3.9+ users still get
1.3.13.

Addresses review feedback in OpenAPITools#25544.

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

1 participant