diff --git a/Bantam/CommandChain.cs b/Bantam/CommandChain.cs index e48c54c..34035a7 100644 --- a/Bantam/CommandChain.cs +++ b/Bantam/CommandChain.cs @@ -9,6 +9,7 @@ namespace Bantam public abstract class CommandChain { internal List Commands = new List(); + internal CommandAllocator FailureCommand; } public class CommandChain : CommandChain where T : class, Event @@ -18,5 +19,10 @@ public class CommandChain : CommandChain where T : class, Event Commands.Add(new CommandAllocator(initializer)); return this; } + + public void OnFailure(CommandInitializer initializer = null) where U : Command, new() + { + FailureCommand = new CommandAllocator(initializer); + } } } diff --git a/Bantam/CommandChainExecutor.cs b/Bantam/CommandChainExecutor.cs index 86e1065..bd6a049 100644 --- a/Bantam/CommandChainExecutor.cs +++ b/Bantam/CommandChainExecutor.cs @@ -50,6 +50,8 @@ internal class EventCommandChainExecutor : CommandChainExecutor private CommandRelay manager; private Event triggeringEvent; private List.Enumerator enumerator; + private CommandAllocator failureAllocator; + private CommandAllocator currentAllocator; private ObjectPool pool; private Command currentCommand; @@ -60,6 +62,8 @@ public void Reset() currentCommand = null; pool = null; enumerator.Dispose(); + failureAllocator = null; + currentAllocator = null; } internal void Start(Event triggeringEvent, CommandChain chain, CommandRelay manager, ObjectPool pool) @@ -67,6 +71,7 @@ internal void Start(Event triggeringEvent, CommandChain chain, CommandRelay mana this.triggeringEvent = triggeringEvent; this.manager = manager; this.pool = pool; + failureAllocator = chain.FailureCommand; enumerator = chain.Commands.GetEnumerator(); enumerator.MoveNext(); Next(); @@ -74,7 +79,7 @@ internal void Start(Event triggeringEvent, CommandChain chain, CommandRelay mana public void CurrentCommandComplete() { - enumerator.Current.FreeCommand(pool, currentCommand); + currentAllocator.FreeCommand(pool, currentCommand); currentCommand = null; if (enumerator.MoveNext()) Next(); @@ -84,14 +89,30 @@ public void CurrentCommandComplete() public void CurrentCommandFailed() { - enumerator.Current.FreeCommand(pool, currentCommand); + currentAllocator.FreeCommand(pool, currentCommand); currentCommand = null; - manager.CompleteChainExecution(this); + if (null != failureAllocator) + ExecuteFailureCommand(); + else + manager.CompleteChainExecution(this); } private void Next() { - currentCommand = enumerator.Current.AllocateCommand(pool, triggeringEvent); + currentAllocator = enumerator.Current; + StartCommandFromCurrentAllocator(); + } + + private void ExecuteFailureCommand() + { + currentAllocator = failureAllocator; + failureAllocator = null; + StartCommandFromCurrentAllocator(); + } + + void StartCommandFromCurrentAllocator() + { + currentCommand = currentAllocator.AllocateCommand(pool, triggeringEvent); currentCommand.Start(this); } } diff --git a/BantamTest/CommandRelayTest.cs b/BantamTest/CommandRelayTest.cs index dc49db9..f93dd18 100644 --- a/BantamTest/CommandRelayTest.cs +++ b/BantamTest/CommandRelayTest.cs @@ -76,6 +76,33 @@ public void LaunchRunsInitializerOnCommandIfProvided() testObj.Launch(cmd => cmd.value = 7500); Assert.AreEqual(7500, DummyCommand.LastValue); } + + [Test] + public void OnFailureExecutesCommandWhenChainFails() + { + testObj.On().Do().OnFailure(); + eventBus.Dispatch(); + Assert.AreEqual(1, DummyCommand.ExecuteCount); + } + + [Test] + public void OnFailureCommandCanFailWithoutCreatingInfiniteRecursion() + { + testObj.On().Do().OnFailure(); + eventBus.Dispatch(); + } + + [Test] + public void OnFailureCanTakeAnOptionalInitializerForCommand() + { + var expectedValue = 55; + testObj.On().Do().OnFailure((cmd, evt) => + { + cmd.value = evt.value; + }); + eventBus.Dispatch(evt => evt.value = expectedValue); + Assert.AreEqual(expectedValue, DummyCommand.LastValue); + } } public class DummyCommand : Command diff --git a/README.md b/README.md index 28aab69..7623bcf 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,11 @@ commandRelay.On() .Do((cmd, evt) => cmd.loginEvent = evt) .Do(); +//Handle a failed command chain +commandRelay.On() + .Do() + .OnFailure(); + //Create a Model. modelRegistry.Create(mdl => mdl.username = "Jane Doe"); ```