Skip to content

Commit 2d9a8ce

Browse files
authored
ReactiveNode: add AddNodeSource overloads for DI composition (#197)
* feat(ReactiveNode): add AddNodeSource overloads for DI composition Add two new AddNodeSource overloads to ILightTransitionReactiveNodeConfigurator: - AddNodeSource<TNodeSource>(): Resolves observable node sources from DI - AddNodeSource(Func<IServiceProvider, IObservable<...>>): Factory pattern for composing observables from existing DI services The factory overload enables cleaner extension methods that can leverage existing service registrations without creating dedicated node source classes. * Making TurnOffThenPassThroughNode public.
1 parent 792369d commit 2d9a8ce

4 files changed

Lines changed: 68 additions & 1 deletion

File tree

src/CodeCasa.AutomationPipelines.Lights/Nodes/TurnOffThenPassThroughNode.cs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,25 @@
22

33
namespace CodeCasa.AutomationPipelines.Lights.Nodes;
44

5-
internal class TurnOffThenPassThroughNode : PipelineNode<LightTransition>
5+
/// <summary>
6+
/// A pipeline node that initially outputs <see cref="LightTransition.Off()"/>,
7+
/// then switches to pass-through mode after receiving its first input.
8+
/// </summary>
9+
/// <remarks>
10+
/// This node is useful for scenarios where a light should be turned off,
11+
/// but then forward subsequent inputs from upstream nodes without modification.
12+
/// The pass-through behavior is activated upon receiving the first input.
13+
/// </remarks>
14+
public sealed class TurnOffThenPassThroughNode : PipelineNode<LightTransition>
615
{
16+
/// <summary>
17+
/// Initializes a new instance of the <see cref="TurnOffThenPassThroughNode"/> class.
18+
/// </summary>
19+
/// <remarks>
20+
/// The initial output is set to <see cref="LightTransition.Off()"/>.
21+
/// Pass-through mode is not enabled in the constructor because the input
22+
/// is immediately set when this node is added to the timeline.
23+
/// </remarks>
724
public TurnOffThenPassThroughNode()
825
{
926
// Note: we cannot simply call ChangeOutputAndTurnOnPassThroughOnNextInput here, as the input will immediately be set when this node is added to the timeline.

src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/CompositeLightTransitionReactiveNodeConfigurator.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,23 @@ public ILightTransitionReactiveNodeConfigurator<TLight> AddUncoupledDimmer(IDimm
7676
}
7777
return this;
7878

79+
80+
}
81+
82+
/// <inheritdoc/>
83+
public ILightTransitionReactiveNodeConfigurator<TLight> AddNodeSource<TNodeSource>()
84+
where TNodeSource : IObservable<Func<IServiceProvider, IPipelineNode<LightTransition>?>>
85+
{
86+
configurators.Values.ForEach(c => c.AddNodeSource<TNodeSource>());
87+
return this;
88+
}
89+
90+
/// <inheritdoc/>
91+
public ILightTransitionReactiveNodeConfigurator<TLight> AddNodeSource(
92+
Func<IServiceProvider, IObservable<Func<IServiceProvider, IPipelineNode<LightTransition>?>>> nodeFactorySourceFactory)
93+
{
94+
configurators.Values.ForEach(c => c.AddNodeSource(nodeFactorySourceFactory));
95+
return this;
7996
}
8097

8198
/// <inheritdoc/>

src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/ILightTransitionReactiveNodeConfigurator.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,25 @@ public partial interface ILightTransitionReactiveNodeConfigurator<TLight> where
5757
/// <returns>The configurator instance for method chaining.</returns>
5858
ILightTransitionReactiveNodeConfigurator<TLight> AddUncoupledDimmer(IDimmer dimmer, Action<DimmerOptions> dimOptions);
5959

60+
/// <summary>
61+
/// Adds a dynamic node source resolved from the service provider.
62+
/// The node source type must implement <see cref="IObservable{T}"/> where T is a factory function for creating pipeline nodes.
63+
/// Useful for reusable, class-based node sources registered in dependency injection.
64+
/// </summary>
65+
/// <typeparam name="TNodeSource">The type of the node source, which must be an observable that emits node factory functions.</typeparam>
66+
/// <returns>The configurator instance for method chaining.</returns>
67+
ILightTransitionReactiveNodeConfigurator<TLight> AddNodeSource<TNodeSource>()
68+
where TNodeSource : IObservable<Func<IServiceProvider, IPipelineNode<LightTransition>?>>;
69+
70+
/// <summary>
71+
/// Adds a dynamic node source created by a factory function that receives the service provider.
72+
/// Useful for extension methods that need to compose existing DI services to build the observable.
73+
/// </summary>
74+
/// <param name="nodeFactorySourceFactory">A factory function that receives the service provider and returns an observable that emits node factory functions.</param>
75+
/// <returns>The configurator instance for method chaining.</returns>
76+
ILightTransitionReactiveNodeConfigurator<TLight> AddNodeSource(
77+
Func<IServiceProvider, IObservable<Func<IServiceProvider, IPipelineNode<LightTransition>?>>> nodeFactorySourceFactory);
78+
6079
/// <summary>
6180
/// Adds a dynamic node source that activates a new node in the reactive node each time the observable emits a factory.
6281
/// The emitted factory is invoked to create and activate the new pipeline node.

src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/LightTransitionReactiveNodeConfigurator.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,20 @@ public ILightTransitionReactiveNodeConfigurator<TLight> AddNodeSource(IObservabl
113113
return this;
114114
}
115115

116+
/// <inheritdoc/>
117+
public ILightTransitionReactiveNodeConfigurator<TLight> AddNodeSource<TNodeSource>()
118+
where TNodeSource : IObservable<Func<IServiceProvider, IPipelineNode<LightTransition>?>>
119+
{
120+
return AddNodeSource(ActivatorUtilities.CreateInstance<TNodeSource>(ServiceProvider));
121+
}
122+
123+
/// <inheritdoc/>
124+
public ILightTransitionReactiveNodeConfigurator<TLight> AddNodeSource(
125+
Func<IServiceProvider, IObservable<Func<IServiceProvider, IPipelineNode<LightTransition>?>>> nodeFactorySourceFactory)
126+
{
127+
return AddNodeSource(nodeFactorySourceFactory(ServiceProvider));
128+
}
129+
116130
/// <inheritdoc/>
117131
public ILightTransitionReactiveNodeConfigurator<TLight> AddNodeSource(IObservable<Func<IServiceProvider, IPipelineNode<LightTransition>?>> nodeFactorySource)
118132
{

0 commit comments

Comments
 (0)