@@ -2,9 +2,16 @@ import * as spec from '@jsii/spec';
22import { toSnakeCase , toPascalCase } from 'codemaker' ;
33import * as fs from 'fs-extra' ;
44import * as reflect from 'jsii-reflect' ;
5+ import {
6+ ApiLocation ,
7+ enforcesStrictMode ,
8+ RosettaTabletReader ,
9+ TargetLanguage ,
10+ } from 'jsii-rosetta' ;
511import * as path from 'path' ;
612
713import { Generator , Legalese } from '../generator' ;
14+ import { assertSpecIsRosettaCompatible } from '../rosetta-assembly' ;
815import { Target , TargetOptions } from '../target' ;
916import { subprocess } from '../util' ;
1017import { VERSION } from '../version' ;
@@ -17,7 +24,7 @@ export class RubyTarget extends Target {
1724
1825 public constructor ( options : TargetOptions ) {
1926 super ( options ) ;
20- this . generator = new RubyGenerator ( options ) ;
27+ this . generator = new RubyGenerator ( options . rosetta , options ) ;
2128 }
2229
2330 public async build ( sourceDir : string , outDir : string ) : Promise < void > {
@@ -211,12 +218,32 @@ const RUBY_RESERVED_NAMES = new Set([
211218] ) ;
212219
213220export class RubyGenerator extends Generator {
214- public constructor ( options : TargetOptions ) {
221+ public constructor (
222+ private readonly rosetta : RosettaTabletReader ,
223+ options : TargetOptions ,
224+ ) {
215225 super ( { runtimeTypeChecking : options . runtimeTypeChecking } ) ;
216226 // Ruby convention is 2-space indentation (CodeMaker defaults to 4).
217227 this . code . indentation = 2 ;
218228 }
219229
230+ /**
231+ * Translate a jsii `@example` (authored in TypeScript) into idiomatic Ruby
232+ * via Rosetta. Falls back to the original text if translation fails, so a
233+ * bad snippet degrades to a TypeScript example rather than breaking the
234+ * build.
235+ */
236+ private convertExample ( example : string , apiLocation : ApiLocation ) : string {
237+ assertSpecIsRosettaCompatible ( this . assembly ) ;
238+ const translated = this . rosetta . translateExample (
239+ apiLocation ,
240+ example ,
241+ TargetLanguage . RUBY ,
242+ enforcesStrictMode ( this . assembly ) ,
243+ ) ;
244+ return translated . source ;
245+ }
246+
220247 /**
221248 * Normalize a type reference to its raw `spec.TypeReference` shape.
222249 * Call sites hold two shapes: jsii-reflect `TypeReference` instances
@@ -389,6 +416,12 @@ export class RubyGenerator extends Generator {
389416 /** Property getter: emit an @return of the property's type. */
390417 propertyType ?: RubyTypeRef ;
391418 propertyOptional ?: boolean ;
419+ /**
420+ * The API location the docs belong to. When present, `@example`
421+ * snippets are translated to Ruby via Rosetta; without it they are
422+ * emitted verbatim (i.e. as the original TypeScript).
423+ */
424+ apiLocation ?: ApiLocation ;
392425 } = { } ,
393426 ) : void {
394427 const docs : spec . Docs = this . rawDocs ( docsSource ) ?? { } ;
@@ -430,12 +463,14 @@ export class RubyGenerator extends Generator {
430463 tags . push ( `# @see ${ this . inlineDoc ( docs . see ) } ` ) ;
431464 }
432465
433- const exampleLines = docs . example
466+ const exampleText =
467+ docs . example && opts . apiLocation
468+ ? this . convertExample ( docs . example , opts . apiLocation )
469+ : docs . example ;
470+ const exampleLines = exampleText
434471 ? [
435472 '# @example' ,
436- ...docs . example
437- . split ( '\n' )
438- . map ( ( l ) => `# ${ l . trimEnd ( ) } ` . trimEnd ( ) ) ,
473+ ...exampleText . split ( '\n' ) . map ( ( l ) => `# ${ l . trimEnd ( ) } ` . trimEnd ( ) ) ,
439474 ]
440475 : [ ] ;
441476
@@ -692,10 +727,18 @@ export class RubyGenerator extends Generator {
692727 ( m ) => this . rubyConstName ( m . name ) ,
693728 typeSpec . fqn ,
694729 ) ;
695- this . emitDocs ( typeSpec ) ;
730+ this . emitDocs ( typeSpec , {
731+ apiLocation : { api : 'type' , fqn : typeSpec . fqn } ,
732+ } ) ;
696733 this . code . open ( `module ${ prefix } ${ this . rubyModuleName ( typeSpec . name ) } ` ) ;
697734 for ( const member of resolvedMembers ) {
698- this . emitDocs ( member ) ;
735+ this . emitDocs ( member , {
736+ apiLocation : {
737+ api : 'member' ,
738+ fqn : typeSpec . fqn ,
739+ memberName : member . name ,
740+ } ,
741+ } ) ;
699742 this . code . line (
700743 `${ this . rubyConstName ( member . name ) } = Jsii::Enum.new("${ rubyDq ( typeSpec . fqn ) } ", "${ rubyDq ( member . name ) } ")` ,
701744 ) ;
@@ -744,7 +787,9 @@ export class RubyGenerator extends Generator {
744787 ? ' < Jsii::Struct'
745788 : '' ;
746789
747- this . emitDocs ( typeSpec ) ;
790+ this . emitDocs ( typeSpec , {
791+ apiLocation : { api : 'type' , fqn : typeSpec . fqn } ,
792+ } ) ;
748793 this . code . open ( `${ kind } ${ prefix } ${ rubyName } ${ baseString } ` ) ;
749794
750795 if ( ! typeSpec . datatype ) {
@@ -796,6 +841,11 @@ export class RubyGenerator extends Generator {
796841 this . emitDocs ( prop , {
797842 propertyType : prop . type ,
798843 propertyOptional : prop . optional ,
844+ apiLocation : {
845+ api : 'member' ,
846+ fqn : typeSpec . fqn ,
847+ memberName : prop . name ,
848+ } ,
799849 } ) ;
800850 this . code . line ( `attr_reader :${ this . rubyName ( prop . name ) } ` ) ;
801851 }
@@ -832,6 +882,11 @@ export class RubyGenerator extends Generator {
832882 this . emitDocs ( prop , {
833883 propertyType : prop . type ,
834884 propertyOptional : prop . optional ,
885+ apiLocation : {
886+ api : 'member' ,
887+ fqn : typeSpec . fqn ,
888+ memberName : prop . name ,
889+ } ,
835890 } ) ;
836891 this . code . open ( `def ${ propRubyName } ()` ) ;
837892 this . code . line ( `jsii_get_property("${ rubyDq ( prop . name ) } ")` ) ;
@@ -868,6 +923,11 @@ export class RubyGenerator extends Generator {
868923 params : method . parameters ,
869924 returns : method . spec ?. returns ,
870925 isMethod : true ,
926+ apiLocation : {
927+ api : 'member' ,
928+ fqn : typeSpec . fqn ,
929+ memberName : method . name ,
930+ } ,
871931 } ) ;
872932 this . code . open ( `def ${ this . rubyName ( method . name ) } (${ sigParams } )` ) ;
873933 for ( const p of method . parameters ) {
@@ -951,7 +1011,9 @@ export class RubyGenerator extends Generator {
9511011 ( i ) => `::${ this . rubyFullTypeName ( i ) } ` ,
9521012 ) ;
9531013
954- this . emitDocs ( typeSpec ) ;
1014+ this . emitDocs ( typeSpec , {
1015+ apiLocation : { api : 'type' , fqn : typeSpec . fqn } ,
1016+ } ) ;
9551017 this . code . open ( `class ${ prefix } ${ rubyName } < ${ baseClass } ` ) ;
9561018
9571019 for ( const mixin of interfaceMixins ) {
@@ -977,7 +1039,10 @@ export class RubyGenerator extends Generator {
9771039 } )
9781040 . join ( ', ' ) ;
9791041
980- this . emitDocs ( initializer , { params : initializer . parameters } ) ;
1042+ this . emitDocs ( initializer , {
1043+ params : initializer . parameters ,
1044+ apiLocation : { api : 'initializer' , fqn : typeSpec . fqn } ,
1045+ } ) ;
9811046 this . code . open ( `def initialize(${ initParams } )` ) ;
9821047 for ( const p of initializer . parameters ) {
9831048 const rubyParam = this . rubyName ( p . name ) ;
@@ -1084,6 +1149,11 @@ export class RubyGenerator extends Generator {
10841149 params : method . parameters ,
10851150 returns : method . spec ?. returns ,
10861151 isMethod : true ,
1152+ apiLocation : {
1153+ api : 'member' ,
1154+ fqn : typeSpec . fqn ,
1155+ memberName : method . name ,
1156+ } ,
10871157 } ) ;
10881158 this . code . open ( `def self.${ this . rubyMethodName ( method ) } (${ sigParams } )` ) ;
10891159 for ( const p of method . parameters ) {
@@ -1112,6 +1182,11 @@ export class RubyGenerator extends Generator {
11121182 this . emitDocs ( prop , {
11131183 propertyType : prop . type ,
11141184 propertyOptional : prop . optional ,
1185+ apiLocation : {
1186+ api : 'member' ,
1187+ fqn : typeSpec . fqn ,
1188+ memberName : prop . name ,
1189+ } ,
11151190 } ) ;
11161191 this . code . open ( `def self.${ rubyName } ()` ) ;
11171192 this . code . line (
@@ -1136,6 +1211,11 @@ export class RubyGenerator extends Generator {
11361211 this . emitDocs ( prop , {
11371212 propertyType : prop . type ,
11381213 propertyOptional : prop . optional ,
1214+ apiLocation : {
1215+ api : 'member' ,
1216+ fqn : typeSpec . fqn ,
1217+ memberName : prop . name ,
1218+ } ,
11391219 } ) ;
11401220 this . code . open ( `def ${ rubyName } ()` ) ;
11411221 this . code . line ( `jsii_get_property("${ rubyDq ( prop . name ) } ")` ) ;
@@ -1178,6 +1258,11 @@ export class RubyGenerator extends Generator {
11781258 params : method . parameters ,
11791259 returns : method . spec ?. returns ,
11801260 isMethod : true ,
1261+ apiLocation : {
1262+ api : 'member' ,
1263+ fqn : typeSpec . fqn ,
1264+ memberName : method . name ,
1265+ } ,
11811266 } ) ;
11821267 this . code . open ( `def ${ this . rubyMethodName ( method ) } (${ sigParams } )` ) ;
11831268 for ( const p of method . parameters ) {
0 commit comments