Skip to content

Commit 9c5a963

Browse files
hmackoesie10
authored andcommitted
Add experimental model editor support for Ruby
Make the minimum changes necessary for prototype Ruby support in the model editor. This consists of: - Reading/writing modelled methods from/to data extensions in the dynamic languages format - Special-casing Ruby in a few places where Java/C# was previously assumed.
1 parent f4a2d85 commit 9c5a963

3 files changed

Lines changed: 154 additions & 1 deletion

File tree

extensions/ql-vscode/src/model-editor/languages/languages.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import { QueryLanguage } from "../../common/query-language";
22
import { ModelsAsDataLanguage } from "./models-as-data";
3+
import { ruby } from "./ruby";
34
import { staticLanguage } from "./static";
45

56
const languages: Partial<Record<QueryLanguage, ModelsAsDataLanguage>> = {
67
[QueryLanguage.CSharp]: staticLanguage,
78
[QueryLanguage.Java]: staticLanguage,
9+
[QueryLanguage.Ruby]: ruby,
810
};
911

1012
export function getModelsAsDataLanguage(
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
import { ModelsAsDataLanguage } from "./models-as-data";
2+
import { sharedExtensiblePredicates, sharedKinds } from "./shared";
3+
4+
function parseRubyMethodFromPath(path: string): string {
5+
const match = path.match(/Method\[([^\]]+)].*/);
6+
if (match) {
7+
return match[1];
8+
} else {
9+
return "";
10+
}
11+
}
12+
13+
function parseRubyAccessPath(path: string): {
14+
methodName: string;
15+
path: string;
16+
} {
17+
const match = path.match(/Method\[([^\]]+)]\.(.*)/);
18+
if (match) {
19+
return { methodName: match[1], path: match[2] };
20+
} else {
21+
return { methodName: "", path: "" };
22+
}
23+
}
24+
25+
function rubyMethodSignature(typeName: string, methodName: string) {
26+
return `${typeName}#${methodName}`;
27+
}
28+
29+
export const ruby: ModelsAsDataLanguage = {
30+
createMethodSignature: ({ typeName, methodName }) =>
31+
`${typeName}#${methodName}`,
32+
predicates: {
33+
source: {
34+
extensiblePredicate: sharedExtensiblePredicates.source,
35+
supportedKinds: sharedKinds.source,
36+
// extensible predicate sourceModel(
37+
// string type, string path, string kind
38+
// );
39+
generateMethodDefinition: (method) => [
40+
method.typeName,
41+
`Method[${method.methodName}].${method.output}`,
42+
method.kind,
43+
],
44+
readModeledMethod: (row) => {
45+
const typeName = row[0] as string;
46+
const { methodName, path: output } = parseRubyAccessPath(
47+
row[1] as string,
48+
);
49+
return {
50+
type: "source",
51+
input: "",
52+
output,
53+
kind: row[2] as string,
54+
provenance: "manual",
55+
signature: rubyMethodSignature(typeName, methodName),
56+
packageName: "",
57+
typeName,
58+
methodName,
59+
methodParameters: "",
60+
};
61+
},
62+
},
63+
sink: {
64+
extensiblePredicate: sharedExtensiblePredicates.sink,
65+
supportedKinds: sharedKinds.sink,
66+
// extensible predicate sinkModel(
67+
// string type, string path, string kind
68+
// );
69+
generateMethodDefinition: (method) => {
70+
const path = `Method[${method.methodName}].${method.input}`;
71+
return [method.typeName, path, method.kind];
72+
},
73+
readModeledMethod: (row) => {
74+
const typeName = row[0] as string;
75+
const { methodName, path: input } = parseRubyAccessPath(
76+
row[1] as string,
77+
);
78+
return {
79+
type: "sink",
80+
input,
81+
output: "",
82+
kind: row[2] as string,
83+
provenance: "manual",
84+
signature: rubyMethodSignature(typeName, methodName),
85+
packageName: "",
86+
typeName,
87+
methodName,
88+
methodParameters: "",
89+
};
90+
},
91+
},
92+
summary: {
93+
extensiblePredicate: sharedExtensiblePredicates.summary,
94+
supportedKinds: sharedKinds.summary,
95+
// extensible predicate summaryModel(
96+
// string type, string path, string input, string output, string kind
97+
// );
98+
generateMethodDefinition: (method) => [
99+
method.typeName,
100+
`Method[${method.methodName}]`,
101+
method.input,
102+
method.output,
103+
method.kind,
104+
],
105+
readModeledMethod: (row) => {
106+
const typeName = row[0] as string;
107+
const methodName = parseRubyMethodFromPath(row[1] as string);
108+
return {
109+
type: "summary",
110+
input: row[2] as string,
111+
output: row[3] as string,
112+
kind: row[4] as string,
113+
provenance: "manual",
114+
signature: rubyMethodSignature(typeName, methodName),
115+
packageName: "",
116+
typeName,
117+
methodName,
118+
methodParameters: "",
119+
};
120+
},
121+
},
122+
neutral: {
123+
extensiblePredicate: sharedExtensiblePredicates.neutral,
124+
supportedKinds: sharedKinds.neutral,
125+
// extensible predicate neutralModel(
126+
// string type, string path, string kind
127+
// );
128+
generateMethodDefinition: (method) => [
129+
method.typeName,
130+
`Method[${method.methodName}]`,
131+
method.kind,
132+
],
133+
readModeledMethod: (row) => {
134+
const typeName = row[0] as string;
135+
const methodName = parseRubyMethodFromPath(row[1] as string);
136+
return {
137+
type: "neutral",
138+
input: "",
139+
output: "",
140+
kind: row[2] as string,
141+
provenance: "manual",
142+
signature: rubyMethodSignature(typeName, methodName),
143+
packageName: "",
144+
typeName,
145+
methodName,
146+
methodParameters: "",
147+
};
148+
},
149+
},
150+
},
151+
};

extensions/ql-vscode/src/model-editor/model-editor-module.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import { ModelEditorViewTracker } from "./model-editor-view-tracker";
2323
import { ModelConfigListener } from "../config";
2424
import { ModelingEvents } from "./modeling-events";
2525

26-
const SUPPORTED_LANGUAGES: string[] = ["java", "csharp"];
26+
const SUPPORTED_LANGUAGES: string[] = ["java", "csharp", "ruby"];
2727

2828
export class ModelEditorModule extends DisposableObject {
2929
private readonly queryStorageDir: string;

0 commit comments

Comments
 (0)