Skip to content

fix(ruby): accept acronyms in module name validation - #152

Open
2ynn wants to merge 8 commits into
mainfrom
fix/ruby/module-name-acronyms
Open

2ynn wants to merge 8 commits into
mainfrom
fix/ruby/module-name-acronyms

Conversation

@2ynn

@2ynn 2ynn commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Why

The Ruby module gen.yaml field is validated against ValidationRegex in templates/templates/ruby/config.ts. Its ValidationMessage reads "Module names should be PascalCase and have no special chars", but the current pattern ^([A-Z][a-z0-9]+)+$ is stricter than that message: every capital must be followed by a lowercase letter or digit, so acronyms are rejected. MySDK, OpenAPI, and SDK all fail.

PascalCase allows acronyms; Ruby's own standard library uses them (JSON, URI, OpenSSL, Net::HTTP). The generator already handles them as well:

  • Every template reference to the module goes through sanitizeModuleName (templates/templates/ruby/includes/sanitization.ts), which applies caser().ToPascal. It keeps acronyms inside mixed-case names (MySDK, OpenAPI, HTTPClient are unchanged) and always keeps SDK. A name that is only an uppercase run other than SDK, or that ends in an acronym followed by a digit, is rewritten (API → Api, OpenAPIV3 → OpenApiv3); the result is still a valid Ruby constant.
  • File paths go through caser().ToSnake (getSDKFolderName, templateAuxPath): MySDK → lib/my_sdk/.
  • The default value OpenApiSdk already renders as module OpenApiSDK in the review and primary Ruby SDKs, so acronym constants are already normal output.

The pattern is the only thing blocking these names. The Speakeasy CLI enforces it during speakeasy run, so an SDK whose module contains an acronym fails gen.yaml validation on every generator release that includes the current pattern. When speakeasyVersion is latest, the CLI then reruns with the last version that succeeded, so the SDK quietly stops receiving generator updates and the run can still report success.

No coverage for ValidationRegex in any target

Nothing in this repository exercises ValidationRegex. The generator never reads it; only cmd/jsonschema copies it into a JSON Schema pattern. Generation and SDK tests therefore pass whatever the pattern says, which is how the Ruby regression went unnoticed. The same gap exists for every target:

  • Patterns are written as JavaScript regular expressions and exported through RegExp.source, but the CLI rewrites the \u002f escape that goja's RegExp.source emits for an unescaped / and compiles the result with Go's regexp.MustCompile. A JavaScript-only construct such as lookahead, lookbehind, or a backreference is valid in config.ts but panics the CLI for every user of that target.
  • A DefaultValue that fails its own pattern makes a freshly initialised gen.yaml fail validation.

Today all 241 patterns across the available templates (3 common, 238 target-specific, the same count for newSDK false and true) compile in Go and accept their defaults, so the check below is a guard against regressions.

What changed

  • templates/templates/ruby/config.ts: module pattern ^([A-Z][a-z0-9]+)+$ → ^[A-Z][A-Za-z0-9]*$. This is exactly what the existing message says: an uppercase first letter, then letters and digits only. Names that passed before still pass; _, ::, -, a lowercase first letter, and a leading digit are still rejected.
  • templates/templates/common/common/typeDefs.d.ts: SDKGenConfigField gains an optional ValidationExamples: { valid?: string[]; invalid?: string[] }. It is test-only: GetLanguageConfigFields decodes into sdk-gen-config's SDKGenConfigField, which drops unknown keys, so the CLI, speakeasy configure, and cmd/jsonschema never see it and sdk-gen-config needs no change. The templates' tsconfig.json files do not include config.ts, so the type documents the key but does not check it; the test below does.
  • templates/templates/ruby/config.ts: the module field declares its accepted (AcmeRubySDK, OpenAPI, SDK, Acme2, and the single letter A) and rejected (openApiSdk, Open_Api, Open::Api, Open-Api, 2Acme, empty) names as ValidationExamples.
  • pkg/templates/validation_regex_test.go: TestValidationRegexes runs a common subtest over common/common/config.ts and one subtest per template from GetAvailableTemplates(), each with newSDK both false and true. It reads the raw config.ts output (so ValidationExamples survive) and decodes each field into SDKGenConfigField plus ValidationExamples with mapstructure's ErrorUnused, so a misspelled key fails instead of being silently dropped. ValidationFunc is also accepted, because two existing fields (mcp-typescript advancedFlags, typescriptv2 usageSDKInitImports) declare it even though neither the generator nor the CLI reads it. It then compiles every ValidationRegex after applying the CLI's slash rewrite, and asserts that the DefaultValue and every valid example match and every invalid example does not. A field that declares examples without a ValidationRegex fails, so removing a pattern cannot silently skip its examples. Common fields that a target passes through unchanged are checked once in the common subtest. Each template subtest also asserts GetLanguageConfigFields still succeeds. A default-value check alone cannot catch a pattern that is too strict while the default still passes; examples next to the pattern cover that without a test file per target.
  • Changeset: core / ruby patch.

Generated output does not change, so no review SDKs are regenerated.

Testing

  • Generated a Ruby SDK with module: AcmeRubySDK and typingStrategy: sorbet via cmd/generate: emits module AcmeRubySDK under lib/acme_ruby_sdk/.
  • go test ./pkg/templates -run TestValidationRegexes -count=1 -race: passes. Mutation checks, each reverted afterwards:
    • lookahead in the Ruby module pattern (^(?=O)[A-Za-z]+$): fails with a compile error naming the field;
    • Ruby module default openApiSdk: fails naming the field, default, and pattern;
    • previous Ruby module pattern ^([A-Z][a-z0-9]+)+$: fails on valid examples AcmeRubySDK, OpenAPI, SDK;
    • Open_Api added to valid, Acme added to invalid: each fails naming the example;
    • a valid example maybe on a common ^(true|false)$ field: fails in the common subtest;
    • key misspelled as ValidationExample, or valid misspelled inside it: each fails with an unknown-key error naming the field;
    • Ruby module pattern removed while its examples stay: fails naming the field;
    • * changed to + in the Ruby module pattern: fails on the valid example A;
    • removing the slash rewrite: fails on the cli target's packageName, whose pattern contains /.
  • make lint: passes.
  • Not run: end-to-end speakeasy run, which needs a CLI build bundling this generator.

Public-safety check

  • This change contains no credentials, customer documents, private repository URLs, private filesystem paths, or unredacted private logs.
  • Title, body, comments, and commit messages name no customers or customer-derived identifiers, private paths or trackers, or workflow provenance, and are understandable without private context (.claude/skills/public-repo-communication/SKILL.md).
  • Generated fixtures and review SDK changes are public-safe.
  • I reviewed git diff --check.

The `module` validation pattern `^([A-Z][a-z0-9]+)+$` requires a lowercase
letter or digit after every capital, so PascalCase names containing
acronyms such as `MySDK` or `OpenAPI` fail gen.yaml validation even though
the generator renders them correctly. Accept any name starting with an
uppercase letter followed by letters or digits.
@github-actions github-actions Bot added the ruby Trigger (19) snapshot tests for ruby label Oct 26, 2026

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

All reported issues were addressed across 3 files

Shadow auto-approve: would not auto-approve because issues were found.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread templates/templates/ruby/config.ts

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

0 issues found across 1 file (changes from recent commits).

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Shadow auto-approve: would not auto-approve. Auto-approval blocked by 1 unresolved issue from previous reviews.

View guided diff | Turn on auto-fix | Re-trigger cubic

@speakeasy-generator-bot

speakeasy-generator-bot Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

Generator Snapshot Testing

Selector(s) Result Summary
snapshot-ruby ✅ Snapshot run completed

Snapshot results: https://github.com/speakeasy-api/openapi-generation-snapshots/issues/137#issuecomment-6427253168

Snapshot results are summarized above. Additional run context is linked for maintainers with access.

Every target's gen.yaml config fields may declare a ValidationRegex in
config.ts. The patterns are authored as JavaScript regular expressions
but the Speakeasy CLI compiles them with Go's regexp.MustCompile, so a
JavaScript-only construct would panic the CLI, and a default value that
fails its own pattern would reject a freshly initialised gen.yaml. Add a
test over all available templates that compiles each pattern the way the
CLI does and checks each default value against it.

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

0 issues found across 1 file (changes from recent commits).

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Shadow auto-approve: would not auto-approve. Auto-approval blocked by 1 unresolved issue from previous reviews.

View guided diff | Turn on auto-fix | Re-trigger cubic

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

0 issues found across 2 files (changes from recent commits).

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Shadow auto-approve: would not auto-approve. Auto-approval blocked by 1 unresolved issue from previous reviews.

View guided diff | Turn on auto-fix | Re-trigger cubic

Config fields in config.ts can now list ValidationExamples with valid
and invalid values next to their ValidationRegex. The key is test-only:
GetLanguageConfigFields decodes into SDKGenConfigField, which drops it,
so the CLI and the gen.yaml JSON schema never see it.

TestValidationRegexes reads the raw config.ts output of every template
and of the common config fields, compiles each pattern the way the CLI
does, and checks the default value and every example against it. The
Ruby module name cases move from their own test into ruby/config.ts.

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

0 issues found across 4 files (changes from recent commits).

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Shadow auto-approve: would not auto-approve. Auto-approval blocked by 1 unresolved issue from previous reviews.

View guided diff | Turn on auto-fix | Re-trigger cubic

Decode each raw config.ts field into SDKGenConfigField plus
ValidationExamples with ErrorUnused, so a misspelled ValidationExamples
key or example list fails the test instead of being silently dropped.
config.ts files are not covered by the templates' tsconfig, so the
TypeScript type does not catch these. ValidationFunc is accepted
because two fields already declare it.

Also match the CLI's raw string for the escaped slash, and drop the
Ruby module example that repeats the default value.

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

0 issues found across 2 files (changes from recent commits).

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Shadow auto-approve: would not auto-approve. Auto-approval blocked by 1 unresolved issue from previous reviews.

View guided diff | Turn on auto-fix | Re-trigger cubic

@2ynn
2ynn marked this pull request as ready for review October 26, 2026 19:16
@2ynn
2ynn requested a review from a team as a code owner October 26, 2026 19:16
@2ynn
2ynn requested a review from simplesagar October 26, 2026 19:16
Removing a field's ValidationRegex while keeping its examples skipped
all of them, so the test stayed green after validation was dropped.
Assert that fields without a pattern declare no examples. Also add the
single-letter Ruby module name A as a valid example, the shortest name
the pattern accepts.

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

0 issues found across 2 files (changes from recent commits).

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Shadow auto-approve: would require human review. Relaxes Ruby module-name validation to accept acronyms and adds cross-target regex tests, but the new pattern also accepts T while the generator silently renames it to TT; the dismissed finding is unresolved and needs human sign-off.

View guided diff | Turn on auto-fix | Re-trigger cubic

@TristanSpeakEasy TristanSpeakEasy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks good, accepting acronyms matches the Ruby naming behaviour and the regex examples should help keep this from regressing.

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

Labels

ruby Trigger (19) snapshot tests for ruby

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants