Skip to content

Commit b6055f2

Browse files
authored
Fixed mrc_class_method_def blocks for methods that return a value (#299)
* In mrc_class_method_def.ts: Added constants for NO_RETURN_VALUE and UNTYPED_RETURN_VALUE. Added upgrade_004_to_005 to change mrcReturnType from 'Any' to UNTYPED_RETURN_VALUE. Changed createCustomMethodBlockWithReturn to use UNTYPED_RETURN_VALUE instead of 'Any'. In upgrade_project.ts: Incremented CURRENT_VERSION to 0.0.5. Changed switch statement in upgradeProjectIfNecessary to have the default case at the top. All other cases have @ts-ignore and blank lines so it is more clear that the @ts-ignore goes with the case after it. Added upgradeBlocksFiles function that can be called from other upgradeFrom_..._to_... functions. Added upgradeFrom_004_to_005. * In mrc_class_method_def.ts: Added upgrade_002_to_003 function. In upgrade_project.ts: Modified upgradeBlocksFiles to take a predicate function. Added anyModuleType and isOpMode functions that can be passed to upgradeBlocksFiles as the predicate function. Modified upgradeFrom_002_to_003 to call upgradeBlocksFiles. Modified upgradeFrom_004_to_005 to pass anyModuleType to upgradeBlocksFiles. * Added preupgradePredicate and preupgradeFunc parameters to upgradeBlocksFiles. Modified upgradeFrom_001_to_002 to call upgradeBlocksFiles. * In mrc_mechanism_component_holder.ts: Renamed function hidePrivateComponents to upgrade_001_to_002 and added code to check that the module type is ROBOT. In module_content.ts: Renamed addPrivateComponents to preupgrade_001_to_002. In upgrade_project.ts: Updated upgradeFrom_001_to_002. * Removed code that checks that the workspace is headless in upgrade functions.
1 parent 2523d8a commit b6055f2

File tree

4 files changed

+158
-76
lines changed

4 files changed

+158
-76
lines changed

src/blocks/mrc_class_method_def.ts

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ import * as paramContainer from './mrc_param_container'
3737

3838
export const BLOCK_NAME = 'mrc_class_method_def';
3939

40+
const NO_RETURN_VALUE = 'None';
41+
const UNTYPED_RETURN_VALUE = '';
42+
4043
const INPUT_TITLE = 'TITLE';
4144
export const FIELD_METHOD_NAME = 'NAME';
4245
const FIELD_PARAM_PREFIX = 'PARAM_';
@@ -82,8 +85,8 @@ type ClassMethodDefExtraState = {
8285
canBeCalledOutsideClass: boolean,
8386
/**
8487
* The return type of the function.
85-
* Use 'None' for no return value.
86-
* Use '' for an untyped return value.
88+
* Use NO_RETURN_VALUE for no return value.
89+
* Use UNTYPED_RETURN_VALUE for an untyped return value.
8790
*/
8891
returnType: string,
8992
/**
@@ -252,8 +255,8 @@ const CLASS_METHOD_DEF = {
252255
this.removeInput(INPUT_RETURN);
253256
}
254257

255-
// Add return input if return type is not 'None'
256-
if (this.mrcReturnType && this.mrcReturnType !== 'None') {
258+
// Add return input if return type is not NO_RETURN_VALUE
259+
if (this.mrcReturnType !== undefined && this.mrcReturnType !== NO_RETURN_VALUE) {
257260
this.appendValueInput(INPUT_RETURN)
258261
.setAlign(Blockly.inputs.Align.RIGHT)
259262
.appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN);
@@ -348,6 +351,11 @@ const CLASS_METHOD_DEF = {
348351
methodBlock.mrcParameters = filteredParams;
349352
}
350353
},
354+
upgrade_004_to_005: function(this: ClassMethodDefBlock) {
355+
if (this.mrcReturnType === 'Any') {
356+
this.mrcReturnType = UNTYPED_RETURN_VALUE;
357+
}
358+
},
351359
};
352360

353361
/**
@@ -508,7 +516,7 @@ export function createCustomMethodBlock(): toolboxItems.Block {
508516
canChangeSignature: true,
509517
canBeCalledWithinClass: true,
510518
canBeCalledOutsideClass: true,
511-
returnType: 'None',
519+
returnType: NO_RETURN_VALUE,
512520
params: [],
513521
};
514522
const fields: {[key: string]: any} = {};
@@ -521,7 +529,7 @@ export function createCustomMethodBlockWithReturn(): toolboxItems.Block {
521529
canChangeSignature: true,
522530
canBeCalledWithinClass: true,
523531
canBeCalledOutsideClass: true,
524-
returnType: 'Any',
532+
returnType: UNTYPED_RETURN_VALUE,
525533
params: [],
526534
};
527535
const fields: {[key: string]: any} = {};
@@ -609,3 +617,23 @@ export function getMethodNamesAlreadyOverriddenInWorkspace(
609617
}
610618
});
611619
}
620+
621+
/**
622+
* Upgrades the ClassMethodDefBlocks in the given workspace from version 002 to 003.
623+
* This function should only be called when upgrading old projects.
624+
*/
625+
export function upgrade_002_to_003(workspace: Blockly.Workspace): void {
626+
workspace.getBlocksByType(BLOCK_NAME).forEach(block => {
627+
(block as ClassMethodDefBlock).upgrade_002_to_003();
628+
});
629+
}
630+
631+
/**
632+
* Upgrades the ClassMethodDefBlocks in the given workspace from version 004 to 005.
633+
* This function should only be called when upgrading old projects.
634+
*/
635+
export function upgrade_004_to_005(workspace: Blockly.Workspace): void {
636+
workspace.getBlocksByType(BLOCK_NAME).forEach(block => {
637+
(block as ClassMethodDefBlock).upgrade_004_to_005();
638+
});
639+
}

src/blocks/mrc_mechanism_component_holder.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import { ComponentBlock } from './mrc_component';
3636
import { BLOCK_NAME as MRC_EVENT_NAME } from './mrc_event';
3737
import { OUTPUT_NAME as EVENT_OUTPUT } from './mrc_event';
3838
import { EventBlock } from './mrc_event';
39+
import { getModuleTypeForWorkspace } from './utils/workspaces';
3940

4041
export const BLOCK_NAME = 'mrc_mechanism_component_holder';
4142

@@ -520,13 +521,14 @@ export function mrcDescendantsMayHaveChanged(workspace: Blockly.Workspace): void
520521
}
521522

522523
/**
523-
* Hide private components.
524+
* Upgrades the MechanismComponentHolderBlock in the given workspace from version 001 to 002 by
525+
* setting mrcHidePrivateComponents to true.
524526
* This function should only be called when upgrading old projects.
525527
*/
526-
export function hidePrivateComponents(workspace: Blockly.Workspace) {
527-
// Make sure the workspace is headless.
528-
if (workspace.rendered) {
529-
throw new Error('hidePrivateComponents should never be called with a rendered workspace.');
528+
export function upgrade_001_to_002(workspace: Blockly.Workspace) {
529+
// Make sure the module type is ROBOT.
530+
if (getModuleTypeForWorkspace(workspace) !== storageModule.ModuleType.ROBOT) {
531+
throw new Error('upgrade_001_to_002 should only be called for a robot module.');
530532
}
531533
workspace.getBlocksByType(BLOCK_NAME).forEach(block => {
532534
(block as MechanismComponentHolderBlock).mrcHidePrivateComponents = true;

src/storage/module_content.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -270,10 +270,10 @@ export class ModuleContent {
270270
}
271271

272272
/**
273-
* Add privateComponents field.
274-
* This function should only called when upgrading old projects.
273+
* Preupgrades the module content text by adding the privateComponents field.
274+
* This function should only be called when upgrading old projects.
275275
*/
276-
export function addPrivateComponents(moduleContentText: string): string {
276+
export function preupgrade_001_to_002(moduleContentText: string): string {
277277
const parsedContent = JSON.parse(moduleContentText);
278278
if (!('privateComponents' in parsedContent)) {
279279
parsedContent.privateComponents = [];

src/storage/upgrade_project.ts

Lines changed: 114 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -22,130 +22,170 @@
2222
import * as semver from 'semver';
2323
import * as Blockly from 'blockly/core';
2424

25-
import * as mechanismComponentHolder from '../blocks/mrc_mechanism_component_holder';
2625
import * as commonStorage from './common_storage';
2726
import * as storageModule from './module';
2827
import * as storageModuleContent from './module_content';
2928
import * as storageNames from './names';
3029
import * as storageProject from './project';
31-
import { ClassMethodDefBlock, BLOCK_NAME as MRC_CLASS_METHOD_DEF_BLOCK_NAME } from '../blocks/mrc_class_method_def';
30+
import { upgrade_001_to_002 } from '../blocks/mrc_mechanism_component_holder';
31+
import { upgrade_002_to_003, upgrade_004_to_005 } from '../blocks/mrc_class_method_def';
3232
import * as workspaces from '../blocks/utils/workspaces';
3333

3434
export const NO_VERSION = '0.0.0';
35-
export const CURRENT_VERSION = '0.0.4';
35+
export const CURRENT_VERSION = '0.0.5';
3636

3737
export async function upgradeProjectIfNecessary(
3838
storage: commonStorage.Storage, projectName: string): Promise<void> {
3939
const projectInfo = await storageProject.fetchProjectInfo(storage, projectName);
4040
if (semver.lt(projectInfo.version, CURRENT_VERSION)) {
4141
switch (projectInfo.version) {
42+
default:
43+
throw new Error('Unrecognized project version: ' + projectInfo.version);
44+
45+
// Intentional fallthrough after case '0.0.0'
4246
// @ts-ignore
4347
case '0.0.0':
4448
upgradeFrom_000_to_001(storage, projectName, projectInfo)
45-
// Intentional fallthrough
49+
50+
// Intentional fallthrough after case '0.0.1'
4651
// @ts-ignore
4752
case '0.0.1':
4853
upgradeFrom_001_to_002(storage, projectName, projectInfo);
49-
// Intentional fallthrough
54+
55+
// Intentional fallthrough after case '0.0.2'
5056
// @ts-ignore
5157
case '0.0.2':
52-
upgradeFrom_002_to_003(storage, projectName, projectInfo);
58+
upgradeFrom_002_to_003(storage, projectName, projectInfo);
59+
60+
// Intentional fallthrough after case '0.0.3'
61+
// @ts-ignore
5362
case '0.0.3':
5463
upgradeFrom_003_to_004(storage, projectName, projectInfo);
55-
break;
56-
default:
57-
throw new Error('Unrecognized project version: ' + projectInfo.version);
5864

65+
// Intentional fallthrough after case '0.0.4'
66+
// @ts-ignore
67+
case '0.0.4':
68+
upgradeFrom_004_to_005(storage, projectName, projectInfo);
5969
}
6070
await storageProject.saveProjectInfo(storage, projectName);
6171
}
6272
}
6373

64-
async function upgradeFrom_000_to_001(
65-
_storage: commonStorage.Storage,
66-
_projectName: string,
67-
projectInfo: storageProject.ProjectInfo): Promise<void> {
68-
// Project was saved without a project.info.json file.
69-
// Nothing needs to be done to upgrade to '0.0.1';
70-
projectInfo.version = '0.0.1';
71-
}
72-
73-
async function upgradeFrom_001_to_002(
74+
async function upgradeBlocksFiles(
7475
storage: commonStorage.Storage,
7576
projectName: string,
76-
projectInfo: storageProject.ProjectInfo): Promise<void> {
77-
// Modules were saved without private components.
78-
// The Robot's mrc_mechanism_component_holder block was saved without hidePrivateComponents.
77+
preupgradePredicate: (moduleType: storageModule.ModuleType) => boolean,
78+
preupgradeFunc: (moduleContentText: string) => string,
79+
upgradePredicate: (moduleType: storageModule.ModuleType) => boolean,
80+
upgradeFunc: (w: Blockly.Workspace) => void
81+
): Promise<void> {
7982
const projectFileNames: string[] = await storage.list(
8083
storageNames.makeProjectDirectoryPath(projectName));
8184
for (const projectFileName of projectFileNames) {
8285
const modulePath = storageNames.makeFilePath(projectName, projectFileName);
83-
let moduleContentText = await storage.fetchFileContentText(modulePath);
86+
const moduleType = storageNames.getModuleType(modulePath);
87+
const originalModuleContentText = await storage.fetchFileContentText(modulePath);
88+
let moduleContentText = originalModuleContentText;
8489

85-
// Add private components to the module content.
86-
moduleContentText = storageModuleContent.addPrivateComponents(moduleContentText);
90+
if (preupgradePredicate(moduleType)) {
91+
moduleContentText = preupgradeFunc(moduleContentText);
92+
}
8793

88-
if (storageNames.getModuleType(modulePath) === storageModule.ModuleType.ROBOT) {
89-
// If this module is the robot, hide the private components part of the
90-
// mrc_mechanism_component_holder block.
94+
if (upgradePredicate(moduleType)) {
9195
const moduleContent = storageModuleContent.parseModuleContentText(moduleContentText);
9296
let blocks = moduleContent.getBlocks();
9397

9498
// Create a temporary workspace to upgrade the blocks.
95-
const headlessWorkspace = workspaces.createHeadlessWorkspace(storageModule.ModuleType.ROBOT);
99+
const headlessWorkspace = workspaces.createHeadlessWorkspace(moduleType);
96100

97101
try {
98102
Blockly.serialization.workspaces.load(blocks, headlessWorkspace);
99-
mechanismComponentHolder.hidePrivateComponents(headlessWorkspace);
103+
upgradeFunc(headlessWorkspace);
100104
blocks = Blockly.serialization.workspaces.save(headlessWorkspace);
101105
} finally {
102106
workspaces.destroyHeadlessWorkspace(headlessWorkspace);
103107
}
108+
104109
moduleContent.setBlocks(blocks);
105110
moduleContentText = moduleContent.getModuleContentText();
106111
}
107112

108-
await storage.saveFile(modulePath, moduleContentText);
113+
if (moduleContentText !== originalModuleContentText) {
114+
await storage.saveFile(modulePath, moduleContentText);
115+
}
109116
}
110-
projectInfo.version = '0.0.2';
111117
}
112118

113-
async function upgradeFrom_002_to_003(
114-
storage: commonStorage.Storage,
115-
projectName: string,
116-
projectInfo: storageProject.ProjectInfo): Promise<void> {
117-
// Opmodes had robot as a parameter to init method
118-
const projectFileNames: string[] = await storage.list(
119-
storageNames.makeProjectDirectoryPath(projectName));
119+
/**
120+
* Predicate function that can be passed to upgradeBlocksFiles indicating that all modules should be
121+
* affected.
122+
*/
123+
function anyModuleType(_moduleType: storageModule.ModuleType): boolean {
124+
return true;
125+
}
120126

121-
for (const projectFileName of projectFileNames) {
122-
const modulePath = storageNames.makeFilePath(projectName, projectFileName);
127+
/**
128+
* Predicate function that can be passed to upgradeBlocksFiles indicating that only OpMode modules
129+
* should be affected.
130+
*/
131+
function isOpMode(moduleType: storageModule.ModuleType): boolean {
132+
return moduleType === storageModule.ModuleType.OPMODE;
133+
}
123134

124-
if (storageNames.getModuleType(modulePath) === storageModule.ModuleType.OPMODE) {
125-
let moduleContentText = await storage.fetchFileContentText(modulePath);
126-
const moduleContent = storageModuleContent.parseModuleContentText(moduleContentText);
127-
let blocks = moduleContent.getBlocks();
135+
/**
136+
* Predicate function that can be passed to upgradeBlocksFiles indicating that only Robot modules
137+
* should be affected.
138+
*/
139+
function isRobot(moduleType: storageModule.ModuleType): boolean {
140+
return moduleType === storageModule.ModuleType.ROBOT;
141+
}
128142

129-
// Create a temporary workspace to upgrade the blocks.
130-
const headlessWorkspace = workspaces.createHeadlessWorkspace(storageModule.ModuleType.ROBOT);
143+
/**
144+
* Predicate function that can be passed to upgradeBlocksFiles indicating that no modules should be
145+
* affected.
146+
*/
147+
function noModuleTypes(_moduleType: storageModule.ModuleType): boolean {
148+
return false;
149+
}
131150

132-
try {
133-
Blockly.serialization.workspaces.load(blocks, headlessWorkspace);
151+
/**
152+
* Preupgrade function that makes no changes to moduleContentText.
153+
*/
154+
function noPreupgrade(moduleContentText: string): string {
155+
return moduleContentText;
156+
}
134157

135-
// Method blocks need to be upgraded
136-
headlessWorkspace.getBlocksByType(MRC_CLASS_METHOD_DEF_BLOCK_NAME, false).forEach(block => {
137-
(block as ClassMethodDefBlock).upgrade_002_to_003();
138-
});
139-
blocks = Blockly.serialization.workspaces.save(headlessWorkspace);
140-
} finally {
141-
workspaces.destroyHeadlessWorkspace(headlessWorkspace);
142-
}
158+
async function upgradeFrom_000_to_001(
159+
_storage: commonStorage.Storage,
160+
_projectName: string,
161+
projectInfo: storageProject.ProjectInfo): Promise<void> {
162+
// Project was saved without a project.info.json file.
163+
// Nothing needs to be done to upgrade to '0.0.1';
164+
projectInfo.version = '0.0.1';
165+
}
143166

144-
moduleContent.setBlocks(blocks);
145-
moduleContentText = moduleContent.getModuleContentText();
146-
await storage.saveFile(modulePath, moduleContentText);
147-
}
148-
}
167+
async function upgradeFrom_001_to_002(
168+
storage: commonStorage.Storage,
169+
projectName: string,
170+
projectInfo: storageProject.ProjectInfo): Promise<void> {
171+
// Modules were saved without private components.
172+
// The Robot's mrc_mechanism_component_holder block was saved without hidePrivateComponents.
173+
await upgradeBlocksFiles(
174+
storage, projectName,
175+
anyModuleType, storageModuleContent.preupgrade_001_to_002,
176+
isRobot, upgrade_001_to_002);
177+
projectInfo.version = '0.0.2';
178+
}
179+
180+
async function upgradeFrom_002_to_003(
181+
storage: commonStorage.Storage,
182+
projectName: string,
183+
projectInfo: storageProject.ProjectInfo): Promise<void> {
184+
// OpModes had robot as a parameter to init method.
185+
await upgradeBlocksFiles(
186+
storage, projectName,
187+
noModuleTypes, noPreupgrade,
188+
isOpMode, upgrade_002_to_003);
149189
projectInfo.version = '0.0.3';
150190
}
151191

@@ -157,3 +197,15 @@ async function upgradeFrom_003_to_004(
157197
// from loading a project with an older version of software.
158198
projectInfo.version = '0.0.4';
159199
}
200+
201+
async function upgradeFrom_004_to_005(
202+
storage: commonStorage.Storage,
203+
projectName: string,
204+
projectInfo: storageProject.ProjectInfo): Promise<void> {
205+
// mrc_class_method_def blocks that return a value need to have returnType changed from 'Any' to ''.
206+
await upgradeBlocksFiles(
207+
storage, projectName,
208+
noModuleTypes, noPreupgrade,
209+
anyModuleType, upgrade_004_to_005);
210+
projectInfo.version = '0.0.5';
211+
}

0 commit comments

Comments
 (0)