Created
October 16, 2012 02:54
-
-
Save ovaillancourt/3897003 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Very simple Mealy style finite state machine relying on a nodejs event emitter. Absolutely not tested | |
* and contains absolutely no fancy feature. You can put multiple "from" and "to" states by separating | |
* them with a space. Putting no "from" or "to" amounts to setting them as "undefined", a supported state. | |
*/ | |
// Example: | |
/* | |
var fsm = new Fsm([ | |
{ name : 'wakingup' , from: 'sleeping' , to : 'awake' }, | |
{ name : 'goingToSleep' , from: 'awake' , to : 'sleeping' }, | |
{ name : 'transition' , from: '*' , to : '*' }, | |
{ name : 'birth' , to : '*' }, | |
{ name : 'death' , from: '*' } | |
]); | |
fsm.on( 'wakingup', function(){ console.log( 'waking up' ); } ); | |
fsm.on( 'goingToSleep', function(){ console.log( 'going to sleep' ); } ); | |
fsm.on( 'transition', function(){ console.log( 'transition' ); } ); | |
fsm.on( 'birth', function(){ console.log( 'birth' ); } ); | |
fsm.on( 'death', function(){ console.log( 'death' ); } ); | |
fsm.start( 'awake' ); | |
console.log( '----' ); | |
fsm.transitionTo( 'sleeping' ); | |
console.log( '----' ); | |
fsm.transitionTo( 'awake' ); | |
console.log( '----' ); | |
fsm.end(); | |
*/ | |
// Constructor | |
function Fsm( transitionTable ){ | |
require( 'events' ).EventEmitter.call( this ); | |
if( !Array.isArray( transitionTable ) ){ | |
throw new Error( 'the transitionTable argument must be an array in the fsm constructor' ); | |
} | |
this.tt = transitionTable; | |
this.current = undefined; | |
this.context = {}; | |
this._initTable(); | |
} | |
// Inheritance | |
Fsm.prototype = Object.create( require( 'events').EventEmitter.prototype ); | |
// Methods | |
function convert( obj ){ | |
return typeof obj === 'string' && obj !== '*' | |
? obj.split( ' ' ) : obj; | |
} | |
Fsm.prototype._initTable = function(){ | |
for( var i = 0; i < this.tt.length; ++i ){ | |
this.tt[ i ].from = convert( this.tt[ i ].from ); | |
this.tt[ i ].to = convert( this.tt[ i ].to ); | |
} | |
} | |
Fsm.prototype.start = function( stateName ){ | |
this.transitionTo( stateName ); | |
} | |
Fsm.prototype.end = function(){ | |
this.transitionTo( undefined ); | |
} | |
function isMatch( ref, target ){ | |
return ( Array.isArray( target ) && ~target.indexOf( ref ) ) || | |
( typeof ref === 'undefined' && typeof target === 'undefined' ) || | |
( typeof target === 'string' && target === '*' ); | |
} | |
Fsm.prototype.transitionTo = function( stateName ){ | |
var transitioned = false; | |
for( var i = 0; i < this.tt.length; ++i ){ | |
var cur = this.tt[ i ]; | |
var match = isMatch( this.current, cur.from ) && | |
isMatch( stateName, cur.to ); | |
if( match && cur.name ){ | |
this.emit( cur.name, this.context ); | |
transitioned = true; | |
} | |
} | |
if( transitioned ){ | |
this.current = stateName; | |
} | |
else{ | |
this.emit( '-invalid-', this.current, stateName ); | |
} | |
} | |
module.exports = Fsm; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
state machine would make a nice component :D :D :D