Skip to content

Commit

Permalink
chained dialog folding, plus unit test
Browse files Browse the repository at this point in the history
  • Loading branch information
willportnoy committed Jun 25, 2016
1 parent 06c7137 commit b05d1c4
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 0 deletions.
64 changes: 64 additions & 0 deletions CSharp/Library/Dialogs/Chain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,18 @@ public static IDialog<T> Return<T>(T item)
return new ReturnDialog<T>(item);
}

/// <summary>
/// Fold items from an enumeration of dialogs.
/// </summary>
/// <typeparam name="T"> The type of the dialogs in the enumeration produced by the antecedent dialog.</typeparam>
/// <param name="antecedent">The antecedent dialog that produces an enumeration of <see cref="IDialog{T}"/>.</param>
/// <param name="folder">The accumulator for the dialog enumeration.</param>
/// <returns>The accumulated result.</returns>
public static IDialog<T> Fold<T>(this IDialog<IEnumerable<IDialog<T>>> antecedent, Func<T, T, T> folder)
{
return new FoldDialog<T>(antecedent, folder);
}

/// <summary>
/// Constructs a case.
/// </summary>
Expand Down Expand Up @@ -654,6 +666,58 @@ public async Task StartAsync(IDialogContext context)
context.Done(Value);
}
}

[Serializable]
private sealed class FoldDialog<T> : IDialog<T>
{
public readonly IDialog<IEnumerable<IDialog<T>>> Antecedent;
public readonly Func<T, T, T> Folder;
public FoldDialog(IDialog<IEnumerable<IDialog<T>>> antecedent, Func<T, T, T> folder)
{
SetField.NotNull(out this.Antecedent, nameof(antecedent), antecedent);
SetField.NotNull(out this.Folder, nameof(folder), folder);
}
async Task IDialog<T>.StartAsync(IDialogContext context)
{
context.Call(this.Antecedent, this.AfterAntecedent);
}
private IReadOnlyList<IDialog<T>> items;
private async Task AfterAntecedent(IDialogContext context, IAwaitable<IEnumerable<IDialog<T>>> result)
{
this.items = (await result).ToArray();
await Iterate(context);
}
private int index = 0;
private T folded = default(T);
private async Task Iterate(IDialogContext context)
{
if (this.index < this.items.Count)
{
var child = this.items[this.index];
context.Call(child, AfterItem);
}
else
{
context.Done(this.folded);
}
}
private async Task AfterItem(IDialogContext context, IAwaitable<T> result)
{
var itemT = await result;
if (this.index == 0)
{
this.folded = itemT;
}
else
{
this.folded = this.Folder(this.folded, itemT);
}

++this.index;

await this.Iterate(context);
}
}
}

/// <summary>
Expand Down
37 changes: 37 additions & 0 deletions CSharp/Tests/Microsoft.Bot.Builder.Tests/ChainTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,43 @@ public async Task LinqQuerySyntax_Throws_ClosureCaptureException()
}
}

[TestMethod]
public async Task SampleChain_Quiz()
{
var quiz = Chain
.PostToChain()
.Select(_ => "how many questions?")
.PostToUser()
.WaitToBot()
.Select(m => int.Parse(m.Text))
.Select(count => Enumerable.Range(0, count).Select(index => Chain.Return($"question {index + 1}?").PostToUser().WaitToBot().Select(m => m.Text)))
.Fold((l, r) => l + "," + r)
.Select(answers => "your answers were: " + answers)
.PostToUser();

using (var container = Build(Options.ResolveDialogFromContainer))
{
var builder = new ContainerBuilder();
builder
.RegisterInstance(quiz)
.As<IDialog<object>>();
builder.Update(container);

await AssertScriptAsync(container,
"hello",
"how many questions?",
"3",
"question 1?",
"A",
"question 2?",
"B",
"question 3?",
"C",
"your answers were: A,B,C"
);
}
}

[TestMethod]
public async Task SampleChain_Joke()
{
Expand Down

0 comments on commit b05d1c4

Please sign in to comment.