diff --git a/src/PersistenceExample/App.config b/src/PersistenceExample/App.config new file mode 100644 index 0000000..343984d --- /dev/null +++ b/src/PersistenceExample/App.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/PersistenceExample/PersistenceExample.csproj b/src/PersistenceExample/PersistenceExample.csproj new file mode 100644 index 0000000..e29df7e --- /dev/null +++ b/src/PersistenceExample/PersistenceExample.csproj @@ -0,0 +1,64 @@ + + + + + Debug + AnyCPU + {3C45B29F-222C-4914-A4F8-3775438A4070} + Exe + Properties + PersistenceExample + PersistenceExample + v3.5 + 512 + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + {a15eec02-60ec-4705-a58b-a6ecd55a9628} + Stateless + + + + + \ No newline at end of file diff --git a/src/PersistenceExample/Program.cs b/src/PersistenceExample/Program.cs new file mode 100644 index 0000000..01542ca --- /dev/null +++ b/src/PersistenceExample/Program.cs @@ -0,0 +1,51 @@ +using System; +using Stateless; + +namespace PersistenceExample +{ + static class Program + { + static void Main() + { + try + { + const string on = "On"; + const string off = "Off"; + const char space = ' '; + + Console.WriteLine("Press to toggle the switch. Any other key will raise an error."); + + var savedState = off; + Func loadMethod = () => + { + Console.WriteLine("(Persistence: state {0} has been loaded)", savedState); + return savedState; + }; + Action saveMethod = state => + { + savedState = state; + Console.WriteLine("(Persistence: state {0} has been saved)", state); + }; + + var initialState = loadMethod(); + var onOffSwitch = new StateMachine(initialState, saveMethod); + + onOffSwitch.Configure(off).Permit(space, on); + onOffSwitch.Configure(on).Permit(space, off); + + while (true) + { + Console.WriteLine("Switch is in state: " + onOffSwitch.State); + var pressed = Console.ReadKey(true).KeyChar; + onOffSwitch.Fire(pressed); + } + } + catch (Exception ex) + { + Console.WriteLine("Exception: " + ex.Message); + Console.WriteLine("Press any key to continue..."); + Console.ReadKey(true); + } + } + } +} diff --git a/src/PersistenceExample/Properties/AssemblyInfo.cs b/src/PersistenceExample/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..6684b51 --- /dev/null +++ b/src/PersistenceExample/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("PersistenceExample")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("PersistenceExample")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("2a02c2e2-2c9a-433f-8e35-1e1e72e3625c")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Stateless.Tests/StateMachineFixture.cs b/src/Stateless.Tests/StateMachineFixture.cs index 4cc5d7b..99f2e70 100755 --- a/src/Stateless.Tests/StateMachineFixture.cs +++ b/src/Stateless.Tests/StateMachineFixture.cs @@ -66,6 +66,31 @@ public void StateCanBeStoredExternally() Assert.AreEqual(State.C, state); } + [Test] + public void StateIsWrittenJustOnceOnEveryStateTransition() + { + var state = State.B; + var saveCount = 0; + Action stateMutator = s => + { + saveCount++; + state = s; + }; + var sm = new StateMachine(State.B, stateMutator); + sm.Configure(State.B).Permit(Trigger.X, State.C); + sm.Configure(State.C).Permit(Trigger.Y, State.B); + Assert.AreEqual(State.B, sm.State); + Assert.AreEqual(State.B, state); + sm.Fire(Trigger.X); + Assert.AreEqual(State.C, sm.State); + Assert.AreEqual(State.C, state); + Assert.AreEqual(1, saveCount); + sm.Fire(Trigger.Y); + Assert.AreEqual(State.B, sm.State); + Assert.AreEqual(State.B, state); + Assert.AreEqual(2, saveCount); + } + [Test] public void SubstateIsIncludedInCurrentState() { diff --git a/src/Stateless.sln b/src/Stateless.sln index 307b401..e34f1db 100755 --- a/src/Stateless.sln +++ b/src/Stateless.sln @@ -1,6 +1,8 @@  -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.31101.0 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stateless", "Stateless\Stateless.csproj", "{A15EEC02-60EC-4705-A58B-A6ECD55A9628}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stateless.Tests", "Stateless.Tests\Stateless.Tests.csproj", "{70AD8860-1F5E-4F78-9276-CAD498EDDF73}" @@ -11,6 +13,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TelephoneCallExample", "Tel EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BugTrackerExample", "BugTrackerExample\BugTrackerExample.csproj", "{3A56311E-C3AC-4E34-A1DF-CA5042A31039}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PersistenceExample", "PersistenceExample\PersistenceExample.csproj", "{3C45B29F-222C-4914-A4F8-3775438A4070}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -37,6 +41,10 @@ Global {3A56311E-C3AC-4E34-A1DF-CA5042A31039}.Debug|Any CPU.Build.0 = Debug|Any CPU {3A56311E-C3AC-4E34-A1DF-CA5042A31039}.Release|Any CPU.ActiveCfg = Release|Any CPU {3A56311E-C3AC-4E34-A1DF-CA5042A31039}.Release|Any CPU.Build.0 = Release|Any CPU + {3C45B29F-222C-4914-A4F8-3775438A4070}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3C45B29F-222C-4914-A4F8-3775438A4070}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3C45B29F-222C-4914-A4F8-3775438A4070}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3C45B29F-222C-4914-A4F8-3775438A4070}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Stateless/StateMachine.cs b/src/Stateless/StateMachine.cs index beecc6e..984ba80 100755 --- a/src/Stateless/StateMachine.cs +++ b/src/Stateless/StateMachine.cs @@ -39,6 +39,22 @@ public StateMachine(TState initialState) _stateMutator = s => reference.State = s; } + /// + /// Construct a state machine with external storage (saving only). + /// + /// The initial state. + /// An action that will be called to write new state values. + public StateMachine(TState initialState, Action stateMutator) + { + var reference = new StateReference { State = initialState }; + _stateAccessor = () => reference.State; + _stateMutator = s => + { + reference.State = s; + stateMutator(s); + }; + } + /// /// The current state. ///