Skip to content

Commit 1a6521e

Browse files
takaokoujiclaude
andcommitted
feat: complete registerOnXxx infrastructure migration
This commit completes the refactoring of Ruby-to-blocks converter by: - Remove legacy onXxx functions from migrated converters: - control.js: onIf, onUntil - motion.js: onOpAsgn - my-blocks.js: onVar, onDefs - operators.js: onAnd, onOr - variables.js: onOpAsgn, onVar, onVasgn - Remove legacy _converters array infrastructure - Replace with targeted legacy support for unmigrated converters - Maintain backward compatibility for looks, sound, pen, music converters - All tests pass, ensuring functionality is preserved Closes #399 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent a0f4ad5 commit 1a6521e

File tree

6 files changed

+18
-299
lines changed

6 files changed

+18
-299
lines changed

src/lib/ruby-to-blocks-converter/control.js

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -20,36 +20,6 @@ const StopOptions = [
2020
*/
2121
const ControlConverter = {
2222

23-
onIf: function (cond, statement, elseStatement) {
24-
const block = this._createBlock('control_if', 'statement');
25-
if (!this._isFalse(cond)) {
26-
this._addInput(block, 'CONDITION', cond);
27-
}
28-
this._addSubstack(block, statement);
29-
if (elseStatement) {
30-
block.opcode = 'control_if_else';
31-
this._addSubstack(block, elseStatement, 2);
32-
}
33-
return block;
34-
},
35-
36-
onUntil: function (cond, statement) {
37-
statement = this._removeWaitBlocks(statement);
38-
39-
let opcode;
40-
if (statement === null) {
41-
opcode = 'control_wait_until';
42-
} else {
43-
opcode = 'control_repeat_until';
44-
}
45-
const block = this._createBlock(opcode, 'statement');
46-
if (!this._isFalse(cond)) {
47-
this._addInput(block, 'CONDITION', cond);
48-
}
49-
this._addSubstack(block, statement);
50-
return block;
51-
},
52-
5323
register: function (converter) {
5424
// sleep(duration) - control_wait
5525
converter.registerCallMethod('self', 'sleep', 1, params => {

src/lib/ruby-to-blocks-converter/index.js

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -89,25 +89,6 @@ class RubyToBlocksConverter {
8989
constructor (vm) {
9090
this.vm = vm;
9191
this._translator = message => message.defaultMessage;
92-
this._converters = [
93-
MusicConverter,
94-
PenConverter,
95-
EV3Converter,
96-
GdxForConverter,
97-
SmalrubotS1Converter,
98-
BoostConverter,
99-
TranslateConverter,
100-
MakeyMakeyConverter,
101-
102-
MotionConverter,
103-
LooksConverter,
104-
SoundConverter,
105-
ControlConverter,
106-
SensingConverter,
107-
OperatorsConverter,
108-
VariablesConverter,
109-
MyBlocksConverter
110-
];
11192
this._receiverToMethods = {};
11293
this._receiverToMyBlocks = {};
11394
this._onIfHandlers = [];
@@ -593,16 +574,31 @@ class RubyToBlocksConverter {
593574
}
594575
}
595576

596-
// Then, check legacy converter objects
597-
for (let i = 0; i < this._converters.length; i++) {
598-
const converter = this._converters[i];
577+
// Then, check legacy converter objects for remaining unmigrated handlers
578+
const legacyConverters = [
579+
MusicConverter,
580+
PenConverter,
581+
EV3Converter,
582+
GdxForConverter,
583+
SmalrubotS1Converter,
584+
BoostConverter,
585+
TranslateConverter,
586+
MakeyMakeyConverter,
587+
LooksConverter,
588+
SoundConverter,
589+
SensingConverter
590+
];
591+
592+
for (let i = 0; i < legacyConverters.length; i++) {
593+
const converter = legacyConverters[i];
599594
if (Object.prototype.hasOwnProperty.call(converter, handlerName)) {
600595
const block = converter[handlerName].apply(this, args);
601596
if (block) {
602597
return block;
603598
}
604599
}
605600
}
601+
606602
return null;
607603
}
608604

src/lib/ruby-to-blocks-converter/motion.js

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -164,31 +164,6 @@ const MotionConverter = {
164164
}
165165
return block;
166166
});
167-
},
168-
169-
// Handle compound assignments like x += value, y += value
170-
onOpAsgn: function (lh, operator, rh) {
171-
let block;
172-
if (this._isBlock(lh) && operator === '+' && this._isNumberOrBlock(rh)) {
173-
let xy;
174-
switch (lh.opcode) {
175-
case 'motion_xposition':
176-
case 'motion_yposition':
177-
// All Motion blocks are sprite-only
178-
if (this._isStage()) {
179-
throw new RubyToBlocksConverterError(lh.node, 'Stage selected: no motion blocks');
180-
}
181-
if (lh.opcode === 'motion_xposition') {
182-
xy = 'x';
183-
} else {
184-
xy = 'y';
185-
}
186-
block = this._changeBlock(lh, `motion_change${xy}by`, 'statement');
187-
this._addNumberInput(block, `D${_.toUpper(xy)}`, 'math_number', rh, 10);
188-
break;
189-
}
190-
}
191-
return block;
192167
}
193168
};
194169

src/lib/ruby-to-blocks-converter/my-blocks.js

Lines changed: 0 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -180,134 +180,6 @@ const MyBlocksConverter = {
180180

181181
return block;
182182
});
183-
},
184-
185-
// eslint-disable-next-line no-unused-vars
186-
onVar: function (scope, variable) {
187-
let block;
188-
if (scope === 'local') {
189-
let opcode;
190-
let blockType;
191-
if (variable.isBoolean) {
192-
opcode = 'argument_reporter_boolean';
193-
blockType = 'value_boolean';
194-
} else {
195-
opcode = 'argument_reporter_string_number';
196-
blockType = 'value';
197-
}
198-
// Use normalized variable name (should already be in snake_case lowercase)
199-
const normalizedName = this._toSnakeCaseLowercase(variable.name);
200-
block = this._createBlock(opcode, blockType, {
201-
fields: {
202-
VALUE: {
203-
name: 'VALUE',
204-
value: normalizedName
205-
}
206-
}
207-
});
208-
if (Object.prototype.hasOwnProperty.call(this._context.argumentBlocks, variable.id)) {
209-
this._context.argumentBlocks[variable.id].push(block.id);
210-
} else {
211-
this._context.argumentBlocks[variable.id] = [block.id];
212-
}
213-
}
214-
return block;
215-
},
216-
217-
// eslint-disable-next-line no-unused-vars
218-
onDefs: function (node, saved) {
219-
const receiver = this._process(node.children[0]);
220-
if (!this._isSelf(receiver)) {
221-
return null;
222-
}
223-
224-
const procedureName = node.children[1].toString();
225-
const block = this._createBlock('procedures_definition', 'hat', {
226-
topLevel: true
227-
});
228-
const procedure = this._createProcedure(procedureName);
229-
230-
const customBlock = this._createBlock('procedures_prototype', 'statement', {
231-
shadow: true
232-
});
233-
this._addInput(block, 'custom_block', customBlock);
234-
235-
this._context.localVariables = {};
236-
this._process(node.children[2]).forEach(n => {
237-
const originalName = n.toString();
238-
// Convert argument name to snake_case lowercase
239-
const normalizedName = this._toSnakeCaseLowercase(originalName);
240-
241-
procedure.argumentNames.push(normalizedName);
242-
procedure.argumentVariables.push(this._lookupOrCreateVariable(normalizedName));
243-
procedure.procCode.push('%s');
244-
procedure.argumentDefaults.push('');
245-
const inputId = Blockly.utils.genUid();
246-
procedure.argumentIds.push(inputId);
247-
const inputBlock = this._createBlock('argument_reporter_string_number', 'value', {
248-
fields: {
249-
VALUE: {
250-
name: 'VALUE',
251-
value: normalizedName
252-
}
253-
},
254-
shadow: true
255-
});
256-
this._addInput(customBlock, inputId, inputBlock);
257-
procedure.argumentBlocks.push(inputBlock);
258-
});
259-
260-
let body = this._process(node.children[3]);
261-
if (!_.isArray(body)) {
262-
body = [body];
263-
}
264-
if (this._isBlock(body[0])) {
265-
block.next = body[0].id;
266-
body[0].parent = block.id;
267-
}
268-
269-
const booleanIndexes = [];
270-
procedure.argumentVariables.forEach((v, i) => {
271-
if (v.isBoolean) {
272-
booleanIndexes.push(i);
273-
procedure.procCode[i + 1] = '%b';
274-
procedure.argumentDefaults[i] = 'false';
275-
procedure.argumentBlocks[i].opcode = 'argument_reporter_boolean';
276-
this._setBlockType(procedure.argumentBlocks[i], 'value_boolean');
277-
}
278-
});
279-
280-
if (booleanIndexes.length > 0 &&
281-
Object.prototype.hasOwnProperty.call(this._context.procedureCallBlocks, procedure.id)) {
282-
this._context.procedureCallBlocks[procedure.id].forEach(id => {
283-
const b = this._context.blocks[id];
284-
b.mutation.proccode = procedure.procCode.join(' ');
285-
booleanIndexes.forEach(booleanIndex => {
286-
const input = b.inputs[procedure.argumentIds[booleanIndex]];
287-
const inputBlock = this._context.blocks[input.block];
288-
if (inputBlock) {
289-
if (!inputBlock.shadow && input.shadow) {
290-
delete this._context.blocks[input.shadow];
291-
input.shadow = null;
292-
}
293-
}
294-
});
295-
});
296-
}
297-
298-
customBlock.mutation = {
299-
argumentdefaults: JSON.stringify(procedure.argumentDefaults),
300-
argumentids: JSON.stringify(procedure.argumentIds),
301-
argumentnames: JSON.stringify(procedure.argumentNames),
302-
children: [],
303-
proccode: procedure.procCode.join(' '),
304-
tagName: 'mutation',
305-
warp: 'false'
306-
};
307-
308-
this._restoreContext({localVariables: saved.localVariables});
309-
310-
return block;
311183
}
312184
};
313185

src/lib/ruby-to-blocks-converter/operators.js

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -254,40 +254,6 @@ const OperatorsConverter = {
254254
}
255255
return block;
256256
});
257-
},
258-
259-
// eslint-disable-next-line no-unused-vars
260-
onAnd: function (operands) {
261-
const block = this._createBlock('operator_and', 'value_boolean');
262-
operands.forEach(o => {
263-
if (o) {
264-
o.parent = block.id;
265-
}
266-
});
267-
if (!this._isFalse(operands[0])) {
268-
this._addInput(block, 'OPERAND1', this._createTextBlock(operands[0]));
269-
}
270-
if (!this._isFalse(operands[1])) {
271-
this._addInput(block, 'OPERAND2', this._createTextBlock(operands[1]));
272-
}
273-
return block;
274-
},
275-
276-
// eslint-disable-next-line no-unused-vars
277-
onOr: function (operands) {
278-
const block = this._createBlock('operator_or', 'value_boolean');
279-
operands.forEach(o => {
280-
if (o) {
281-
o.parent = block.id;
282-
}
283-
});
284-
if (!this._isFalse(operands[0])) {
285-
this._addInput(block, 'OPERAND1', this._createTextBlock(operands[0]));
286-
}
287-
if (!this._isFalse(operands[1])) {
288-
this._addInput(block, 'OPERAND2', this._createTextBlock(operands[1]));
289-
}
290-
return block;
291257
}
292258
};
293259

src/lib/ruby-to-blocks-converter/variables.js

Lines changed: 0 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -249,66 +249,6 @@ const VariablesConverter = {
249249
}
250250
return null;
251251
});
252-
},
253-
254-
// eslint-disable-next-line no-unused-vars
255-
onOpAsgn: function (lh, operator, rh) {
256-
let block;
257-
if (operator === '+' && this._isString(lh) && this._isNumberOrBlock(rh)) {
258-
const variable = this._lookupOrCreateVariable(lh);
259-
if (variable.scope !== 'local') {
260-
block = this._createBlock('data_changevariableby', 'statement', {
261-
fields: {
262-
VARIABLE: {
263-
name: 'VARIABLE',
264-
id: variable.id,
265-
value: variable.name,
266-
variableType: variable.type
267-
}
268-
}
269-
});
270-
this._addNumberInput(block, 'VALUE', 'math_number', rh, 1);
271-
}
272-
}
273-
return block;
274-
},
275-
276-
// eslint-disable-next-line no-unused-vars
277-
onVar: function (scope, variable) {
278-
if (scope === 'global' || scope === 'instance') {
279-
return this._createBlock('data_variable', 'value_variable', {
280-
fields: {
281-
VARIABLE: {
282-
name: 'VARIABLE',
283-
id: variable.id,
284-
value: variable.name,
285-
variableType: variable.type
286-
}
287-
}
288-
});
289-
}
290-
return null;
291-
},
292-
293-
// eslint-disable-next-line no-unused-vars
294-
onVasgn: function (scope, variable, rh) {
295-
if (scope === 'global' || scope === 'instance') {
296-
if (this._isNumberOrBlock(rh) || this._isStringOrBlock(rh)) {
297-
const block = this._createBlock('data_setvariableto', 'statement', {
298-
fields: {
299-
VARIABLE: {
300-
name: 'VARIABLE',
301-
id: variable.id,
302-
value: variable.name,
303-
variableType: variable.type
304-
}
305-
}
306-
});
307-
this._addTextInput(block, 'VALUE', this._isNumber(rh) ? rh.toString() : rh, '0');
308-
return block;
309-
}
310-
}
311-
return null;
312252
}
313253
};
314254

0 commit comments

Comments
 (0)