Core.Attempt - backport and refactor Attempt.Outcome from v7

This commit is contained in:
Stephan
2013-09-11 09:28:01 +02:00
parent 32e3048717
commit 82f81d1f4f
3 changed files with 152 additions and 0 deletions

View File

@@ -52,5 +52,115 @@ namespace Umbraco.Core
{
return Attempt<T>.SucceedIf(success, result);
}
/// <summary>
/// Executes an attempt function, with callbacks.
/// </summary>
/// <typeparam name="T">The type of the attempted operation result.</typeparam>
/// <param name="attempt">The attempt returned by the attempt function.</param>
/// <param name="onSuccess">An action to execute in case the attempt succeeds.</param>
/// <param name="onFail">An action to execute in case the attempt fails.</param>
/// <returns>The outcome of the attempt.</returns>
/// <remarks>Runs <paramref name="onSuccess"/> or <paramref name="onFail"/> depending on the
/// whether the attempt function reports a success or a failure.</remarks>
public static Outcome Try<T>(Attempt<T> attempt, Action<T> onSuccess, Action<Exception> onFail = null)
{
if (attempt.Success)
{
onSuccess(attempt.Result);
return Outcome.Success;
}
if (onFail != null)
{
onFail(attempt.Exception);
}
return Outcome.Failure;
}
/// <summary>
/// Represents the outcome of an attempt.
/// </summary>
/// <remarks>Can be a success or a failure, and allows for attempts chaining.</remarks>
public struct Outcome
{
private readonly bool _success;
/// <summary>
/// Gets an outcome representing a success.
/// </summary>
public static readonly Outcome Success = new Outcome(true);
/// <summary>
/// Gets an outcome representing a failure.
/// </summary>
public static readonly Outcome Failure = new Outcome(false);
private Outcome(bool success)
{
_success = success;
}
/// <summary>
/// Executes another attempt function, if the previous one failed, with callbacks.
/// </summary>
/// <typeparam name="T">The type of the attempted operation result.</typeparam>
/// <param name="nextFunction">The attempt function to execute, returning an attempt.</param>
/// <param name="onSuccess">An action to execute in case the attempt succeeds.</param>
/// <param name="onFail">An action to execute in case the attempt fails.</param>
/// <returns>If it executes, returns the outcome of the attempt, else returns a success outcome.</returns>
/// <remarks>
/// <para>Executes only if the previous attempt failed, else does not execute and return a success outcome.</para>
/// <para>If it executes, then runs <paramref name="onSuccess"/> or <paramref name="onFail"/> depending on the
/// whether the attempt function reports a success or a failure.</para>
/// </remarks>
public Outcome OnFailure<T>(Func<Attempt<T>> nextFunction, Action<T> onSuccess, Action<Exception> onFail = null)
{
return _success
? Success
: ExecuteNextFunction(nextFunction, onSuccess, onFail);
}
/// <summary>
/// Executes another attempt function, if the previous one succeeded, with callbacks.
/// </summary>
/// <typeparam name="T">The type of the attempted operation result.</typeparam>
/// <param name="nextFunction">The attempt function to execute, returning an attempt.</param>
/// <param name="onSuccess">An action to execute in case the attempt succeeds.</param>
/// <param name="onFail">An action to execute in case the attempt fails.</param>
/// <returns>If it executes, returns the outcome of the attempt, else returns a failed outcome.</returns>
/// <remarks>
/// <para>Executes only if the previous attempt succeeded, else does not execute and return a success outcome.</para>
/// <para>If it executes, then runs <paramref name="onSuccess"/> or <paramref name="onFail"/> depending on the
/// whether the attempt function reports a success or a failure.</para>
/// </remarks>
public Outcome OnSuccess<T>(Func<Attempt<T>> nextFunction, Action<T> onSuccess, Action<Exception> onFail = null)
{
return _success
? ExecuteNextFunction(nextFunction, onSuccess, onFail)
: Failure;
}
private static Outcome ExecuteNextFunction<T>(Func<Attempt<T>> nextFunction, Action<T> onSuccess, Action<Exception> onFail = null)
{
var attempt = nextFunction();
if (attempt.Success)
{
onSuccess(attempt.Result);
return Success;
}
if (onFail != null)
{
onFail(attempt.Exception);
}
return Failure;
}
}
}
}

View File

@@ -0,0 +1,41 @@
using System;
using NUnit.Framework;
using Umbraco.Core;
namespace Umbraco.Tests
{
[TestFixture]
public class AttemptTests
{
[Test]
public void Chained_Attempts()
{
Attempt.Try(Attempt.Succeed("success!"),
s => Assert.AreEqual("success!", s),
exception => Assert.Fail("Should have been successful."))
// previous one was a success so that one SHOULD NOT run
// and report success
.OnFailure(() => Attempt.Succeed(123),
i => Assert.Fail("The previous attempt was successful!"),
exception => Assert.Fail("The previous attempt was successful!"))
// previous one did not run, last run was a success so that one SHOULD run
// and report failure
.OnSuccess(() => Attempt<double>.Fail(new Exception("Failed!")),
d => Assert.Fail("Should have failed."),
exception => Assert.AreEqual("Failed!", exception.Message))
// previous one did run and was a failure so that one SHOULD NOT run
.OnSuccess(() => Attempt.Succeed(987),
i => Assert.Fail("The previous attempt failed!"),
exception => Assert.Fail("The previous attempt failed!"))
// previous one did not run, last run was a failure so that one SHOULD run
// then why does it run?
.OnFailure(() => Attempt.Succeed("finished"),
i => Assert.AreEqual("finished", i),
exception => Assert.Fail("Should have been successful."));
}
}
}

View File

@@ -142,6 +142,7 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="AttemptTests.cs" />
<Compile Include="Auditing\AuditTests.cs" />
<Compile Include="BootManagers\CoreBootManagerTests.cs" />
<Compile Include="BusinessLogic\DictionaryTest.cs" />