Skip to content

Instantly share code, notes, and snippets.

@quidmonkey
Last active June 5, 2017 17:30
Show Gist options
  • Save quidmonkey/395da0b98b2136bdb38d3f769ca44bf8 to your computer and use it in GitHub Desktop.
Save quidmonkey/395da0b98b2136bdb38d3f769ca44bf8 to your computer and use it in GitHub Desktop.
Simple FSM Based on Redux.js Style of State Management - Features Timewarping
import { merge, mergeWith } from 'lodash-es';
const history = []; // history of all states
let timePeriod = 0; // index for history, used to timewarp throughout the history
let state = Object.freeze({}); // current state
/**
* Recursively merges all objects, and overwrites
* all other values.
* @param {*} destObj Current state
* @param {*} srcVal Any value to be merged
* @return {*} Resolved value
*/
export function customizer(destObj, srcVal) {
if (isObj(srcVal)) {
return merge(destObj, srcVal);
} else {
return srcVal;
}
}
/**
* Fast forward history by one iteration.
* @return {Object} History state
*/
export function fastForwardTime() {
timePeriod = Math.min(history.length - 1, ++timePeriod);
return getHistory(timePeriod);
}
/**
* Freeze the new state to prevent mutations
* @param {Object} candidate New State candidate
* @return {Object} New Immutable State
*/
export function freezeState(candidate) {
return Object.freeze(candidate);
}
/**
* Get the history for a specific time period
* @param {Number} period History index
* @return {*} History state
*/
export function getHistory(period) {
return history[period];
}
/**
* Get current state
* @return {Object} Current state
*/
export function getState() {
return state;
}
/**
* Is a value an object?
* @param {*} val Any JavaScript value
* @return {Boolean} True, if value is an object; false, otherwise.
*/
export function isObj(val) {
return typeof val === 'object' && !(val instanceof Array);
}
/**
* Timewarp function that sets the current time period
* to the present or most recent state
* @return {Object} Latest history state
*/
export function presentTime() {
timePeriod = history.length - 1;
return getHistory(timePeriod);
}
/**
* Rewind history by one iteration
* @return {Object} History state
*/
export function rewindTime() {
timePeriod = Math.max(0, --timePeriod);
return getHistory(timePeriod);
}
/**
* Set a new state
* @param {Object} newState New state to be merged
* @return {Object} New state candidate that has been merged with previous state
*/
export function setState(newState) {
const candidate = mergeWith(state, newState, customizer);
state = freezeState(candidate);
history.push(state);
timePeriod = history.length - 1;
return state;
}
export default {
getState,
fastForwardTime,
presentTime,
rewindTime,
setState
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment