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
|
||||
Thumbs.db
|
||||
|
||||
playground/gxc-canvas-viewer/**/*.*
|
||||
playground/*/**/*.*
|
||||
|
|
@ -1,15 +1,9 @@
|
|||
import { isIgnorableKey, isImmutableKey } from "@gxc-solutions/model-base/decorators";
|
||||
import { IModel } from "@gxc-solutions/model-base/interfaces";
|
||||
import { Subject } from "rxjs";
|
||||
import { ICommand, ICommandExecuter, ICommandResult } from "../interfaces";
|
||||
import { ICommandAndArg, ICommandExecuter, ICommandResult } from "../interfaces";
|
||||
|
||||
export abstract class AbstractExecutor<T extends IModel> implements ICommandExecuter<T> {
|
||||
protected _changes$ = new Subject<ICommandResult<T>[]>();
|
||||
readonly changes$ = this._changes$.asObservable();
|
||||
|
||||
abstract execute(command: ICommand<T>, object: T): this;
|
||||
|
||||
abstract flush(): Promise<void>;
|
||||
abstract execute(context: ICommandAndArg<T>[]): Promise<ICommandResult<T>[]>;
|
||||
|
||||
protected _apply(object: T, values: Partial<T>): boolean {
|
||||
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 { ICommand, ICommandResult, IIndexer } from "../interfaces";
|
||||
import { ICommandAndArg, ICommandResult, IIndexer } from "../interfaces";
|
||||
import { AbstractExecutor } from "./abstract-executor";
|
||||
import { Queue } from "./queue";
|
||||
|
||||
// Set of actions for apply once
|
||||
export class ParallelCommandExecutor<T extends IModel> extends AbstractExecutor<T> {
|
||||
private _results: Promise<ICommandResult<T>[]>[] = [];
|
||||
private _queue: Queue<any>;
|
||||
|
||||
constructor(private _indexer: IIndexer<T>) {
|
||||
super();
|
||||
}
|
||||
|
||||
execute(command: ICommand<T>, object: T): this {
|
||||
const result = command.execute(object);
|
||||
this._results.push(result);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
async flush() {
|
||||
if (this._results.length > 0) {
|
||||
const allResults = await Promise.all(this._results);
|
||||
async execute(context: ICommandAndArg<T>[]): Promise<ICommandResult<T>[]> {
|
||||
if (context.length > 0) {
|
||||
const allResults = await Promise.all(context.map(({ command, args }) => command.execute(args)));
|
||||
const isWrongResult = this._checkResults(allResults);
|
||||
|
||||
if (isWrongResult) {
|
||||
this._results = [];
|
||||
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) {
|
||||
this._results = [];
|
||||
return;
|
||||
return [];
|
||||
}
|
||||
this._changes$.next(flattenResults);
|
||||
return flattenResults;
|
||||
}
|
||||
this._results = [];
|
||||
return [];
|
||||
}
|
||||
|
||||
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 { ICommand, ICommandResult, IIndexer } from "../interfaces";
|
||||
import { ICommandAndArg, ICommandResult, 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>[] = [];
|
||||
|
||||
constructor(private _indexer: IIndexer<T>) {
|
||||
super();
|
||||
}
|
||||
|
||||
execute(command: ICommand<T>, object: T): this {
|
||||
this._commands.push({ command, object });
|
||||
return this;
|
||||
}
|
||||
|
||||
async flush(): Promise<void> {
|
||||
async execute(context: ICommandAndArg<T>[]): Promise<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);
|
||||
commandResult.map(({ id, result, type }) => {
|
||||
if (results.has(id)) {
|
||||
|
|
@ -40,9 +28,8 @@ export class SerialExecutor<T extends IModel> extends AbstractExecutor<T> {
|
|||
return this._apply(object, result.result);
|
||||
});
|
||||
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 { 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> {
|
||||
changes$: Observable<ICommandResult<T>[]>;
|
||||
execute(command: ICommand<T>, object: T): this;
|
||||
flush(): Promise<void>;
|
||||
execute(context: ICommandAndArg<T>[]): Promise<ICommandResult<T>[]>;
|
||||
}
|
||||
|
||||
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> {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@gxc-solutions/command-executer",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.2",
|
||||
"main": "index.js",
|
||||
"author": "GXC Solutions",
|
||||
"publishConfig": {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "template-of-lib-repo",
|
||||
"name": "gxc-command-executer",
|
||||
"version": "0.0.0",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"rootDir": "./lib/src",
|
||||
"outDir": "./playground/lib",
|
||||
"outDir": "./playground/command-executer",
|
||||
},
|
||||
"extends": ["./tsconfig.json"],
|
||||
"exclude": ["./node_modules/*", "./lib/**/*.test.ts"]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue