Skip to content
This repository was archived by the owner on May 18, 2026. It is now read-only.

Commit 5ac2763

Browse files
authored
Merge pull request #530 from smalruby/fix/issue-28-escape-control-characters
fix: escape control characters in Ruby code generation
2 parents 89096a4 + c3e597a commit 5ac2763

5 files changed

Lines changed: 83 additions & 30 deletions

File tree

cspell.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"gapi",
77
"googleusercontent",
88
"Hira",
9+
"Interconvert",
910
"mbit",
1011
"smalrubot"
1112
],

src/lib/ruby-generator/index.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,14 @@ RubyGenerator.scrubNakedValue = function (line) {
333333

334334
RubyGenerator.escapeChars_ = {
335335
'"': '\\"',
336-
'\\': '\\\\'
336+
'\\': '\\\\',
337+
'\n': '\\n',
338+
'\t': '\\t',
339+
'\r': '\\r',
340+
'\b': '\\b',
341+
'\f': '\\f',
342+
'\v': '\\v',
343+
'\0': '\\0'
337344
};
338345

339346
RubyGenerator.quote_ = function (string) {

test/integration/ruby-tab/operators.test.js

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,16 @@
11
import dedent from 'dedent';
22
import SeleniumHelper from '../../helpers/selenium-helper';
33
import RubyHelper from '../../helpers/ruby-helper';
4-
import {EDIT_MENU_XPATH} from '../../helpers/menu-xpaths';
54

65
const seleniumHelper = new SeleniumHelper();
76
const {
8-
clickText,
9-
clickXpath,
107
getDriver,
118
loadUri,
129
urlFor
1310
} = seleniumHelper;
1411

1512
const rubyHelper = new RubyHelper(seleniumHelper);
1613
const {
17-
fillInRubyProgram,
18-
currentRubyProgram,
1914
expectInterconvertBetweenCodeAndRuby
2015
} = rubyHelper;
2116

@@ -98,28 +93,4 @@ describe('Ruby Tab: Operators category blocks', () => {
9893
`;
9994
await expectInterconvertBetweenCodeAndRuby(code);
10095
});
101-
102-
test('Ruby -> Code -> Ruby (escape characters) ', async () => {
103-
await loadUri(urlFor('/'));
104-
105-
const beforeRuby = dedent`
106-
"\\\\" + "\\\\"
107-
108-
"\\n" + "\\n"
109-
`;
110-
111-
const afterRuby = dedent`
112-
"\\" + "\\"
113-
114-
"\n" + "\n"
115-
`;
116-
117-
await clickText('Ruby', '*[@role="tab"]');
118-
await fillInRubyProgram(beforeRuby);
119-
await clickText('Code', '*[@role="tab"]');
120-
await clickXpath(EDIT_MENU_XPATH);
121-
await clickText('Generate Ruby from Code');
122-
await clickText('Ruby', '*[@role="tab"]');
123-
expect(await currentRubyProgram()).toEqual(`${afterRuby}\n`);
124-
});
12596
});
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import RubyGenerator from '../../../../src/lib/ruby-generator';
2+
3+
describe('RubyGenerator', () => {
4+
describe('quote_', () => {
5+
test('should escape double quotes', () => {
6+
const result = RubyGenerator.quote_('"');
7+
expect(result).toBe('"\\\""');
8+
});
9+
10+
test('should escape backslashes', () => {
11+
const result = RubyGenerator.quote_('\\');
12+
expect(result).toBe('"\\\\"');
13+
});
14+
15+
test('should escape newline characters', () => {
16+
const result = RubyGenerator.quote_('\n');
17+
expect(result).toBe('"\\n"');
18+
});
19+
20+
test('should escape tab characters', () => {
21+
const result = RubyGenerator.quote_('\t');
22+
expect(result).toBe('"\\t"');
23+
});
24+
25+
test('should escape carriage return characters', () => {
26+
const result = RubyGenerator.quote_('\r');
27+
expect(result).toBe('"\\r"');
28+
});
29+
30+
test('should escape backspace characters', () => {
31+
const result = RubyGenerator.quote_('\b');
32+
expect(result).toBe('"\\b"');
33+
});
34+
35+
test('should escape form feed characters', () => {
36+
const result = RubyGenerator.quote_('\f');
37+
expect(result).toBe('"\\f"');
38+
});
39+
40+
test('should escape vertical tab characters', () => {
41+
const result = RubyGenerator.quote_('\v');
42+
expect(result).toBe('"\\v"');
43+
});
44+
45+
test('should escape null characters', () => {
46+
const result = RubyGenerator.quote_('\0');
47+
expect(result).toBe('"\\0"');
48+
});
49+
});
50+
});

test/unit/lib/ruby-generator/looks.test.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,30 @@ describe('RubyGenerator/Looks', () => {
8282
const expected = 'say("Hello!")\n';
8383
expect(RubyGenerator.looks_say(block)).toEqual(expected);
8484
});
85+
86+
test('print with newline character', () => {
87+
const block = {
88+
id: 'block-id',
89+
opcode: 'looks_say',
90+
inputs: { MESSAGE: {} }
91+
};
92+
RubyGenerator.cache_.comments['block-id'] = { text: '@ruby:method:print' };
93+
RubyGenerator.valueToCode = jest.fn().mockReturnValue('"Hello, Ruby.\\n"');
94+
const expected = 'print("Hello, Ruby.\\n")\n';
95+
expect(RubyGenerator.looks_say(block)).toEqual(expected);
96+
});
97+
98+
test('puts with tab character', () => {
99+
const block = {
100+
id: 'block-id',
101+
opcode: 'looks_say',
102+
inputs: { MESSAGE: {} }
103+
};
104+
RubyGenerator.cache_.comments['block-id'] = { text: '@ruby:method:puts' };
105+
RubyGenerator.valueToCode = jest.fn().mockReturnValue('"Hello\\tRuby"');
106+
const expected = 'puts("Hello\\tRuby")\n';
107+
expect(RubyGenerator.looks_say(block)).toEqual(expected);
108+
});
85109
});
86110

87111
describe('scrub_ (meta-comment filtering)', () => {

0 commit comments

Comments
 (0)