Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Updating pages for v0.2.0

  • Loading branch information...
commit aa4272f2d6912ff8f4dd5bd5dcec11f0c460a1f4 2 parents 09a410e + 3ac9292
@ifandelse authored
Showing with 19 additions and 31 deletions.
  1. +18 −30 index.html
  2. +1 −1  params.json
View
48 index.html
@@ -49,8 +49,14 @@
</ul>
</li>
<li>It's simple! Machina makes the process of organizing the various states your fsm needs to know about, and the kinds of events each state can handle.</li>
-<li>Powerful integration. Out of the box, machina will auto-wire itself into postal.js (client side message bus) or amplify.js (specifically amplify.core, which houses a lightweight message bus), if one of them is present. These allows your FSM to respond to messages, instead of state handlers being invoked directly, and it also allows your fsm to publish events over pub/sub.</li>
-<li>Extend for more power. You can support other pub/sub libraries by writing a provider for it (an object that defines a "wireUp" and a "addEventTransforms" call. "wireUp" is where you tell your FSM how to talk to your pub/sub library. "addEventTransforms" helps machina translate event message arguments into a more expressive message payload.</li>
+<li>Powerful integration. By using a plugin like <a href="https://github.com/ifandelse/machina.postal">machina.postal</a>, your FSM instances can auto-wire into <a href="https://github.com/ifandelse/postal.js">postal.js</a> (a JavaScript message bus), enabling decoupled communications with other components in your application. This wires up both subscribers (for state handlers to be invoked) and publishers (to publish your FSM's events to the message bus).</li>
+<li>Extend for more power.
+
+<ul>
+<li>Writing your own message bus/eventing wire-up plugin is fairly simple. Look at <a href="https://github.com/ifandelse/machina.postal">machina.postal</a> for an example.</li>
+<li>Hook into the top level "newFsm" event to give other components in your app a handle to your FSM</li>
+</ul>
+</li>
</ul><h2>How do I use it?</h2>
<p>(Be sure to check out the example folder in this repository for more in-depth demos).</p>
@@ -78,8 +84,6 @@
<span class="k">return</span> <span class="kc">true</span><span class="p">;</span>
<span class="p">},</span>
- <span class="nx">eventListeners</span><span class="o">:</span> <span class="p">[</span> <span class="s2">"CustomerSyncComplete"</span> <span class="p">],</span>
-
<span class="nx">initialState</span><span class="o">:</span> <span class="s2">"offline"</span><span class="p">,</span>
<span class="nx">states</span> <span class="o">:</span> <span class="p">{</span>
@@ -120,7 +124,7 @@
<p>In the above example, the developer has created an FSM with two possible states: <code>online</code> and <code>offline</code>. While the fsm is in the <code>online</code> state, it will respond to <code>save.customer</code> and <code>sync.customer</code> events. External code triggers these events by calling the <code>handle</code> method on the FSM. For example <code>storageFsm.handle( "sync.customer", { other: "data" } )</code>. The <code>handle</code> method first looks to see if a named handler exists matching the name of the one passed in, then also checks for a catch-all handler (indicated by the "*") if a named handler isn't found. The <code>offline</code> state of the above FSM only responds to <code>save.customer</code> events. If any other type of event name is passed to the <code>handle</code> method of the FSM, other than what each state explicitly handles, it is ignored.</p>
-<p>In addition to the state/handler definitions, the above code example as shows that the FSM will start in the <code>offline</code> state, and can generate a <code>CustomerSyncComplete</code> event. It's worth noting that the <code>events</code> member can be an array of string event names, or an object where each handler name is the key, and the values are either empty arrays, or an array of callbacks. (The array of string event names is converted into an object with the event name as the key, empty array as the value.)</p>
+<p>In addition to the state/handler definitions, the above code example as shows that the FSM will start in the <code>offline</code> state, and can generate a <code>CustomerSyncComplete</code> event.</p>
<p>The <code>verifyState</code> and <code>applicationOffline</code> methods are custom to this instance of the FSM, and are not, of course, part of machina by default.</p>
@@ -171,18 +175,7 @@
<p><code>initialState</code> - the state in which the FSM will start. As soon as the instance is created, the FSM calls the <code>transition</code> method to transition into this state.</p>
-<p><code>messaging</code> - an object used in wiring machina into a message bus</p>
-
-<div class="highlight">
-<pre><span class="nx">messaging</span><span class="o">:</span> <span class="p">{</span>
- <span class="nx">provider</span> <span class="o">:</span> <span class="s2">"postal"</span><span class="p">,</span> <span class="c1">// the name of the provider in machina.busProviders to use for message bus integration</span>
- <span class="nx">eventNamespace</span><span class="o">:</span> <span class="s2">"myFsm.events"</span><span class="p">,</span> <span class="c1">// the "channel" or "exchange" name for published events sent from this FSM</span>
- <span class="nx">handlerNamespace</span><span class="o">:</span> <span class="s2">"myFsm"</span><span class="p">,</span> <span class="c1">// the "channel" or "exchange" for messages sent to this FSM (messages intended to invoke a handler)</span>
- <span class="nx">subscriptions</span><span class="o">:</span> <span class="p">[],</span> <span class="c1">// a list of message bus subscription objects/callbacks for this FSM</span>
-<span class="p">}</span>
-</pre>
-</div>
-
+<p><code>namespace</code> - a name that indentifies the FSM if it's wired up to a message bus through a plugin.</p>
<h2>The machina.Fsm Prototype</h2>
@@ -213,22 +206,13 @@
<li>
<code>Fsm</code> - the constructor function used to create FSMs.</li>
<li>
-<code>busProviders</code> - an object containing providers for various message-bus frameworks, allowing machina to tie into them (postal.js and amplify are available out of the box).</li>
-<li>
-<code>utils</code> - contains various helper functions that can be overridden to drastically change default behavior(s) in machina:
+<code>utils</code> - contains helper functions that can be overridden to change default behavior(s) in machina:
<ul>
<li>
<code>getDefaultOptions</code> - returns the default options object for any machina instance</li>
<li>
-<code>findProvider</code> - function that (by default) checks for postal and then amplify - if one is found, the FSM gets wired into the appropriate message bus.</li>
-<li>
<code>makeFsmNamespace</code> - function that provides a default "channel" or "exchange" for an FSM instance. (e.g. - fsm.0, fsm.1, etc.)</li>
-<li>
-<code>getHandlerNames</code> - function that provides a flattened/distinct list of every handler name, under any state, an an FSM instance.</li>
-<li>
-<code>standardEventTransforms</code> - an object that provides default implementations for transforming event arguments into a meaningful message payload when an FSM instance has been tied into a message bus. Effectively, they provide the difference between a payload that looks like this: <code>"data":{"0":{"_currentAction":"","_priorAction":"unauthorized.*"},"1":"unauthorized","2":"unauthorized"}</code> vs this: <code>"data":{"info":{"_currentAction":"","_priorAction":"unauthorized.*"},"oldState":"unauthorized","newState":"unauthorized"}</code>
-</li>
</ul>
</li>
<li>
@@ -237,11 +221,15 @@
<code>off</code> - function used to unsubscribe a callback to top-level machina events.</li>
<li>
<code>eventListeners</code> - an object literal containing the top-level <code>fireEvent</code> call as well as susbcribers to any top-level events.</li>
-</ul><h2>Roadmap</h2>
+</ul><h2>Release Notes</h2>
+
+<h3>v0.2.0</h3>
<ul>
-<li>machina.js version 0.2.0 is nearly done. I plan to release it at the same time as postal.js v0.6.0</li>
-<li>v0.2.0 of machina will separate the message-bus components into plugins, to keep those concerns out of the core source</li>
+<li>Message bus integration has been removed from machina core, and now exists as plugins. For integration with <a href="https://github.com/ifandelse/postal.js">postal.js</a>, see <a href="https://github.com/ifandelse/machina.postal">machina.postal</a>
+</li>
+<li>Due to the above change, the only "messaging-related" metadata on an FSM now is the "namespace" value that can be passed as the FSM is created. This value is optional, and will be given a default if none is provided. Messaging plugins can utilize this value as a channel/namespace name and/or topic prefix.</li>
+<li>A "priorState" member has been added to the Fsm.</li>
</ul>
</section>
<footer>
View
2  params.json
@@ -1 +1 @@
-{"name":"Machina.js","body":"# machina.js\r\n\r\n## What is it?\r\nMachina.js is a JavaScript framework for highly customizable finite state machines (FSMs). Many of the ideas for machina have been very heavily borrowed from the Erlang/OTP FSM behaviors.\r\n\r\n## Why would I use it?\r\n* Finite state machines offer a way to structure web client code in a very organized manner, and can make it much simpler to extend behavior for all or only key edge cases.\r\n\t* For example - instead of nested callbacks/deferreds, use an FSM to act as an \"event aggregator\" that is aware of when state needs to transition in the app once a set of conditions has been satisfied.\r\n\t* FSMs *can* work well for concerns like:\r\n\t\t* app \"init\" (bootstrapping your web client so that certain application behaviors are not available until all appropriate resources/data/behavior are present)\r\n\t\t* persistence concerns - offline vs online. Abstract persistence behind an fsm that simply listens for messages (commands) to persist data. Depending on the state of the client (offline vs online), the FSM will handle the activity accordingly - calling code never needs to know.\r\n\t\t* Often-changing-subsets of view or model elements. Take a navigation menu, for example. Depending on the context (i.e. - state), you may wish to show/hide certain menu options. This usually turns out to be a handful of menu show-vs-hide combinations. An FSM can abstract this well.\r\n* It's simple! Machina makes the process of organizing the various states your fsm needs to know about, and the kinds of events each state can handle.\r\n* Powerful integration. Out of the box, machina will auto-wire itself into postal.js (client side message bus) or amplify.js (specifically amplify.core, which houses a lightweight message bus), if one of them is present. These allows your FSM to respond to messages, instead of state handlers being invoked directly, and it also allows your fsm to publish events over pub/sub.\r\n* Extend for more power. You can support other pub/sub libraries by writing a provider for it (an object that defines a \"wireUp\" and a \"addEventTransforms\" call. \"wireUp\" is where you tell your FSM how to talk to your pub/sub library. \"addEventTransforms\" helps machina translate event message arguments into a more expressive message payload.\r\n\r\n## How do I use it?\r\n(Be sure to check out the example folder in this repository for more in-depth demos).\r\n\r\nCreating an FSM:\r\n\r\n```javascript\r\nvar storageFsm = new machina.Fsm({\r\n\tapplicationOffline: function() {\r\n\t\tvar offline = false;\r\n\t\t// checks window.navigator.online and more, sets the offline value\r\n\t\treturn offline;\r\n\t},\r\n\r\n\tverifyState: function( payload ) {\r\n\t\tif( applicationOffline() && this.state !== \"offline\" ) {\r\n\t\t\tthis.offlineMarkerTime = new Date();\r\n\t\t\tthis.transition(\"offline\");\r\n\t\t\treturn false;\r\n\t\t}\r\n\t\telse if ( !applicationOffline() && this.state === \"offline\" ) {\r\n\t\t\tthis.transition( \"online\" );\r\n\t\t\treturn false;\r\n\t\t}\r\n\t\treturn true;\r\n\t},\r\n\r\n\teventListeners: [ \"CustomerSyncComplete\" ],\r\n\r\n\tinitialState: \"offline\",\r\n\r\n\tstates : {\r\n\t\t\"online\" : {\r\n\t\t\t_onEnter: function() {\r\n\t\t\t\tthis.handle(\"sync.customer\");\r\n\t\t\t},\r\n\r\n\t\t\t\"save.customer\" : function( payload ) {\r\n\t\t\t\tif( verifyState( payload ) ) {\r\n storage.saveToRemote( payload );\r\n\t\t\t\t}\r\n\t\t\t},\r\n\r\n\t\t\t\"sync.customer\" : function() {\r\n\t\t\t\tif( verifyState( payload ) ) {\r\n\t\t\t\t\tvar unsynced = storage.getFromLocal( { startTime: this.offlineMarkerTime } );\r\n\t\t\t\t\t// Big assumption here! In the real world,\r\n\t\t\t\t\t// we'd batch this sync in reasonable chunks.\r\n\t\t\t\t\tstorage.saveBatchToRemote( unsynced );\r\n\t\t\t\t\tthis.fireEvent( \"CustomerSyncComplete\", { customers: unsynced } );\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t},\r\n\r\n\t\t\"offline\" : {\r\n\t\t\t\"save.customer\" : function( payload ) {\r\n\t\t\t\tif( verifyState( payload ) ) {\r\n storage.saveToLocal( payload );\r\n\t\t\t\t}\r\n }\r\n\t\t}\r\n\t}\r\n});\r\n```\r\n\r\nIn the above example, the developer has created an FSM with two possible states: `online` and `offline`. While the fsm is in the `online` state, it will respond to `save.customer` and `sync.customer` events. External code triggers these events by calling the `handle` method on the FSM. For example `storageFsm.handle( \"sync.customer\", { other: \"data\" } )`. The `handle` method first looks to see if a named handler exists matching the name of the one passed in, then also checks for a catch-all handler (indicated by the \"*\") if a named handler isn't found. The `offline` state of the above FSM only responds to `save.customer` events. If any other type of event name is passed to the `handle` method of the FSM, other than what each state explicitly handles, it is ignored.\r\n\r\nIn addition to the state/handler definitions, the above code example as shows that the FSM will start in the `offline` state, and can generate a `CustomerSyncComplete` event. It's worth noting that the `events` member can be an array of string event names, or an object where each handler name is the key, and the values are either empty arrays, or an array of callbacks. (The array of string event names is converted into an object with the event name as the key, empty array as the value.)\r\n\r\nThe `verifyState` and `applicationOffline` methods are custom to this instance of the FSM, and are not, of course, part of machina by default.\r\n\r\nYou can see in the above example that anytime the FSM handles an event, it first checks to see if the state needs to be transitioned between offline and online (via the `verifyState` call). States can also have an `_onEnter` method - which is fired immediately after the FSM transitions into that state.\r\n\r\nNow that we've seen a quick example, let's do a whirlwind API tour.\r\n\r\n## Whirlwind API Tour\r\nWhen you are creating a new FSM instance, `machina.Fsm` takes 1 argument - an options object. Here's a breakdown of the members of this `options` object:\r\n\r\n`eventListeners` - Either a list of event names that the FSM can publish, or an object of event names, associated with the array of event handlers subscribed to them. (You are not required to declare the events your FSM can publish ahead of time - this is only for convenience if you want to add handlers ahead of time.)\r\n\r\n```javascript\r\neventListeners: [\"String\", \"List\", \"ofEvent\", \"names\"]; // this is converted into an object similar to below\r\n// OR\r\neventListeners: {\r\n\tMyEvent1: [],\r\n\tMyEvent2: [function(data) { console.log(data); }]\r\n}\r\n```\r\n\r\n`states` - an object detailing the possible states the FSM can be in, plus the kinds of events/messages each state can handle. States can have normal \"handlers\" as well as a catch-all handler (\"*\"), and an _onEnter handler invoked when the FSM has transitioned into that state.\r\n\r\n```javascript\r\nstates: {\r\n\t\t\"uninitialized\" : {\r\n\t\t\t_onEnter: function() {\r\n\t\t\t\t// do stuff immediately after we transition into uninitialized\r\n\t\t\t},\r\n\r\n\t\t\t\"initialize\" : function( payload ) {\r\n\t\t\t\t// handle an \"initialize\" event\r\n\t\t\t}\r\n\t\t},\r\n\r\n\t\t\"ready\" : {\r\n\t\t\t\"*\" : function( payload ) {\r\n\t\t\t\t// any message that comes while in the \"ready\" state will get handled here\r\n\t\t\t\t// unless it matches another \"ready\" handler exactly.\r\n }\r\n\t\t}\r\n```\r\n\r\n`initialState` - the state in which the FSM will start. As soon as the instance is created, the FSM calls the `transition` method to transition into this state.\r\n\r\n`messaging` - an object used in wiring machina into a message bus\r\n\r\n```javascript\r\nmessaging: {\r\n\tprovider : \"postal\", // the name of the provider in machina.busProviders to use for message bus integration\r\n eventNamespace: \"myFsm.events\", // the \"channel\" or \"exchange\" name for published events sent from this FSM\r\n handlerNamespace: \"myFsm\", // the \"channel\" or \"exchange\" for messages sent to this FSM (messages intended to invoke a handler)\r\n subscriptions: [], // a list of message bus subscription objects/callbacks for this FSM\r\n}\r\n```\r\n\r\n## The machina.Fsm Prototype\r\nEach instance of an machina FSM has the following methods available via it's prototype:\r\n\r\n* `fireEvent(eventName, [other args...])` - looks in the `events` object for a matching event name, and then iterates through the subscriber callbacks for that event and invokes each one, passing in any additional args that were passed to `fireEvent`.\r\n* `handle(msgType, [other args...])` - This is the main way you should be interacting with an FSM instance (assuming no message bus is present). It will try to find a matching eventName/msgType under the current state and invoke it, if one exists. Otherwise it will look for a catch-all handler, or simply ignore the message and raise the \"NoHandler\" event.\r\n* `transition(newState)` - Called when transitioning into a new state.\r\n* `deferUntilTransition(stateName)` - calling this within a state handler function will queue the handler's arguments to be executed at a later time. If you don't provide the `stateName` argument, it will replay the event after the next state transition. Providing the `stateName` argument will queue the event until the FSM transitions into that state.\r\n* `deferUntilNextHandler()` - calling this within a state handler function will queue the handler's arguments to be executed after the next handler is invoked.\r\n* `processQueue()` - called internally during state transitions and after handler methods have been invoked. This call processes any queued events (queued by use of `deferUntilTransition` and/or `deferUntilNextHandler`).\r\n* `on(eventName, callback)` - used to subscribe to events that the FSM generates.\r\n* `off(eventName, callback)` - used to unsubscribe to FSM events.\r\n\r\n## The Top Level machina object\r\nThe top level `machina` object has the following members:\r\n\r\n* `Fsm` - the constructor function used to create FSMs.\r\n* `busProviders` - an object containing providers for various message-bus frameworks, allowing machina to tie into them (postal.js and amplify are available out of the box).\r\n* `utils` - contains various helper functions that can be overridden to drastically change default behavior(s) in machina:\r\n\t* `getDefaultOptions` - returns the default options object for any machina instance\r\n\t* `findProvider` - function that (by default) checks for postal and then amplify - if one is found, the FSM gets wired into the appropriate message bus.\r\n\t* `makeFsmNamespace` - function that provides a default \"channel\" or \"exchange\" for an FSM instance. (e.g. - fsm.0, fsm.1, etc.)\r\n\t* `getHandlerNames` - function that provides a flattened/distinct list of every handler name, under any state, an an FSM instance.\r\n\t* `standardEventTransforms` - an object that provides default implementations for transforming event arguments into a meaningful message payload when an FSM instance has been tied into a message bus. Effectively, they provide the difference between a payload that looks like this: `\"data\":{\"0\":{\"_currentAction\":\"\",\"_priorAction\":\"unauthorized.*\"},\"1\":\"unauthorized\",\"2\":\"unauthorized\"}` vs this: `\"data\":{\"info\":{\"_currentAction\":\"\",\"_priorAction\":\"unauthorized.*\"},\"oldState\":\"unauthorized\",\"newState\":\"unauthorized\"}`\r\n* `on` - function used to subscribe a callback to top-level machina events (currently the only event published at this level is \"newFsm\")\r\n* `off` - function used to unsubscribe a callback to top-level machina events.\r\n* `eventListeners` - an object literal containing the top-level `fireEvent` call as well as susbcribers to any top-level events.\r\n\r\n## Roadmap\r\n\r\n* machina.js version 0.2.0 is nearly done. I plan to release it at the same time as postal.js v0.6.0\r\n* v0.2.0 of machina will separate the message-bus components into plugins, to keep those concerns out of the core source","tagline":"js ex machina - finite state machines in JavaScript","google":"UA-30321396-1","note":"Don't delete this file! It's used internally to help with page regeneration."}
+{"name":"Machina.js","body":"# machina.js\r\n\r\n## What is it?\r\nMachina.js is a JavaScript framework for highly customizable finite state machines (FSMs). Many of the ideas for machina have been very heavily borrowed from the Erlang/OTP FSM behaviors.\r\n\r\n## Why would I use it?\r\n* Finite state machines offer a way to structure web client code in a very organized manner, and can make it much simpler to extend behavior for all or only key edge cases.\r\n\t* For example - instead of nested callbacks/deferreds, use an FSM to act as an \"event aggregator\" that is aware of when state needs to transition in the app once a set of conditions has been satisfied.\r\n\t* FSMs *can* work well for concerns like:\r\n\t\t* app \"init\" (bootstrapping your web client so that certain application behaviors are not available until all appropriate resources/data/behavior are present)\r\n\t\t* persistence concerns - offline vs online. Abstract persistence behind an fsm that simply listens for messages (commands) to persist data. Depending on the state of the client (offline vs online), the FSM will handle the activity accordingly - calling code never needs to know.\r\n\t\t* Often-changing-subsets of view or model elements. Take a navigation menu, for example. Depending on the context (i.e. - state), you may wish to show/hide certain menu options. This usually turns out to be a handful of menu show-vs-hide combinations. An FSM can abstract this well.\r\n* It's simple! Machina makes the process of organizing the various states your fsm needs to know about, and the kinds of events each state can handle.\r\n* Powerful integration. By using a plugin like [machina.postal](https://github.com/ifandelse/machina.postal), your FSM instances can auto-wire into [postal.js](https://github.com/ifandelse/postal.js) (a JavaScript message bus), enabling decoupled communications with other components in your application. This wires up both subscribers (for state handlers to be invoked) and publishers (to publish your FSM's events to the message bus).\r\n* Extend for more power.\r\n\t* Writing your own message bus/eventing wire-up plugin is fairly simple. Look at [machina.postal](https://github.com/ifandelse/machina.postal) for an example.\r\n\t* Hook into the top level \"newFsm\" event to give other components in your app a handle to your FSM\r\n\r\n## How do I use it?\r\n(Be sure to check out the example folder in this repository for more in-depth demos).\r\n\r\nCreating an FSM:\r\n\r\n```javascript\r\nvar storageFsm = new machina.Fsm({\r\n\tapplicationOffline: function() {\r\n\t\tvar offline = false;\r\n\t\t// checks window.navigator.online and more, sets the offline value\r\n\t\treturn offline;\r\n\t},\r\n\r\n\tverifyState: function( payload ) {\r\n\t\tif( applicationOffline() && this.state !== \"offline\" ) {\r\n\t\t\tthis.offlineMarkerTime = new Date();\r\n\t\t\tthis.transition(\"offline\");\r\n\t\t\treturn false;\r\n\t\t}\r\n\t\telse if ( !applicationOffline() && this.state === \"offline\" ) {\r\n\t\t\tthis.transition( \"online\" );\r\n\t\t\treturn false;\r\n\t\t}\r\n\t\treturn true;\r\n\t},\r\n\r\n\tinitialState: \"offline\",\r\n\r\n\tstates : {\r\n\t\t\"online\" : {\r\n\t\t\t_onEnter: function() {\r\n\t\t\t\tthis.handle(\"sync.customer\");\r\n\t\t\t},\r\n\r\n\t\t\t\"save.customer\" : function( payload ) {\r\n\t\t\t\tif( verifyState( payload ) ) {\r\n storage.saveToRemote( payload );\r\n\t\t\t\t}\r\n\t\t\t},\r\n\r\n\t\t\t\"sync.customer\" : function() {\r\n\t\t\t\tif( verifyState( payload ) ) {\r\n\t\t\t\t\tvar unsynced = storage.getFromLocal( { startTime: this.offlineMarkerTime } );\r\n\t\t\t\t\t// Big assumption here! In the real world,\r\n\t\t\t\t\t// we'd batch this sync in reasonable chunks.\r\n\t\t\t\t\tstorage.saveBatchToRemote( unsynced );\r\n\t\t\t\t\tthis.fireEvent( \"CustomerSyncComplete\", { customers: unsynced } );\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t},\r\n\r\n\t\t\"offline\" : {\r\n\t\t\t\"save.customer\" : function( payload ) {\r\n\t\t\t\tif( verifyState( payload ) ) {\r\n storage.saveToLocal( payload );\r\n\t\t\t\t}\r\n }\r\n\t\t}\r\n\t}\r\n});\r\n```\r\n\r\nIn the above example, the developer has created an FSM with two possible states: `online` and `offline`. While the fsm is in the `online` state, it will respond to `save.customer` and `sync.customer` events. External code triggers these events by calling the `handle` method on the FSM. For example `storageFsm.handle( \"sync.customer\", { other: \"data\" } )`. The `handle` method first looks to see if a named handler exists matching the name of the one passed in, then also checks for a catch-all handler (indicated by the \"*\") if a named handler isn't found. The `offline` state of the above FSM only responds to `save.customer` events. If any other type of event name is passed to the `handle` method of the FSM, other than what each state explicitly handles, it is ignored.\r\n\r\nIn addition to the state/handler definitions, the above code example as shows that the FSM will start in the `offline` state, and can generate a `CustomerSyncComplete` event.\r\n\r\nThe `verifyState` and `applicationOffline` methods are custom to this instance of the FSM, and are not, of course, part of machina by default.\r\n\r\nYou can see in the above example that anytime the FSM handles an event, it first checks to see if the state needs to be transitioned between offline and online (via the `verifyState` call). States can also have an `_onEnter` method - which is fired immediately after the FSM transitions into that state.\r\n\r\nNow that we've seen a quick example, let's do a whirlwind API tour.\r\n\r\n## Whirlwind API Tour\r\nWhen you are creating a new FSM instance, `machina.Fsm` takes 1 argument - an options object. Here's a breakdown of the members of this `options` object:\r\n\r\n`eventListeners` - Either a list of event names that the FSM can publish, or an object of event names, associated with the array of event handlers subscribed to them. (You are not required to declare the events your FSM can publish ahead of time - this is only for convenience if you want to add handlers ahead of time.)\r\n\r\n```javascript\r\neventListeners: [\"String\", \"List\", \"ofEvent\", \"names\"]; // this is converted into an object similar to below\r\n// OR\r\neventListeners: {\r\n\tMyEvent1: [],\r\n\tMyEvent2: [function(data) { console.log(data); }]\r\n}\r\n```\r\n\r\n`states` - an object detailing the possible states the FSM can be in, plus the kinds of events/messages each state can handle. States can have normal \"handlers\" as well as a catch-all handler (\"*\"), and an _onEnter handler invoked when the FSM has transitioned into that state.\r\n\r\n```javascript\r\nstates: {\r\n\t\t\"uninitialized\" : {\r\n\t\t\t_onEnter: function() {\r\n\t\t\t\t// do stuff immediately after we transition into uninitialized\r\n\t\t\t},\r\n\r\n\t\t\t\"initialize\" : function( payload ) {\r\n\t\t\t\t// handle an \"initialize\" event\r\n\t\t\t}\r\n\t\t},\r\n\r\n\t\t\"ready\" : {\r\n\t\t\t\"*\" : function( payload ) {\r\n\t\t\t\t// any message that comes while in the \"ready\" state will get handled here\r\n\t\t\t\t// unless it matches another \"ready\" handler exactly.\r\n }\r\n\t\t}\r\n```\r\n\r\n`initialState` - the state in which the FSM will start. As soon as the instance is created, the FSM calls the `transition` method to transition into this state.\r\n\r\n`namespace` - a name that indentifies the FSM if it's wired up to a message bus through a plugin.\r\n\r\n## The machina.Fsm Prototype\r\nEach instance of an machina FSM has the following methods available via it's prototype:\r\n\r\n* `fireEvent(eventName, [other args...])` - looks in the `events` object for a matching event name, and then iterates through the subscriber callbacks for that event and invokes each one, passing in any additional args that were passed to `fireEvent`.\r\n* `handle(msgType, [other args...])` - This is the main way you should be interacting with an FSM instance (assuming no message bus is present). It will try to find a matching eventName/msgType under the current state and invoke it, if one exists. Otherwise it will look for a catch-all handler, or simply ignore the message and raise the \"NoHandler\" event.\r\n* `transition(newState)` - Called when transitioning into a new state.\r\n* `deferUntilTransition(stateName)` - calling this within a state handler function will queue the handler's arguments to be executed at a later time. If you don't provide the `stateName` argument, it will replay the event after the next state transition. Providing the `stateName` argument will queue the event until the FSM transitions into that state.\r\n* `deferUntilNextHandler()` - calling this within a state handler function will queue the handler's arguments to be executed after the next handler is invoked.\r\n* `processQueue()` - called internally during state transitions and after handler methods have been invoked. This call processes any queued events (queued by use of `deferUntilTransition` and/or `deferUntilNextHandler`).\r\n* `on(eventName, callback)` - used to subscribe to events that the FSM generates.\r\n* `off(eventName, callback)` - used to unsubscribe to FSM events.\r\n\r\n## The Top Level machina object\r\nThe top level `machina` object has the following members:\r\n\r\n* `Fsm` - the constructor function used to create FSMs.\r\n* `utils` - contains helper functions that can be overridden to change default behavior(s) in machina:\r\n\t* `getDefaultOptions` - returns the default options object for any machina instance\r\n\t* `makeFsmNamespace` - function that provides a default \"channel\" or \"exchange\" for an FSM instance. (e.g. - fsm.0, fsm.1, etc.)\r\n* `on` - function used to subscribe a callback to top-level machina events (currently the only event published at this level is \"newFsm\")\r\n* `off` - function used to unsubscribe a callback to top-level machina events.\r\n* `eventListeners` - an object literal containing the top-level `fireEvent` call as well as susbcribers to any top-level events.\r\n\r\n## Release Notes\r\n\r\n### v0.2.0\r\n\r\n* Message bus integration has been removed from machina core, and now exists as plugins. For integration with [postal.js](https://github.com/ifandelse/postal.js), see [machina.postal](https://github.com/ifandelse/machina.postal)\r\n* Due to the above change, the only \"messaging-related\" metadata on an FSM now is the \"namespace\" value that can be passed as the FSM is created. This value is optional, and will be given a default if none is provided. Messaging plugins can utilize this value as a channel/namespace name and/or topic prefix.\r\n* A \"priorState\" member has been added to the Fsm.","tagline":"js ex machina - finite state machines in JavaScript","google":"UA-30321396-1","note":"Don't delete this file! It's used internally to help with page regeneration."}
Please sign in to comment.
Something went wrong with that request. Please try again.