fix(ruby): stop sub-SDKs named after Ruby or SDK constants from shadowing them

Merged
2Y
12h ago
+3629-1310

Why

Generated Ruby code lives inside the SDK module, and every tag becomes a sub-SDK class directly under it. Unqualified constant references in the templates therefore resolved to a same-named sub-SDK instead of the intended constant:

  • File.extname, Time.now, Date.iso8601, is_a? Hash and similar failed srb tc when a spec had a File, Time, Date or Hash tag.
  • include Kernel made the gem fail to load when a spec had a Kernel tag.
  • A Utils or SDKHooks tag replaced the SDK's own module (Utils is not a class). An SDKConfiguration tag overwrote sdkconfiguration.rb and the @sdk_configuration instance variable.

What changed

  • Ruby core and stdlib constants (::String, ::JSON, ::File, ::StandardError, ...) are prefixed with :: in the SDK templates and in the TypeScript helpers that emit Ruby code.
  • Usage snippets are top-level Ruby, so their date, date-time, file and environment variable example values stay unqualified (rubyConstantRef). Model defaults and generated tests keep the prefix.
  • Sub-SDKs whose class or file name matches an SDK internal (Utils, SDKHooks, SDKConfiguration, sdkconfiguration.rb) get an SDK suffix, repeated until the name is free of every other sub-SDK and the main SDK file (subSDKTypeName). A sub-SDK accessor named sdk_configuration becomes sdk_configuration_ (sanitizeSubSDKFieldName).
  • Schemas keep their names: models live under Models:: and do not shadow these constants.
  • New snapshot tests, each compiled with srb tc and rubocop:
    • TestSnapRubySubSDKCoreConstantCollision: tags File, Time, Hash, Class, Kernel, Utils, Date, String, Array, SDKConfiguration, SDKHooks, UtilsSDK.
    • TestSnapRubySubSDKReservedNameNesting: sdkClassName: UtilsSDK with a Utils tag (sub-SDK becomes UtilsSDKSDK), plus nested groups admin.SDKConfiguration and admin.SDKHooks.
    • TestSnapRubyCoreConstantExampleValues: date, date-time and file example values stay unqualified in USAGE.md and use :: in generated tests.
  • Existing Ruby namespace snapshots are updated for the :: prefixes.
  • Ruby review SDK regenerated. The diff contains only :: qualification, formatter re-wrapping and the matching gen.lock checksums.

Drive-by: date and date-time doc links

Unrelated to the shadowing fix. Ruby docs linked date fields to DateTime and date-time fields to Date. templates/templates/ruby/includes/documentation.ts now swaps them back, with its own changeset (docs, patch). In the review SDK this only changes four docs pages and their gen.lock checksums.

Testing

  • go test ./pkg/generate/snapshots/namespaces/ -run Ruby -count=1 with Ruby 3.2.11 and Go 1.26.10: all 6 Ruby snapshot tests generate, compile (srb tc, rubocop) and match.
  • make lint steps: prettier, go mod tidy -diff, tsgo for the Ruby templates and golangci-lint all pass.
  • Each reserved tag (Utils, SDKHooks, SDKConfiguration, sdk_configuration) was generated with main and with this branch, then loaded with SDK.new. On main, all four fail to load (Utils is not a class, SDKHooks is not a module, wrong number of arguments (given 1, expected 6)). On this branch they load and the accessors return the renamed sub-SDKs. The renames therefore only affect SDKs that could not be used before.
  • TARGET=review make build-ruby passes (srb tc, rubocop, mock-server build and staticcheck).
  • A large production-scale spec with many tags regenerates and passes srb tc and rubocop (5039 files, no offenses).

Known and out of scope: the tag pair SDKConfiguration + sdkconfiguration (already renamed upstream to SDKConfiguration1 / Sdkconfiguration2) still fails at runtime because attr_accessor is derived from Type.Name while the instance variable is derived from FieldName. This mismatch predates this change.

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.