mirror of
https://github.com/google/blockly.git
synced 2026-01-11 02:47:09 +01:00
feat: add serialization of procedure models (#6582)
* feat: add procedure model serializer * chore: cleanup * fix: serializer registration * chore: add inline docs * chore: cleanup and unskip tests * chore: format * fix: refactor factories to use generics * chore: format * chore: add docs to constructor
This commit is contained in:
@@ -13,6 +13,7 @@ goog.declareModuleId('Blockly.serialization');
|
||||
import * as blocks from './serialization/blocks.js';
|
||||
import * as exceptions from './serialization/exceptions.js';
|
||||
import * as priorities from './serialization/priorities.js';
|
||||
import * as procedures from './serialization/procedures.js';
|
||||
import * as registry from './serialization/registry.js';
|
||||
import * as variables from './serialization/variables.js';
|
||||
import * as workspaces from './serialization/workspaces.js';
|
||||
@@ -22,6 +23,7 @@ export {
|
||||
blocks,
|
||||
exceptions,
|
||||
priorities,
|
||||
procedures,
|
||||
registry,
|
||||
variables,
|
||||
workspaces,
|
||||
|
||||
@@ -21,6 +21,12 @@ goog.declareModuleId('Blockly.serialization.priorities');
|
||||
* @alias Blockly.serialization.priorities.VARIABLES
|
||||
*/
|
||||
export const VARIABLES = 100;
|
||||
|
||||
/**
|
||||
* The priority for deserializing variable data.
|
||||
*/
|
||||
export const PROCEDURES = 75;
|
||||
|
||||
/**
|
||||
* The priority for deserializing blocks.
|
||||
*
|
||||
|
||||
152
core/serialization/procedures.ts
Normal file
152
core/serialization/procedures.ts
Normal file
@@ -0,0 +1,152 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2021 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import {IParameterModel} from '../interfaces/i_parameter_model.js';
|
||||
import {IProcedureModel} from '../interfaces/i_procedure_model.js';
|
||||
import type {ISerializer} from '../interfaces/i_serializer.js';
|
||||
import {ObservableProcedureModel} from '../procedures/observable_procedure_model.js';
|
||||
import {ObservableParameterModel} from '../procedures/observable_parameter_model.js';
|
||||
import * as priorities from './priorities.js';
|
||||
import * as serializationRegistry from './registry.js';
|
||||
import type {Workspace} from '../workspace.js';
|
||||
|
||||
|
||||
/**
|
||||
* Representation of a procedure data model.
|
||||
*/
|
||||
export interface State {
|
||||
id: string, name: string, returnTypes: string[]|null,
|
||||
parameters?: ParameterState[],
|
||||
}
|
||||
|
||||
/**
|
||||
* Representation of a parameter data model.
|
||||
*/
|
||||
export interface ParameterState {
|
||||
id: string, name: string, types?: string[],
|
||||
}
|
||||
|
||||
/**
|
||||
* A newable signature for an IProcedureModel.
|
||||
*
|
||||
* Refer to
|
||||
* https://www.typescriptlang.org/docs/handbook/2/generics.html#using-class-types-in-generics
|
||||
* for what is going on with this.
|
||||
*/
|
||||
type ProcedureModelConstructor<ProcedureModel extends IProcedureModel> =
|
||||
new (workspace: Workspace, name: string, id: string) => ProcedureModel;
|
||||
|
||||
/**
|
||||
* A newable signature for an IParameterModel.
|
||||
*
|
||||
* Refer to
|
||||
* https://www.typescriptlang.org/docs/handbook/2/generics.html#using-class-types-in-generics
|
||||
* for what is going on with this.
|
||||
*/
|
||||
type ParameterModelConstructor<ParameterModel extends IParameterModel> =
|
||||
new (workspace: Workspace, name: string, id: string) => ParameterModel;
|
||||
|
||||
|
||||
/** Serializes the given IProcedureModel to JSON. */
|
||||
function saveProcedure(proc: IProcedureModel): State {
|
||||
const state: State = {
|
||||
id: proc.getId(),
|
||||
name: proc.getName(),
|
||||
returnTypes: proc.getReturnTypes(),
|
||||
};
|
||||
if (!proc.getParameters().length) return state;
|
||||
state.parameters = proc.getParameters().map((param) => saveParameter(param));
|
||||
return state;
|
||||
}
|
||||
|
||||
/** Serializes the given IParameterModel to JSON. */
|
||||
function saveParameter(param: IParameterModel): ParameterState {
|
||||
const state: ParameterState = {
|
||||
id: param.getId(),
|
||||
name: param.getName(),
|
||||
};
|
||||
if (!param.getTypes().length) return state;
|
||||
state.types = param.getTypes();
|
||||
return state;
|
||||
}
|
||||
|
||||
/** Deserializes the given procedure model State from JSON. */
|
||||
function
|
||||
loadProcedure<ProcedureModel extends IProcedureModel,
|
||||
ParameterModel extends IParameterModel>(
|
||||
procedureModelClass: ProcedureModelConstructor<ProcedureModel>,
|
||||
parameterModelClass: ParameterModelConstructor<ParameterModel>,
|
||||
state: State, workspace: Workspace): ProcedureModel {
|
||||
const proc = new procedureModelClass(workspace, state.name, state.id)
|
||||
.setReturnTypes(state.returnTypes);
|
||||
if (!state.parameters) return proc;
|
||||
for (const [index, param] of state.parameters.entries()) {
|
||||
proc.insertParameter(
|
||||
loadParameter(parameterModelClass, param, workspace), index);
|
||||
}
|
||||
return proc;
|
||||
}
|
||||
|
||||
/** Deserializes the given ParameterState from JSON. */
|
||||
function loadParameter<ParameterModel extends IParameterModel>(
|
||||
parameterModelClass: ParameterModelConstructor<ParameterModel>,
|
||||
state: ParameterState, workspace: Workspace): ParameterModel {
|
||||
return new parameterModelClass(workspace, state.name, state.id)
|
||||
.setTypes(state.types || []);
|
||||
}
|
||||
|
||||
/** Serializer for saving and loading procedure state. */
|
||||
export class ProcedureSerializer<ProcedureModel extends IProcedureModel,
|
||||
ParameterModel extends
|
||||
IParameterModel> implements ISerializer {
|
||||
public priority = priorities.PROCEDURES;
|
||||
|
||||
/**
|
||||
* Constructs the procedure serializer.
|
||||
*
|
||||
* Example usage:
|
||||
* new ProcedureSerializer(MyProcedureModelClass, MyParameterModelClass)
|
||||
*
|
||||
* @param procedureModelClass The class (implementing IProcedureModel) that
|
||||
* you want this serializer to deserialize.
|
||||
* @param parameterModelClass The class (implementing IParameterModel) that
|
||||
* you want this serializer to deserialize.
|
||||
*/
|
||||
constructor(
|
||||
private readonly procedureModelClass:
|
||||
ProcedureModelConstructor<ProcedureModel>,
|
||||
private readonly parameterModelClass:
|
||||
ParameterModelConstructor<ParameterModel>) {}
|
||||
|
||||
/** Serializes the procedure models of the given workspace. */
|
||||
save(workspace: Workspace): State[]|null {
|
||||
return workspace.getProcedureMap().getProcedures().map(
|
||||
(proc) => saveProcedure(proc));
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes the procedures models defined by the given state into the
|
||||
* workspace.
|
||||
*/
|
||||
load(state: State[], workspace: Workspace) {
|
||||
const map = workspace.getProcedureMap();
|
||||
for (const procState of state) {
|
||||
map.add(loadProcedure(
|
||||
this.procedureModelClass, this.parameterModelClass, procState,
|
||||
workspace));
|
||||
}
|
||||
}
|
||||
|
||||
/** Disposes of any procedure models that exist on the workspace. */
|
||||
clear(workspace: Workspace) {
|
||||
workspace.getProcedureMap().clear();
|
||||
}
|
||||
}
|
||||
|
||||
serializationRegistry.register(
|
||||
'procedures',
|
||||
new ProcedureSerializer(
|
||||
ObservableProcedureModel, ObservableParameterModel));
|
||||
@@ -714,12 +714,11 @@ suite('JSO Deserialization', function() {
|
||||
});
|
||||
});
|
||||
|
||||
// TODO(#6522): Unskip tests.
|
||||
suite.skip('Procedures', function() {
|
||||
suite('Procedures', function() {
|
||||
class MockProcedureModel {
|
||||
constructor(id) {
|
||||
constructor(workspace, name, id) {
|
||||
this.id = id ?? Blockly.utils.idGenerator.genUid();
|
||||
this.name = '';
|
||||
this.name = name;
|
||||
this.parameters = [];
|
||||
this.returnTypes = null;
|
||||
this.enabled = true;
|
||||
@@ -776,7 +775,7 @@ suite('JSO Deserialization', function() {
|
||||
}
|
||||
|
||||
class MockParameterModel {
|
||||
constructor(name, id) {
|
||||
constructor(workspace, name, id) {
|
||||
this.id = id ?? Blockly.utils.idGenerator.genUid();
|
||||
this.name = name;
|
||||
this.types = [];
|
||||
@@ -808,7 +807,7 @@ suite('JSO Deserialization', function() {
|
||||
setup(function() {
|
||||
this.procedureSerializer = new
|
||||
Blockly.serialization.procedures.ProcedureSerializer(
|
||||
MockProcedureModel, MockParameterModel);
|
||||
MockProcedureModel, MockParameterModel);
|
||||
this.procedureMap = this.workspace.getProcedureMap();
|
||||
});
|
||||
|
||||
|
||||
@@ -795,8 +795,7 @@ suite('JSO Serialization', function() {
|
||||
});
|
||||
});
|
||||
|
||||
// TODO(#6522): Unskip serialization tests.
|
||||
suite.skip('Procedures', function() {
|
||||
suite('Procedures', function() {
|
||||
class MockProcedureModel {
|
||||
constructor() {
|
||||
this.id = Blockly.utils.idGenerator.genUid();
|
||||
@@ -968,7 +967,7 @@ suite('JSO Serialization', function() {
|
||||
new MockProcedureModel().insertParameter(parameterModel, 0));
|
||||
const jso = Blockly.serialization.workspaces.save(this.workspace);
|
||||
const parameter = jso['procedures'][0]['parameters'][0];
|
||||
assertProperty(parameter, 'id', 'testparam');
|
||||
assertProperty(parameter, 'name', 'testparam');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user