diff --git a/Bantam/CommandChain.cs b/Bantam/CommandChain.cs index 5ba413a..e48c54c 100644 --- a/Bantam/CommandChain.cs +++ b/Bantam/CommandChain.cs @@ -4,6 +4,7 @@ namespace Bantam { public delegate void CommandInitializer(U cmd, T evt) where U : Command where T : Event; + public delegate void SimpleCommandInitializer(U cmd) where U : Command; public abstract class CommandChain { diff --git a/Bantam/CommandChainExecutor.cs b/Bantam/CommandChainExecutor.cs index 5f438d8..86e1065 100644 --- a/Bantam/CommandChainExecutor.cs +++ b/Bantam/CommandChainExecutor.cs @@ -3,7 +3,49 @@ namespace Bantam { - internal class CommandChainExecutor : Poolable + internal interface CommandChainExecutor : Poolable + { + void CurrentCommandComplete(); + void CurrentCommandFailed(); + } + + internal class SimpleCommandChainExecutor : CommandChainExecutor where T : Command, new() + { + private CommandRelay manager; + private ObjectPool pool; + private T command; + + public void Reset() + { + manager = null; + pool = null; + command = null; + } + + internal void Start(CommandRelay manager, ObjectPool pool, SimpleCommandInitializer initializer = null) + { + this.manager = manager; + this.pool = pool; + command = pool.Allocate(); + if (null != initializer) + initializer(command); + command.Start(this); + } + + public void CurrentCommandComplete() + { + pool.Free(command); + manager.CompleteChainExecution>(this); + } + + public void CurrentCommandFailed() + { + pool.Free(command); + manager.CompleteChainExecution>(this); + } + } + + internal class EventCommandChainExecutor : CommandChainExecutor { private CommandRelay manager; private Event triggeringEvent; @@ -30,21 +72,21 @@ internal void Start(Event triggeringEvent, CommandChain chain, CommandRelay mana Next(); } - internal void CurrentCommandComplete() + public void CurrentCommandComplete() { enumerator.Current.FreeCommand(pool, currentCommand); currentCommand = null; if (enumerator.MoveNext()) Next(); else - manager.CompleteChainExecution(this); + manager.CompleteChainExecution(this); } - internal void CurrentCommandFailed() + public void CurrentCommandFailed() { enumerator.Current.FreeCommand(pool, currentCommand); currentCommand = null; - manager.CompleteChainExecution(this); + manager.CompleteChainExecution(this); } private void Next() diff --git a/Bantam/CommandRelay.cs b/Bantam/CommandRelay.cs index 0722e15..4d5b176 100644 --- a/Bantam/CommandRelay.cs +++ b/Bantam/CommandRelay.cs @@ -23,17 +23,24 @@ public CommandRelay(EventBus eventBus, ObjectPool pool) chains[typeof(T)].Add(chain); eventBus.AddListener(ev => { - var executor = pool.Allocate(); + var executor = pool.Allocate(); activeExecutors.Add(executor); executor.Start(ev, chain, this, pool); }); return chain; } - internal void CompleteChainExecution(CommandChainExecutor executor) + public void Launch(SimpleCommandInitializer initializer = null) where U : Command, new() + { + var executor = pool.Allocate>(); + activeExecutors.Add(executor); + executor.Start(this, pool, initializer); + } + + internal void CompleteChainExecution(V executor) where V : CommandChainExecutor { activeExecutors.Remove(executor); - pool.Free(executor); + pool.Free(executor); } private void EnsureKeyExists() where T : Event diff --git a/BantamTest/CommandRelayTest.cs b/BantamTest/CommandRelayTest.cs index 8747aa8..dc49db9 100644 --- a/BantamTest/CommandRelayTest.cs +++ b/BantamTest/CommandRelayTest.cs @@ -62,6 +62,20 @@ public void CommandFailureStopsChainExecution() eventBus.Dispatch(); Assert.AreEqual(0, DummyCommand.ExecuteCount); } + + [Test] + public void LaunchCreatesCommandDirectly() + { + testObj.Launch(); + Assert.AreEqual(1, DummyCommand.ExecuteCount); + } + + [Test] + public void LaunchRunsInitializerOnCommandIfProvided() + { + testObj.Launch(cmd => cmd.value = 7500); + Assert.AreEqual(7500, DummyCommand.LastValue); + } } public class DummyCommand : Command diff --git a/README.md b/README.md index 1b19353..28aab69 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,8 @@ A lightweight, no garbage application framework for C# that's compatible with Un ## Features 1. **Object Pool** can be used with generic type parameters or Type object parameters. Pools expose the number of instances of each type they have created to aid with debugging and profiling. 1. **Event Bus** ensures all Events are pooled while still allowing custom data to be assigned to each instance. Supports adding/removing Event listeners, listeners that only get called once, and listeners that will receive all Events on the bus. -1. **Command Relay** will execute sequential chains of Commands in response to Events. Multiple Command chains can be registered for an Event, in which case they will run in parallel. Commands are synchronous by default, but can be made asynchronous on demand. If a Command fails, subsequent Commands in the chain will not be executed. +1. **Command Relay** will execute sequential chains of Commands in response to Events or individually. Multiple Command chains can be registered for an Event, in which case they will run in parallel. Commands are synchronous by default, but can be made asynchronous on demand. If a Command fails, subsequent Commands in the chain will not be executed. +1. **Model Registry** will manage a pool of active Models and let you retrieve them by type. Also emits events when Models are created and deleted, making it easy to write a wrapper for whatever GUI system you're using to bind views to Models. ## Examples ```csharp @@ -27,4 +28,7 @@ eventBus.Dispatch(evt => { commandRelay.On() .Do((cmd, evt) => cmd.loginEvent = evt) .Do(); + +//Create a Model. +modelRegistry.Create(mdl => mdl.username = "Jane Doe"); ```