mooremachine
Advanced tools
Comparing version 2.0.0 to 2.0.1
@@ -1,3 +0,11 @@ | ||
// Copyright 2015 Joyent, Inc. | ||
/* | ||
* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
*/ | ||
/* | ||
* Copyright (c) 2016, Joyent, Inc. | ||
*/ | ||
module.exports = FSM; | ||
@@ -23,2 +31,4 @@ | ||
FSMStateHandle.prototype.validTransitions = function (states) { | ||
if (this.fsh_validTransitions !== undefined) | ||
throw (new Error('FSM validTransitions already set')); | ||
assert.arrayOfString(states, 'states'); | ||
@@ -46,2 +56,8 @@ this.fsh_validTransitions = states; | ||
FSMStateHandle.prototype.reset = function () { | ||
this.fsh_valid = true; | ||
this.fsh_nextState = undefined; | ||
}; | ||
/* Disconnect just this handle, returning our parent handle (if any). */ | ||
FSMStateHandle.prototype.disconnect = function () { | ||
@@ -68,7 +84,21 @@ var ls = this.fsh_listeners; | ||
this.fsh_immediates = []; | ||
if (this.fsh_link !== undefined) | ||
this.fsh_link.disconnect(); | ||
this.fsh_valid = false; | ||
var link = this.fsh_link; | ||
this.fsh_link = undefined; | ||
return (link); | ||
}; | ||
/* Disconnect this handle and all parents. */ | ||
FSMStateHandle.prototype.disconnectAll = function () { | ||
var l = this.disconnect(); | ||
if (l !== undefined) | ||
l.disconnectAll(); | ||
}; | ||
FSMStateHandle.prototype.on = function (obj, evt, cb) { | ||
if (!this.fsh_valid) { | ||
throw (new Error('FSM attempted to set up handler in state ' + | ||
this.fsh_state + ' but already called gotoState() to ' + | ||
'enter state ' + this.fsh_nextState)); | ||
} | ||
obj.on(evt, cb); | ||
@@ -79,2 +109,7 @@ this.fsh_listeners.push([obj, evt, cb]); | ||
FSMStateHandle.prototype.interval = function (interval, cb) { | ||
if (!this.fsh_valid) { | ||
throw (new Error('FSM attempted to set up interval in state ' + | ||
this.fsh_state + ' but already called gotoState() to ' + | ||
'enter state ' + this.fsh_nextState)); | ||
} | ||
var timer = setInterval(cb, interval); | ||
@@ -86,2 +121,7 @@ this.fsh_intervals.push(timer); | ||
FSMStateHandle.prototype.timeout = function (timeout, cb) { | ||
if (!this.fsh_valid) { | ||
throw (new Error('FSM attempted to set up timeout in state ' + | ||
this.fsh_state + ' but already called gotoState() to ' + | ||
'enter state ' + this.fsh_nextState)); | ||
} | ||
var timer = setTimeout(cb, timeout); | ||
@@ -93,2 +133,7 @@ this.fsh_timeouts.push(timer); | ||
FSMStateHandle.prototype.immediate = function (cb) { | ||
if (!this.fsh_valid) { | ||
throw (new Error('FSM attempted to set up immediate in state ' + | ||
this.fsh_state + ' but already called gotoState() to ' + | ||
'enter state ' + this.fsh_nextState)); | ||
} | ||
var timer = setImmediate(cb); | ||
@@ -100,2 +145,7 @@ this.fsh_immediates.push(timer); | ||
FSMStateHandle.prototype.callback = function (cb) { | ||
if (!this.fsh_valid) { | ||
throw (new Error('FSM attempted to set up callback in state ' + | ||
this.fsh_state + ' but already called gotoState() to ' + | ||
'enter state ' + this.fsh_nextState)); | ||
} | ||
var s = this; | ||
@@ -173,10 +223,40 @@ return (function () { | ||
/* | ||
* If we're changing to a state that is not a sub-state of this one, | ||
* then kill of all timers and listeners we created in this state. | ||
* First, kill event handlers and timers from our previous state, as | ||
* needed. | ||
*/ | ||
var parts = (this.fsm_state ? this.fsm_state.split('.') : ['']); | ||
var newParts = state.split('.'); | ||
if (parts[0] !== newParts[0] && this.fsm_handle !== undefined) { | ||
this.fsm_handle.disconnect(); | ||
this.fsm_handle = undefined; | ||
if (newParts.length > 2) | ||
throw (new Error('Invalid FSM destination state: ' + state)); | ||
if (this.fsm_handle !== undefined) { | ||
if (parts[0] === newParts[0] && parts[1] === undefined && | ||
newParts[1] !== undefined) { | ||
/* | ||
* e.g. 'connected' => 'connected.idle'. Don't | ||
* disconnect anything. | ||
*/ | ||
this.fsm_handle.reset(); | ||
} else if (parts[0] === newParts[0] && parts[1] !== undefined && | ||
newParts[1] !== undefined) { | ||
/* | ||
* e.g. 'connected.idle' => 'connected.busy'. Just | ||
* disconnect the things we set up in 'connected.idle' | ||
* while leaving things from 'connected' alone. Also | ||
* reset the parent handle in case it was the cause of | ||
* the transition. | ||
* | ||
* Note we end up here if we're re-entering the same | ||
* exact state, too. | ||
*/ | ||
this.fsm_handle = this.fsm_handle.disconnect(); | ||
if (this.fsm_handle !== undefined) | ||
this.fsm_handle.reset(); | ||
} else { | ||
/* | ||
* e.g. 'connected' => 'closing'. Disconnect all | ||
* handlers (including from any parent states). | ||
*/ | ||
this.fsm_handle.disconnectAll(); | ||
this.fsm_handle = undefined; | ||
} | ||
} | ||
@@ -183,0 +263,0 @@ |
@@ -1,3 +0,11 @@ | ||
// Copyright 2015 Joyent, Inc. | ||
/* | ||
* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
*/ | ||
/* | ||
* Copyright (c) 2016, Joyent, Inc. | ||
*/ | ||
var FSM = require('./fsm'); | ||
@@ -4,0 +12,0 @@ |
{ | ||
"name": "mooremachine", | ||
"version": "2.0.0", | ||
"version": "2.0.1", | ||
"description": "Moore finite state machines", | ||
@@ -20,3 +20,3 @@ "main": "lib/index.js", | ||
], | ||
"license": "MIT", | ||
"license": "MPL-v2.0", | ||
"bugs": { | ||
@@ -23,0 +23,0 @@ "url": "https://github.com/arekinath/node-mooremachine/issues" |
mooremachine | ||
============ | ||
Short version | ||
------------- | ||
This is a framework for organising your async node.js code as Moore | ||
Finite State Machines. FSMs can be easier to reason about and debug than | ||
implicit state kept in callbacks and objects, leading to more correct code. | ||
License and contributing | ||
------------------------ | ||
MPL v2.0 | ||
Contributions should be made through Gerrit -- see | ||
[CONTRIBUTING.md](./CONTRIBUTING.md). | ||
Introduction | ||
@@ -112,3 +127,3 @@ ----------- | ||
ThingFSM.prototype.state_connected = function (on) { | ||
ThingFSM.prototype.state_connected = function (S) { | ||
/* ... */ | ||
@@ -204,2 +219,7 @@ }; | ||
### `FSMStateHandle#callback(cb)` | ||
Wraps an arbitrary callback function in such a way that calling it once the FSM | ||
has left the current state is a no-op. | ||
### `FSMStateHandle#interval(intervalMs, cb)` | ||
@@ -226,1 +246,46 @@ | ||
- `state`: String, state to enter | ||
## Sub-states | ||
It is possible to create a "sub-state" with mooremachine FSMs, which "inherits | ||
from" its parent state. For example: | ||
```js | ||
ThingFSM.prototype.state_connected = function (S) { | ||
S.on(this.tf_sock, 'close', function () { | ||
S.gotoState('closed'); | ||
}); | ||
if (workAvailable) | ||
S.gotoState('connected.busy'); | ||
else | ||
S.gotoState('connected.idle'); | ||
}; | ||
ThingFSM.prototype.state_connected.busy = function (S) { | ||
this.tf_sock.ref(); | ||
/* ... */ | ||
S.on(this.tf_work, 'finished', function () { | ||
S.gotoState('connected'); | ||
}); | ||
}; | ||
ThingFSM.prototype.state_connected.idle = function (S) { | ||
this.tf_sock.unref(); | ||
S.on(this, 'workAvailable', function () { | ||
S.gotoState('connected.busy'); | ||
}); | ||
}; | ||
``` | ||
All event handlers that are set up in the `'connected'` state entry function are | ||
kept when entering `'connected.busy'` or `'connected.idle'`. When changing from | ||
`'connected.busy'` to `'connected.idle'`, the handlers set up in that sub-state | ||
are torn down, but those originating from `'connected'` are kept. | ||
While in a sub-state of `'connected'`, `fsm.isInState('connected')` will | ||
continue to evaluate to `true`. Separate `'stateChanged'` events will be emitted | ||
for each sub-state entered. | ||
Once a handle is used to transition to an unrelated state (e.g. `'closed'` in | ||
the example), all handlers are torn down (from both the parent state and | ||
sub-state) as usual before entering the new state. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Copyleft License
License(Experimental) Copyleft license information was found.
Found 1 instance in 1 package
Non-permissive License
License(Experimental) A license not known to be considered permissive was found.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
20457
7
286
289
0
2
70