Skip to content

Commit 4caddb6

Browse files
authored
Merge pull request #398 from smalruby/refactor/my-blocks-ruby-to-blocks-converter
refactor: migrate my-blocks converter to register pattern with dynamic call handler
2 parents 99bb95c + 75aa637 commit 4caddb6

File tree

2 files changed

+92
-49
lines changed

2 files changed

+92
-49
lines changed

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

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ class RubyToBlocksConverter {
109109
MyBlocksConverter
110110
];
111111
this._receiverToMethods = {};
112+
this._receiverToMyBlocks = {};
112113
this.reset();
113114

114115
[
@@ -134,7 +135,8 @@ class RubyToBlocksConverter {
134135
GdxForConverter,
135136
SmalrubotS1Converter,
136137
MotionConverter,
137-
SensingConverter
138+
SensingConverter,
139+
MyBlocksConverter
138140
].forEach(x => x.register(this));
139141
}
140142

@@ -343,16 +345,59 @@ class RubyToBlocksConverter {
343345
this.registerCallMethodWithBlock(receiverName, name, numArgs, 'none', createBlockFunc);
344346
}
345347

348+
registerCallMyBlock (receiverName, myBlockHandler) {
349+
if (receiverName === 'any') {
350+
this._anyReceiverNames().forEach(rn => this.registerCallMyBlock(rn, myBlockHandler));
351+
return;
352+
}
353+
354+
if (_.isArray(receiverName)) {
355+
receiverName.forEach(rn => this.registerCallMyBlock(rn, myBlockHandler));
356+
return;
357+
}
358+
359+
if (receiverName === 'self') {
360+
this.registerCallMyBlock('sprite', myBlockHandler);
361+
this.registerCallMyBlock('stage', myBlockHandler);
362+
return;
363+
}
364+
365+
if (!this._receiverToMyBlocks[receiverName]) {
366+
this._receiverToMyBlocks[receiverName] = [];
367+
}
368+
this._receiverToMyBlocks[receiverName].push(myBlockHandler);
369+
}
370+
346371
callMethod (receiver, name, args, rubyBlockArgs, rubyBlock, node) {
347372
const receiverName = this._getReceiverName(receiver);
348373
if (!receiverName) return null;
349374

375+
// Check for my-block procedure calls
376+
if (this._receiverToMyBlocks[receiverName]) {
377+
const procedure = this._lookupProcedure(name);
378+
if (procedure) {
379+
const params = {
380+
receiver: receiver,
381+
receiverName: receiverName,
382+
name: name,
383+
args: args,
384+
rubyBlockArgs: rubyBlockArgs,
385+
rubyBlock: rubyBlock,
386+
node: node,
387+
procedure: procedure
388+
};
389+
390+
for (const handler of this._receiverToMyBlocks[receiverName]) {
391+
const block = handler.apply(this, [params]);
392+
if (block) return block;
393+
}
394+
}
395+
}
396+
350397
const methodToNumArgs = this._receiverToMethods[receiverName];
351398
if (!methodToNumArgs) return null;
352-
353399
const numArgsToNumRubyBlockArgs = methodToNumArgs[name];
354400
if (!numArgsToNumRubyBlockArgs) return null;
355-
356401
const numRubyBlockArgsToCreateBlockFuncs = numArgsToNumRubyBlockArgs[args.length];
357402
if (!numRubyBlockArgsToCreateBlockFuncs) return null;
358403

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

Lines changed: 44 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
/* global Opal */
21
import _ from 'lodash';
32
import Blockly from 'scratch-blocks';
43
import {RubyToBlocksConverterError} from './errors';
@@ -7,54 +6,53 @@ import {RubyToBlocksConverterError} from './errors';
76
* My Blocks converter
87
*/
98
const MyBlocksConverter = {
10-
// eslint-disable-next-line no-unused-vars
11-
onSend: function (receiver, name, args, rubyBlockArgs, rubyBlock) {
12-
let block;
13-
if (this._isSelf(receiver) || receiver === Opal.nil) {
14-
const procedure = this._lookupProcedure(name);
15-
if (procedure) {
16-
if (procedure.argumentIds.length === args.length) {
17-
block = this._createBlock('procedures_call', 'statement', {
18-
mutation: {
19-
argumentids: JSON.stringify(procedure.argumentIds),
20-
children: [],
21-
proccode: procedure.procCode.join(' '),
22-
tagName: 'mutation',
23-
warp: 'false'
24-
}
25-
});
9+
register: function (converter) {
10+
// Register my-block handler for procedure calls
11+
converter.registerCallMyBlock('self', params => {
12+
const {name, args, procedure} = params;
2613

27-
if (Object.prototype.hasOwnProperty.call(this._context.procedureCallBlocks, procedure.id)) {
28-
this._context.procedureCallBlocks[procedure.id].push(block.id);
29-
} else {
30-
this._context.procedureCallBlocks[procedure.id] = [block.id];
31-
}
14+
if (procedure.argumentIds.length !== args.length) return null;
3215

33-
args.forEach((arg, i) => {
34-
const argumentId = procedure.argumentIds[i];
35-
if (this._isFalseOrBooleanBlock(arg)) {
36-
if (procedure.argumentVariables[i].isBoolean ||
37-
this._changeToBooleanArgument(procedure.argumentNames[i])) {
38-
if (!this._isFalse(arg)) {
39-
this._addInput(block, argumentId, arg, null);
40-
}
41-
return;
42-
}
43-
}
44-
if (!procedure.argumentVariables[i].isBoolean &&
45-
(this._isNumberOrBlock(arg) || this._isStringOrBlock(arg))) {
46-
this._addTextInput(block, argumentId, this._isNumber(arg) ? arg.toString() : arg, '');
47-
return;
48-
}
49-
throw new RubyToBlocksConverterError(
50-
this._context.currentNode,
51-
`invalid type of My Block "${name}" argument #${i + 1}`
52-
);
53-
});
16+
const block = converter._createBlock('procedures_call', 'statement', {
17+
mutation: {
18+
argumentids: JSON.stringify(procedure.argumentIds),
19+
children: [],
20+
proccode: procedure.procCode.join(' '),
21+
tagName: 'mutation',
22+
warp: 'false'
5423
}
24+
});
25+
26+
if (Object.prototype.hasOwnProperty.call(converter._context.procedureCallBlocks, procedure.id)) {
27+
converter._context.procedureCallBlocks[procedure.id].push(block.id);
28+
} else {
29+
converter._context.procedureCallBlocks[procedure.id] = [block.id];
5530
}
56-
}
57-
return block;
31+
32+
args.forEach((arg, i) => {
33+
const argumentId = procedure.argumentIds[i];
34+
if (converter._isFalseOrBooleanBlock(arg)) {
35+
if (procedure.argumentVariables[i].isBoolean ||
36+
converter._changeToBooleanArgument(procedure.argumentNames[i])) {
37+
if (!converter._isFalse(arg)) {
38+
converter._addInput(block, argumentId, arg, null);
39+
}
40+
return;
41+
}
42+
}
43+
if (!procedure.argumentVariables[i].isBoolean &&
44+
(converter._isNumberOrBlock(arg) || converter._isStringOrBlock(arg))) {
45+
converter._addTextInput(block, argumentId, converter._isNumber(arg) ? arg.toString() : arg, '');
46+
return;
47+
}
48+
throw new RubyToBlocksConverterError(
49+
converter._context.currentNode,
50+
`invalid type of My Block "${name}" argument #${i + 1}`
51+
);
52+
});
53+
54+
return block;
55+
});
5856
},
5957

6058
// eslint-disable-next-line no-unused-vars
@@ -112,7 +110,7 @@ const MyBlocksConverter = {
112110
const originalName = n.toString();
113111
// Convert argument name to snake_case lowercase
114112
const normalizedName = this._toSnakeCaseLowercase(originalName);
115-
113+
116114
procedure.argumentNames.push(normalizedName);
117115
procedure.argumentVariables.push(this._lookupOrCreateVariable(normalizedName));
118116
procedure.procCode.push('%s');

0 commit comments

Comments
 (0)