diff --git a/Bonsai.Core.Tests/InspectBuilderTests.cs b/Bonsai.Core.Tests/InspectBuilderTests.cs index 4c6f3aad..4a0cc06b 100644 --- a/Bonsai.Core.Tests/InspectBuilderTests.cs +++ b/Bonsai.Core.Tests/InspectBuilderTests.cs @@ -100,6 +100,54 @@ public void Build_GroupInspectBuilder_ReturnNestedVisualizerElement() Assert.AreSame(target, visualizerElement.Builder); } + [TestMethod] + public void Build_DeferInspectBuilder_ReturnNestedVisualizerElement() + { + // related to https://github.com/bonsai-rx/bonsai/issues/1896 + ExpressionBuilder target = null; + var workflow = new TestWorkflow() + .AppendUnit() + .AppendNested( + input => input + .AppendUnit() + .Capture(out target) + .AppendOutput(), + workflow => new Reactive.Defer(workflow)) + .ToInspectableGraph(); + workflow.Build(); + + var output = workflow[workflow.Count - 1].Value; + var visualizerElement = ExpressionBuilder.GetVisualizerElement(output); + Assert.AreSame(target, visualizerElement.Builder); + } + + [TestMethod] + public void Build_DeferWithNestedVisualizerInspectBuilder_ReturnNestedVisualizerElement() + { + // related to https://github.com/bonsai-rx/bonsai/issues/1896 + ExpressionBuilder target = null; + var workflow = new TestWorkflow() + .AppendUnit() + .AppendNested( + input => input + .AppendUnit() + .AppendSubject(nameof(System.Reactive.Unit)) + .AppendNested( + input => input + .AppendUnit() + .Capture(out target) + .AppendOutput(), + workflow => new Reactive.Visualizer(workflow)) + .AppendOutput(), + workflow => new Reactive.Defer(workflow)) + .ToInspectableGraph(); + workflow.Build(); + + var output = workflow[workflow.Count - 1].Value; + var visualizerElement = ExpressionBuilder.GetVisualizerElement(output); + Assert.AreSame(target, visualizerElement.Builder); + } + [TestMethod] public void Build_PropertyMappedInspectBuilderToWorkflowOutput_ReturnVisualizerElement() { diff --git a/Bonsai.Core.Tests/TestWorkflow.cs b/Bonsai.Core.Tests/TestWorkflow.cs index a81ec258..e973c7ed 100644 --- a/Bonsai.Core.Tests/TestWorkflow.cs +++ b/Bonsai.Core.Tests/TestWorkflow.cs @@ -94,6 +94,13 @@ public TestWorkflow AppendNested( return Append(workflowBuilder); } + public TestWorkflow AppendSubject(string name) + where TSubjectBuilder : SubjectExpressionBuilder, new() + { + var subjectBuilder = new TSubjectBuilder { Name = name }; + return Append(subjectBuilder); + } + public ExpressionBuilderGraph ToInspectableGraph() { return Workflow.ToInspectableGraph(); diff --git a/Bonsai.Core/Expressions/InspectBuilder.cs b/Bonsai.Core/Expressions/InspectBuilder.cs index 9820d365..460fe7db 100644 --- a/Bonsai.Core/Expressions/InspectBuilder.cs +++ b/Bonsai.Core/Expressions/InspectBuilder.cs @@ -217,6 +217,13 @@ methodCall.Arguments[0] is MethodCallExpression lazy && { source = methodCall.Arguments[0]; } + // If multicasting into a defer combinator, recurse on the main output + else if (methodCall.Method.DeclaringType == typeof(Reactive.Defer) && + methodCall.Arguments.Count == 1 && + methodCall.Arguments[0] is LambdaExpression lambda) + { + return GetInspectBuilder(lambda.Body); + } else break; } else if (methodCall.Object.Type == typeof(InspectBuilder)) diff --git a/Bonsai.Core/Reactive/Defer.cs b/Bonsai.Core/Reactive/Defer.cs index 9c43de2c..1f125e2c 100644 --- a/Bonsai.Core/Reactive/Defer.cs +++ b/Bonsai.Core/Reactive/Defer.cs @@ -1,9 +1,7 @@ using System.Collections.Generic; -using System.Linq; using System.ComponentModel; using System.Xml.Serialization; using System.Linq.Expressions; -using System.Reflection; using System.Reactive.Linq; using System; using Bonsai.Expressions; @@ -18,12 +16,6 @@ namespace Bonsai.Reactive [Description("Creates a new observable sequence for each subscription using the encapsulated workflow.")] public class Defer : WorkflowExpressionBuilder { - static readonly MethodInfo deferMethod = typeof(Observable).GetMethods() - .Single(m => m.Name == "Defer" && - m.GetParameters()[0].ParameterType - .GetGenericArguments()[0] - .GetGenericTypeDefinition() == typeof(IObservable<>)); - /// /// Initializes a new instance of the class. /// @@ -59,8 +51,13 @@ public override Expression Build(IEnumerable arguments) { var factory = Expression.Lambda(selectorBody); var resultType = selectorBody.Type.GetGenericArguments()[0]; - return Expression.Call(deferMethod.MakeGenericMethod(resultType), factory); + return Expression.Call(typeof(Defer), nameof(Process), new[] { resultType }, factory); }); } + + static IObservable Process(Func> factory) + { + return Observable.Defer(factory); + } } }