Source code:

StateMachinesPoC on Google Project Hosting

Sample:

  1. //<!–
  2.  
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Linq.Expressions;
  7. using System.Text;
  8.  
  9. using Machines;
  10.  
  11. namespace ThatsLifeSample
  12. {
  13.         // First we define the meaningful set of values for life status:
  14.         public enum LifeStatus { Unborn, Child, Unemployed, Employed, Retired, Dead }
  15.  
  16.         // Next we define a metadata attribute to decorate a descendant of State<…>
  17.         // with the only allowed state transitions;
  18.         // note the expected read-write properties MUST BE named "From", "When", "Goto", and "With"
  19.         // "From" denotes the state we’re transitioning AWAY FROM
  20.         // "Goto" denotes the state we’re transitioning TO
  21.         // "When" denotes the trigger/signal value of the transition
  22.         // "With" is the name of the method (possibly fully qualified type + member name)
  23.         // executed during state transitions as they occur
  24.         // On the types:
  25.         // "From" is of the type of state values’ type (Status in this example)
  26.         // "When" is of the type of trigger/signal, can be a custom enum as well, but here we default to string
  27.         // "Goto" is of the same type as "From"
  28.         // "With" is string
  29.         public class LifeTransitionAttribute : TransitionAttribute
  30.         {
  31.                 public LifeStatus From { get; set; }
  32.                 public string When { get; set; }
  33.                 public LifeStatus Goto { get; set; }
  34.                 public string With { get; set; }
  35.         }
  36.  
  37.         // Next we define the state type proper:
  38.         [LifeTransition(From = LifeStatus.Unborn,               When = "Birth",                 Goto = LifeStatus.Child,                With = "StatusChange")]
  39.         [LifeTransition(From = LifeStatus.Unborn,               When = "Death",                 Goto = LifeStatus.Dead,                 With = "StatusChange")]
  40.         [LifeTransition(From = LifeStatus.Child,                When = "Death",                 Goto = LifeStatus.Dead,                 With = "StatusChange")]
  41.         [LifeTransition(From = LifeStatus.Child,                When = "Graduation",    Goto = LifeStatus.Unemployed,   With = "StatusChange")]
  42.         [LifeTransition(From = LifeStatus.Unemployed,   When = "Employment",    Goto = LifeStatus.Employed,             With = "StatusChange")]
  43.         [LifeTransition(From = LifeStatus.Unemployed,   When = "Death",                 Goto = LifeStatus.Dead,                 With = "StatusChange")]
  44.         [LifeTransition(From = LifeStatus.Employed,             When = "Death",                 Goto = LifeStatus.Dead,                 With = "StatusChange")]
  45.         [LifeTransition(From = LifeStatus.Employed,             When = "Retirement",    Goto = LifeStatus.Retired,              With = "StatusChange")]
  46.         [LifeTransition(From = LifeStatus.Retired,              When = "Death",                 Goto = LifeStatus.Dead,                 With = "StatusChange")]
  47.         public class Human : State<LifeStatus>
  48.         {
  49.                 // The call to Build() is necessary to build the internal state/transition graph:
  50.                 public Human() { Build(); }
  51.  
  52.                 public static void StatusChange(IState<LifeStatus> state, LifeStatus from, string trigger, LifeStatus to, object args)
  53.                 {
  54.                         Console.WriteLine("\t\t\tFrom: {0} — (trigger: {1}) –> To: {2}", from, trigger, to);
  55.                 }
  56.         }
  57.  
  58.         // We also define a signal source of triggers/signals, an IObservable<string> here:
  59.         public class PersonLife : SignalSource { }
  60.  
  61.         // Finally, the state machine proper, compatible with the above:
  62.         public class Person : Machine<Human, LifeStatus> { }
  63.  
  64.         public static class Example
  65.         {
  66.                 public static void Run()
  67.                 {
  68.                         var JohnsLife = new PersonLife();
  69.                         var John = new Person().Using(JohnsLife).Start();
  70.                         Console.WriteLine("Simulation 0:");
  71.                         // We use the signal source that the start state (and others) of the state machine
  72.                         // is an observer of (IObserver<string>):
  73.                         JohnsLife.Emit("Birth");
  74.                         JohnsLife.Emit("Graduation");
  75.                         JohnsLife.Emit("Employment");
  76.                         JohnsLife.Emit("Retirement");
  77.                         JohnsLife.Emit("Death");
  78.                 }
  79.         }
  80. }
  81.  
  82. //–>

Note upfront: I crafted this small refactoring up using ICanHaz.js as the templating peer, which is a pretty cool helper, too*.

(* as I find Backbone.js a really nice gem, indeed :) )

coded there (main app, in todos.js);

and introducing/demonstrating :

1. configuration-driven and lazy resolution and loading (on first use in views’ render()) of ICanHaz.js / Mustache.js templates.

2. a little more separation of concerns via a view-state notion, for use when implementing our views’ Backbone.View.render() body, again.

The corresponding gists can be found over there.

Code extract (from todos.js) :

  1.  
  2. //Todo Item View
  3. TaskItemView = BaseView.extend({
  4.         template  : "itemTemplate",
  5.  
  6.         tagName : "li",
  7.  
  8.         initialize : function() {
  9.                 _.bindAll(this,"render","onChangeStatus");
  10.  
  11.                 this.model.bind("change",this.render);
  12.                 this.model.bind("change:status",this.onChangeStatus);
  13.         },
  14.  
  15.         // Events
  16.         events : {
  17.                 "click [data-ui=status]" : "onCheck",
  18.                 "click span.todo-destroy"   : "onClear",
  19.                 "dblclick div.todo-content" : "onEdit",
  20.                 "blur [data-ui=todo]" : "save"
  21.         },
  22.  
  23.         // Actions
  24.         onCheck : function() {
  25.                 this.model.changeStatus();
  26.         },
  27.         onClear : function(evt) {
  28.                 tasks.remove(this.model);
  29.         },
  30.         onEdit : function() {
  31.                 this.ui().toggleClass("editing");
  32.                 this.ui("todo").focus();
  33.         },
  34.         save : function(event) {
  35.                 this.ui().toggleClass("editing");
  36.                 this.model.set({name : this.ui("todo").val()});
  37.         },
  38.  
  39.         // Rendering
  40.         onChangeStatus : function() {
  41.                 this.ui("todo").toggleClass("done");
  42.         },
  43.  
  44.         render : function(){
  45.                 var viewState = this.load(this.model, {
  46.                         viewClass : (this.model.get("status") == "completed") ? "done" : "todo"
  47.                 });
  48.                 this.update(viewState);
  49.                 this.ui("status").prop("checked", viewState.viewClass == "done");
  50.                 return this;
  51.         }
  52. });
  53.  

Now have a good Backbone.js’ing &

‘Hope this helps.

Tags:

… triggered during parse-time, ready to play with, on Google Code :

http://code.google.com/p/ysharp/

Original post about this idea on LtU :

An attempt to make languages first class citizens, starting with the syntax

Some preliminary NDoc-generated API documentation browsable here.

I’ll be at Jonathan Crossland’s Talkware, on CodeGenWeek II (estimated 13th February 2009).

With Jonathan, Oleg, and Omer, I’ll have the pleasure to chat about CodeGen, DSL’s, Oslo (check out Why Oslo is important btw), and maybe a couple other caffeine-powered kinds of stuff.

That’s promising to be… sharpCOOL, no doubt.

Talkware

talking, people, software…

Tags: § § §

There we go.