From fe2ffc9182c0f796be3679b18f0ac795df92139f Mon Sep 17 00:00:00 2001 From: Dmitriy Pleshevskiy Date: Sun, 8 Nov 2020 05:50:20 +0300 Subject: [PATCH] feat: change object to map events and states Closes #4 --- src/index.ts | 45 ++++++++++++++++++++------------------ tests/statemachine.spec.ts | 21 ++++++++++++++++++ 2 files changed, 45 insertions(+), 21 deletions(-) diff --git a/src/index.ts b/src/index.ts index 1e4c123..3edc8bd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,15 +8,15 @@ export type ActionEvent = (event: string, fromState: StateType, toState: StateTy export interface IConfig { - [key: string]: undefined | ActionEvent | ActionConfigMap - onEnter?: ActionEvent - onLeave?: ActionEvent + [key: string]: undefined | ActionEvent | ActionConfigMap; + onEnter?: ActionEvent; + onLeave?: ActionEvent; } export interface IActionConfig { - state: StateType - onBeforeChange?: ActionEvent - onChange?: ActionEvent + state: StateType; + onBeforeChange?: ActionEvent; + onChange?: ActionEvent; } @@ -26,8 +26,8 @@ export class StateMachine { private _currentState: StateType; private _onEnter?: ActionEvent; private _onLeave?: ActionEvent; - private _eventsByState: Record any>> = {}; - private _statesByState: Record = {}; + private _eventsByState = new Map any>>(); + private _statesByState = new Map(); constructor(initial: StateType, config: IConfig) { this._currentState = initial; @@ -38,7 +38,11 @@ export class StateMachine { continue } - this._statesByState[fromStateKey] = []; + const fromState: StateType = /^\d+$/.test(fromStateKey) ? + parseInt(fromStateKey, 10) + : fromStateKey; + + const statesOfState: StateType[] = []; let actions = config[fromStateKey] as ActionConfigMap; for (let actionName in actions) { @@ -47,9 +51,9 @@ export class StateMachine { action as IActionConfig : { state: action as StateType }; - this._statesByState[fromStateKey].push(actionConfig.state); + statesOfState.push(actionConfig.state); + this._statesByState.set(fromState, statesOfState); - let fromState: StateType = /^\d+$/.test(fromStateKey) ? parseInt(fromStateKey, 10) : fromStateKey; this._initChangeState(actionName, fromState, actionConfig.state, actionConfig); } } @@ -57,10 +61,6 @@ export class StateMachine { private _initChangeState(eventName: string, fromState: StateType, toState: StateType, actionConfig: IActionConfig): void { - if (!this._eventsByState[fromState]) { - this._eventsByState[fromState] = {}; - } - const { onBeforeChange, onChange } = actionConfig; const _runEvent = async (method?: ActionEvent, payload: Payload = {}): Promise => { if (method) { @@ -68,7 +68,8 @@ export class StateMachine { } }; - this._eventsByState[fromState][eventName] = async (sourcePayload: Payload = {}) => { + const events = this._eventsByState.get(fromState) ?? {}; + events[eventName] = async (sourcePayload: Payload = {}) => { const payload = cloneDeep(sourcePayload); await _runEvent(this._onEnter, payload); await _runEvent(onBeforeChange, payload); @@ -79,11 +80,13 @@ export class StateMachine { return this; }; + this._eventsByState.set(fromState, events); + if (!this[eventName]) { this[eventName] = async (payload: Payload = {}) => { - if (this._eventsByState[this._currentState] - && this._eventsByState[this._currentState][eventName]) { - return this._eventsByState[this._currentState][eventName](payload); + const events = this._eventsByState.get(this._currentState); + if (events && events[eventName]) { + return events[eventName](payload); } } } @@ -102,10 +105,10 @@ export class StateMachine { } public getAvailableStates(): StateType[] { - return this._statesByState[this._currentState] || [] + return this._statesByState.get(this._currentState) ?? [] } public getAvailableActions(): string[] { - return Object.keys(this._eventsByState[this._currentState] || {}); + return Object.keys(this._eventsByState.get(this._currentState) ?? {}); } } diff --git a/tests/statemachine.spec.ts b/tests/statemachine.spec.ts index 5cd1dd6..f82802a 100644 --- a/tests/statemachine.spec.ts +++ b/tests/statemachine.spec.ts @@ -129,6 +129,27 @@ describe('StateMachine', () => { objectStrFSM._currentState = StrStatus.PENDING; }); + describe('::new', () => { + it('should init fsm model successfully', () => { + + const simpleIntFSM = new StateMachine(IntStatus.PENDING, { + [IntStatus.PENDING]: { + active: IntStatus.ACTIVE, + delete: IntStatus.DELETED, + }, + [IntStatus.ACTIVE]: { + toDraft: IntStatus.PENDING, + archive: IntStatus.ARCHIVED, + doNothing: IntStatus.ACTIVE, + } + }); + + expect(simpleIntFSM).toBeDefined(); + expect(simpleIntFSM.getCurrentState()).toBe(IntStatus.PENDING); + + }) + }) + describe('.getCurrentState', () => { describe('', () => { it('should return initial int state for simple fsm model', () => {