feat: Ruby language support for jsii-rosetta - #3710
Draft
omarqureshi wants to merge 11 commits into
Draft
Conversation
Adds a Ruby language visitor (src/languages/ruby.ts) that translates TypeScript example code to idiomatic Ruby, registered as the RUBY target, plus translation test fixtures.
This was referenced Jul 4, 2026
… the member Static readonly (const) access like BlockPublicAccess.BLOCK_ALL fell into the type-reference branch, which resolved the jsii symbol to the containing type and emitted just AWSCDK::S3::BlockPublicAccess, dropping .BLOCK_ALL. Detect it with isStaticReadonlyAccess and render <type>.<CONST_NAME> (dot access, constant casing) to match pacmak's rubyConstName. Enum members keep their :: form. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`import * as s3tables from 'aws-cdk-lib/aws-s3tables'` translated to `require 'aws-cdk-lib-aws-s3tables'` (the whole specifier had `/` -> `-`), which is not a real gem. A submodule is autoloaded from its package, so the require is just the gem: `require 'aws-cdk-lib'`. Keep only the npm package name (two segments for a scoped `@scope/name`, one otherwise). Adds import->require translation tests covering plain, submodule, scoped, scoped-submodule and relative specifiers. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The array and hash renderers mirrored the source's line breaks faithfully,
which produced inconsistent output: elements sharing a line while `]` dropped
to its own line, and a hash property stranded on the closing line of a
preceding multi-line value (`InputFormat.csv({...}), bucket: bucket`).
Normalise instead: if the literal spans multiple lines (its source text
contains a newline), put every element/property on its own line via a
trailing separator, with the closing bracket on its own line; otherwise keep
it inline. Adds array/hash formatting tests (broken + inline).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
An uninitialised declaration (`declare const bucket: s3.IBucket`) is a "given" the reader supplies. It rendered as a bare `bucket = nil`, which drops the type and reads like a mistake. Keep the type as a trailing comment resolved to its Ruby name: `bucket = nil # AWSCDK::S3::IBucket`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Author
|
I have published https://rubygems.omarqureshi.net/docs/AWSCDK/index.html. As a note, the following applies:
Each modules landing page is the README documentation from aws-cdk with code examples run through rosetta. Each class/interface/enum is run through rosetta and the documentation generated by YARD |
…guages Adding typed dispatch for ConditionalExpression/PostfixUnaryExpression gave DefaultVisitor handlers that always rendered an UnknownSyntax placeholder — but the dispatch default case they replaced reported the node as unsupported and then, in best-effort mode, passed the raw source text through. Python, Java, C# and Go doc snippets containing ternaries or ++/-- therefore regressed from readable raw TypeScript to '(ConditionalExpression ...)' placeholders. Extract the old default-case fallback into AstRenderer.renderUnsupported and call it from both the dispatch default case and the two DefaultVisitor handlers, so the fallback contract (diagnostic + raw text passthrough when bestEffort, placeholder otherwise) cannot drift between the two paths again. Ruby's overrides are untouched and still translate both constructs. Verified against a build of main: Python output is byte-identical to the pre-branch behavior in both best-effort and strict modes. Full suite green apart from 18 pre-existing translations.test.ts failures reproduced identically on the pre-fix branch head. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
omarqureshi
added a commit
to omarqureshi/aws-cdk-rfcs
that referenced
this pull request
Jul 22, 2026
- The monorepo-CI claim now states explicitly that green requires pinning a pre-release build of jsii-compiler#2663, and that the checks on jsii#5178 stay red by construction until #2663 merges and releases — an ordering constraint, not an implementation gap. - New "Upstreaming sequence" subsection ordering the open PRs (#2663 -> #5178 -> #3710/docgen -> #38248) so a reviewer cannot misread the red CI. - Phase 0 registration list now includes `jsii`, matching the appendix governance list; name availability re-verified 2026-07-22 (all still unregistered). - Visitor description made durable (40+ AST handlers, no line counts); the rosetta narrative now points at the already-open aws/jsii-rosetta#3710. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The .rb expectation files were written in the initial Ruby-target commit and never regenerated when three later visitor fixes deliberately changed output: multi-line array/hash normalisation (09c7429 — trailing separators, one element per line) and static-readonly member access (8ca8d19 — EnumType.ENUM_VALUE_A, matching pacmak, which emits consts as class methods: `def self.ENUM_VALUE_A()`; bare `::` would be a NameError since no Ruby constant exists). The suite therefore failed 18 fixtures against its own intended behavior — main is green, so these were branch-internal inconsistencies, not upstream drift. Regenerated through the exact harness path (SnippetTranslator with the fixtures compilation directory). Full suite: 701 passed, 0 failed across 29 suites. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Acronyms are library data: aws-cdk-lib declares its own in targets.ruby.acronyms (compiler-validated schema, embedded in every .jsii), and findRubyName already reads them from the referenced assembly. The hardcoded CDK_ACRONYMS list existed only so fixtures and unit tests could render CDK-ish casing without assemblies — test scaffolding leaked into production code, and a CDK word list inside jsii tooling is a layering violation regardless. Delete the list. rubyModuleName now honours exactly the caller-declared acronyms (assembly config is authoritative; nothing merges over it), and a snippet whose type references cannot be resolved to an assembly gets plain PascalCase — an honest guess instead of fake authority (Cdk::SomeClass, not CDK::SomeClass). The one remaining CDK-ism (guessRubyModuleName's aws-cdk-lib -> AWSCDK mapping for unresolvable refs) is documented in-code with its structural fix: callers hold the assembly and should eventually supply naming config through the API. Unit tests now exercise the mechanism with test-owned data (declared acronyms honoured, undeclared ones inert, short tokens like CE/DB/RAM do not over-match inside Certificate/Database/Ramp); the six affected translation fixtures are regenerated. Full suite: 702 passed, 0 failed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…pace fixture
class_with_namespace.ts was the only fixture with no expected output in
any language — silently skipped everywhere. Translating it crashed in
submodule-reference resolution ("Cannot read properties") on a symbol
with no declarations; the crash reproduces against unmodified upstream
code, so the guard is an upstream-relevant fix in its own right.
With the guard in place the Ruby translation is correct (verified against
a fresh harness render, byte-identical), so the fixture now pins Ruby
behavior for aliased namespace imports; other languages remain skipped,
matching the suite's partial-coverage pattern.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A snippet importing two submodules of the same package emitted the same require twice; jsii-pacmak papered over it in README post-processing, but raw tablet consumers still saw duplicates (submodule-import.rb codified them). The visitor now tracks emitted require targets per instance — translate.ts constructs a fresh visitor per snippet render, so state cannot leak across snippets (verified in source and by probe; the one-visitor-per-conversion path in commands/convert.ts dedupes across a whole markdown document, which is the desirable behavior there too). Different gems still each emit their require; the regenerated fixture pins the single-require form. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Arrow functions had no typed dispatch anywhere — every language passed
them through as raw TypeScript via the renderer's default case, which is
how `() => arnLookup` leaked into otherwise-Ruby doc snippets (Lazy
producers, add_validation callbacks and friends).
Add ArrowFunction/FunctionExpression dispatch with the established
contract: the DefaultVisitor delegates to renderUnsupported, so every
other language keeps byte-identical raw-text passthrough by construction;
the Ruby visitor renders lambdas — `(bell) => bell.ring()` becomes
`->(bell) { bell.ring }`, block bodies become multi-line lambdas, and
parameters are snake_cased. Destructuring/default/rest parameters fall
back to the shared unsupported path with a diagnostic.
The rendered output is not a visual approximation: the runtime coerces
Procs into single-method interface implementations at jsii call sites
(jsii repo, Jsii::Utils.coerce_callable), so a translated
`Lazy.string({produce: -> { arn_lookup }})` is a working program.
Suite: 710 passed, 0 failed across 30 suites.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #3711.
Adds Ruby language support to jsii-rosetta — a new Ruby AST visitor (
src/languages/ruby.ts) that translates TypeScript example code to idiomatic Ruby, registered as theRUBYtarget.Part of the coordinated Ruby language support effort:
jsii-pacmakRuby target: feat(ruby): Ruby language bindings jsii#5178What's included
require/require_relative, classes / behavioral interfaces / enums, methods (includingdef self.for statics) and constructors →initialize, properties →attr_reader/attr_accessorand class constants, object literals → hashes,new X()→X.new, property access withsnake_caseconversion, string & template-literal rendering with proper escaping,if/elsif/else,for..of→.each, spreads → splats, ternaries, and comment/YARD rendering.test/ruby-translations.test.ts).Notes
main; earlier TypeScript 6.0 upgrade commits are dropped as redundant (main is already on TS 6.0 / jsii 6.0, usingtypes: ['*']).if/elsif/elsechains emitting a doubledend, Ruby string-interpolation escaping (#{}),staticmethod/constant rendering,as-cast pass-through (no spurious.to_i/.to_s), andsuper()argument semantics — all covered by the new tests.The visitor is self-contained (no dependency on the Ruby-capable compiler), so the test suite passes on its own.
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.