generated from gxc-solutions/gxc-template-repo
Update files
This commit is contained in:
parent
bc9dbd24e0
commit
ceeec2dc0b
13 changed files with 82 additions and 125 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -30,4 +30,4 @@ yarn-error.log
|
||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|
||||||
playground/gxc-canvas-viewer/**/*.*
|
playground/*/**/*.*
|
||||||
|
|
@ -1,15 +1,9 @@
|
||||||
import { isIgnorableKey, isImmutableKey } from "@gxc-solutions/model-base/decorators";
|
import { isIgnorableKey, isImmutableKey } from "@gxc-solutions/model-base/decorators";
|
||||||
import { IModel } from "@gxc-solutions/model-base/interfaces";
|
import { IModel } from "@gxc-solutions/model-base/interfaces";
|
||||||
import { Subject } from "rxjs";
|
import { ICommandAndArg, ICommandExecuter, ICommandResult } from "../interfaces";
|
||||||
import { ICommand, ICommandExecuter, ICommandResult } from "../interfaces";
|
|
||||||
|
|
||||||
export abstract class AbstractExecutor<T extends IModel> implements ICommandExecuter<T> {
|
export abstract class AbstractExecutor<T extends IModel> implements ICommandExecuter<T> {
|
||||||
protected _changes$ = new Subject<ICommandResult<T>[]>();
|
abstract execute(context: ICommandAndArg<T>[]): Promise<ICommandResult<T>[]>;
|
||||||
readonly changes$ = this._changes$.asObservable();
|
|
||||||
|
|
||||||
abstract execute(command: ICommand<T>, object: T): this;
|
|
||||||
|
|
||||||
abstract flush(): Promise<void>;
|
|
||||||
|
|
||||||
protected _apply(object: T, values: Partial<T>): boolean {
|
protected _apply(object: T, values: Partial<T>): boolean {
|
||||||
return Object.keys(values).some((key) => {
|
return Object.keys(values).some((key) => {
|
||||||
|
|
|
||||||
17
lib/src/executers/collector.ts
Normal file
17
lib/src/executers/collector.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { IModel } from "@gxc-solutions/model-base";
|
||||||
|
import { ICommand, ICommandAndArg, ICommandArguments } from "../interfaces";
|
||||||
|
|
||||||
|
export class CommandsCollector<T extends IModel> {
|
||||||
|
private _commands: ICommandAndArg<T>[] = [];
|
||||||
|
|
||||||
|
constructor(private _: (commands: ICommandAndArg<T>[]) => Promise<void>) {}
|
||||||
|
|
||||||
|
push(command: ICommand<T>, args: ICommandArguments<T>) {
|
||||||
|
this._commands.push({ command, args });
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
flush() {
|
||||||
|
this._(this._commands);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
import { IModel } from "@gxc-solutions/model-base/interfaces";
|
|
||||||
import { IIndexer } from "../interfaces";
|
|
||||||
import { ParallelCommandExecutor } from "./parallel-executor";
|
|
||||||
import { Queue } from "./queue";
|
|
||||||
import { SerialExecutor } from "./serial-executor";
|
|
||||||
|
|
||||||
export class CommandsExecutor<T extends IModel> {
|
|
||||||
private _queue = new Queue();
|
|
||||||
private _parallel: ParallelCommandExecutor<T>;
|
|
||||||
private _serial: SerialExecutor<T>;
|
|
||||||
|
|
||||||
constructor(private _indexer: IIndexer<T>) {
|
|
||||||
this._parallel = new ParallelCommandExecutor<T>(this._indexer);
|
|
||||||
this._serial = new SerialExecutor<T>(this._indexer);
|
|
||||||
}
|
|
||||||
|
|
||||||
async executeParallel() {
|
|
||||||
while (!this._queue.isEmpty()) {
|
|
||||||
// const flush = this._queue.dequeue();
|
|
||||||
// const result = await flush;
|
|
||||||
}
|
|
||||||
const result = this._parallel.flush();
|
|
||||||
this._queue.enqueue(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
executeIndependent() {}
|
|
||||||
}
|
|
||||||
5
lib/src/executers/index.ts
Normal file
5
lib/src/executers/index.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
export * from "./serial-executor";
|
||||||
|
export * from "./abstract-executor";
|
||||||
|
export * from "./parallel-executor";
|
||||||
|
export * from "./collector";
|
||||||
|
export * from "./manager";
|
||||||
31
lib/src/executers/manager.ts
Normal file
31
lib/src/executers/manager.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
import { IModel } from "@gxc-solutions/model-base";
|
||||||
|
import { Subject } from "rxjs";
|
||||||
|
import { ICommandResult, IIndexer } from "../interfaces";
|
||||||
|
import { CommandsCollector } from "./collector";
|
||||||
|
import { ParallelCommandExecutor } from "./parallel-executor";
|
||||||
|
import { SerialExecutor } from "./serial-executor";
|
||||||
|
|
||||||
|
export type RunType = "parallel" | "serial";
|
||||||
|
|
||||||
|
export class ExecuteManager {
|
||||||
|
protected _changes$ = new Subject<ICommandResult<IModel>[]>();
|
||||||
|
readonly changes$ = this._changes$.asObservable();
|
||||||
|
|
||||||
|
constructor(private _indexer: IIndexer<IModel>) {}
|
||||||
|
|
||||||
|
run(type: RunType) {
|
||||||
|
if (type === "parallel") {
|
||||||
|
return new CommandsCollector(async (commands) => {
|
||||||
|
const results = await new ParallelCommandExecutor(this._indexer).execute(commands);
|
||||||
|
this._changes$.next(results);
|
||||||
|
});
|
||||||
|
} else if (type === "serial") {
|
||||||
|
return new CommandsCollector(async (commands) => {
|
||||||
|
const results = await new SerialExecutor(this._indexer).execute(commands);
|
||||||
|
this._changes$.next(results);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw new Error("Unknown run type!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,31 +1,19 @@
|
||||||
import { IModel } from "@gxc-solutions/model-base/interfaces";
|
import { IModel } from "@gxc-solutions/model-base/interfaces";
|
||||||
import { ICommand, ICommandResult, IIndexer } from "../interfaces";
|
import { ICommandAndArg, ICommandResult, IIndexer } from "../interfaces";
|
||||||
import { AbstractExecutor } from "./abstract-executor";
|
import { AbstractExecutor } from "./abstract-executor";
|
||||||
import { Queue } from "./queue";
|
|
||||||
|
|
||||||
// Set of actions for apply once
|
// Set of actions for apply once
|
||||||
export class ParallelCommandExecutor<T extends IModel> extends AbstractExecutor<T> {
|
export class ParallelCommandExecutor<T extends IModel> extends AbstractExecutor<T> {
|
||||||
private _results: Promise<ICommandResult<T>[]>[] = [];
|
|
||||||
private _queue: Queue<any>;
|
|
||||||
|
|
||||||
constructor(private _indexer: IIndexer<T>) {
|
constructor(private _indexer: IIndexer<T>) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
execute(command: ICommand<T>, object: T): this {
|
async execute(context: ICommandAndArg<T>[]): Promise<ICommandResult<T>[]> {
|
||||||
const result = command.execute(object);
|
if (context.length > 0) {
|
||||||
this._results.push(result);
|
const allResults = await Promise.all(context.map(({ command, args }) => command.execute(args)));
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
async flush() {
|
|
||||||
if (this._results.length > 0) {
|
|
||||||
const allResults = await Promise.all(this._results);
|
|
||||||
const isWrongResult = this._checkResults(allResults);
|
const isWrongResult = this._checkResults(allResults);
|
||||||
|
|
||||||
if (isWrongResult) {
|
if (isWrongResult) {
|
||||||
this._results = [];
|
|
||||||
throw new Error("Commands changed the same object multiple times. This is not allowed.");
|
throw new Error("Commands changed the same object multiple times. This is not allowed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -36,12 +24,11 @@ export class ParallelCommandExecutor<T extends IModel> extends AbstractExecutor<
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!isChanged) {
|
if (!isChanged) {
|
||||||
this._results = [];
|
return [];
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
this._changes$.next(flattenResults);
|
return flattenResults;
|
||||||
}
|
}
|
||||||
this._results = [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
private _checkResults(results: ICommandResult<T>[][]): boolean {
|
private _checkResults(results: ICommandResult<T>[][]): boolean {
|
||||||
|
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
import { IModel } from "@gxc-solutions/model-base/interfaces";
|
|
||||||
import { ICommand, IIndexer } from "../interfaces";
|
|
||||||
import { AbstractExecutor } from "./abstract-executor";
|
|
||||||
|
|
||||||
interface ICommandAndArg<T extends IModel> {
|
|
||||||
command: ICommand<T>;
|
|
||||||
object: T;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set of actions for serial execution
|
|
||||||
export class SerialExecutor<T extends IModel> extends AbstractExecutor<T> {
|
|
||||||
private _commands: ICommandAndArg<T>[] = [];
|
|
||||||
private _isFlushed = false;
|
|
||||||
|
|
||||||
constructor(private _indexer: IIndexer<T>) {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
execute(command: ICommand<T>, object: T): this {
|
|
||||||
if (this._isFlushed) {
|
|
||||||
throw new Error("Cannot execute commands after flush has been called.");
|
|
||||||
}
|
|
||||||
this._commands.push({ command, object });
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
async flush(): Promise<void> {
|
|
||||||
this._isFlushed = true;
|
|
||||||
for (const { command, object } of this._commands) {
|
|
||||||
const commandResult = await command.execute(object);
|
|
||||||
const isChanged = commandResult.map(({ id, result /*type */ }) => {
|
|
||||||
const object = this._indexer.get(id);
|
|
||||||
return this._apply(object, result);
|
|
||||||
});
|
|
||||||
if (isChanged) {
|
|
||||||
this._changes$.next(commandResult);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this._commands = [];
|
|
||||||
this._isFlushed = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,29 +1,17 @@
|
||||||
import { IModel } from "@gxc-solutions/model-base/interfaces";
|
import { IModel } from "@gxc-solutions/model-base/interfaces";
|
||||||
import { ICommand, ICommandResult, IIndexer } from "../interfaces";
|
import { ICommandAndArg, ICommandResult, IIndexer } from "../interfaces";
|
||||||
import { AbstractExecutor } from "./abstract-executor";
|
import { AbstractExecutor } from "./abstract-executor";
|
||||||
|
|
||||||
interface ICommandAndArg<T extends IModel> {
|
|
||||||
command: ICommand<T>;
|
|
||||||
object: T;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set of actions for serial execution
|
// Set of actions for serial execution
|
||||||
export class SerialExecutor<T extends IModel> extends AbstractExecutor<T> {
|
export class SerialExecutor<T extends IModel> extends AbstractExecutor<T> {
|
||||||
private _commands: ICommandAndArg<T>[] = [];
|
|
||||||
|
|
||||||
constructor(private _indexer: IIndexer<T>) {
|
constructor(private _indexer: IIndexer<T>) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
execute(command: ICommand<T>, object: T): this {
|
async execute(context: ICommandAndArg<T>[]): Promise<ICommandResult<T>[]> {
|
||||||
this._commands.push({ command, object });
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
async flush(): Promise<void> {
|
|
||||||
const results = new Map<string, ICommandResult<T>>();
|
const results = new Map<string, ICommandResult<T>>();
|
||||||
|
|
||||||
for (const { command, object } of this._commands) {
|
for (const { command, args: object } of context) {
|
||||||
const commandResult = await command.execute(object);
|
const commandResult = await command.execute(object);
|
||||||
commandResult.map(({ id, result, type }) => {
|
commandResult.map(({ id, result, type }) => {
|
||||||
if (results.has(id)) {
|
if (results.has(id)) {
|
||||||
|
|
@ -40,9 +28,8 @@ export class SerialExecutor<T extends IModel> extends AbstractExecutor<T> {
|
||||||
return this._apply(object, result.result);
|
return this._apply(object, result.result);
|
||||||
});
|
});
|
||||||
if (!isChanged) {
|
if (!isChanged) {
|
||||||
this._commands = [];
|
return [];
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
this._changes$.next(Array.from(results.values()));
|
return Array.from(results.values());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,20 @@
|
||||||
import { IModel } from "@gxc-solutions/model-base/interfaces";
|
import { IModel } from "@gxc-solutions/model-base/interfaces";
|
||||||
import { Observable } from "rxjs";
|
|
||||||
|
export interface ICommandAndArg<T extends IModel> {
|
||||||
|
command: ICommand<T>;
|
||||||
|
args: ICommandArguments<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ICommandArguments<T> {
|
||||||
|
model: T;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ICommandExecuter<T extends IModel> {
|
export interface ICommandExecuter<T extends IModel> {
|
||||||
changes$: Observable<ICommandResult<T>[]>;
|
execute(context: ICommandAndArg<T>[]): Promise<ICommandResult<T>[]>;
|
||||||
execute(command: ICommand<T>, object: T): this;
|
|
||||||
flush(): Promise<void>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ICommand<T extends IModel> {
|
export interface ICommand<T extends IModel> {
|
||||||
execute(object: T): Promise<ICommandResult<T>[]>;
|
execute(args: ICommandArguments<T>): Promise<ICommandResult<T>[]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ICommandResult<T extends IModel> {
|
export interface ICommandResult<T extends IModel> {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@gxc-solutions/command-executer",
|
"name": "@gxc-solutions/command-executer",
|
||||||
"version": "0.0.1",
|
"version": "0.0.2",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"author": "GXC Solutions",
|
"author": "GXC Solutions",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"name": "template-of-lib-repo",
|
"name": "gxc-command-executer",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"rootDir": "./lib/src",
|
"rootDir": "./lib/src",
|
||||||
"outDir": "./playground/lib",
|
"outDir": "./playground/command-executer",
|
||||||
},
|
},
|
||||||
"extends": ["./tsconfig.json"],
|
"extends": ["./tsconfig.json"],
|
||||||
"exclude": ["./node_modules/*", "./lib/**/*.test.ts"]
|
"exclude": ["./node_modules/*", "./lib/**/*.test.ts"]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue