Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow multiple REstate host instances #18

Merged
merged 2 commits into from
Dec 23, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion src/REstate/Agent.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
namespace REstate
using REstate.Engine;
using REstate.Schematics;

namespace REstate
{
public interface IAgent
{
Expand All @@ -14,5 +17,10 @@ public Agent(HostConfiguration hostConfiguration)
{
Configuration = hostConfiguration;
}

public string GetStateMap<TState, TInput>(Schematic<TState, TInput> schematic) =>
((HostConfiguration)Configuration).Container
.Resolve<ICartographer<TState, TInput>>()
.WriteMap(schematic.States);
}
}
79 changes: 52 additions & 27 deletions src/REstate/REstateHost.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Linq.Expressions;
using REstate.Engine;
using REstate.IoC;
using REstate.IoC.BoDi;
Expand All @@ -10,29 +11,48 @@ namespace REstate
/// <summary>
/// The entry-point to REstate.
/// </summary>
public static class REstateHost
public class REstateHost
: IHasAgentLazy
{
private static readonly object ConfigurationSyncRoot = new object();
public REstateHost(IComponentContainer container)
: this()
{
var configuration = new HostConfiguration(container);

configuration.RegisterDefaults();

HostConfiguration = configuration;
}

public REstateHost()
{
((IHasAgentLazy) this).AgentLazy = new Lazy<IAgent>(CreateAgent);
}

private static ILog Logger => LogProvider.GetLogger(typeof(REstateHost));
private static readonly Lazy<REstateHost> LazyREstateHost = new Lazy<REstateHost>(() => new REstateHost());

private static IAgent CreateAgent()
private static REstateHost SharedInstance => LazyREstateHost.Value;

internal readonly object ConfigurationSyncRoot = new object();

private IAgent CreateAgent()
{
if (HostConfiguration == null)
lock (ConfigurationSyncRoot)
if (HostConfiguration == null)
UseContainer(
new BoDiComponentContainer(
host: this,
container: new BoDiComponentContainer(
new ObjectContainer()));

return new Agent(HostConfiguration);
}

private static Lazy<IAgent> _agentLazy = new Lazy<IAgent>(CreateAgent);
Lazy<IAgent> IHasAgentLazy.AgentLazy { get; set; }

public static IAgent Agent => _agentLazy.Value;
public static IAgent Agent => SharedInstance.Agent();

private static HostConfiguration HostConfiguration { get; set; }
private HostConfiguration HostConfiguration { get; set; }

/// <summary>
/// Instructs REstate to use a custom container for all components.
Expand All @@ -41,38 +61,43 @@ private static IAgent CreateAgent()
/// <param name="container">The container to use.</param>
public static void UseContainer(IComponentContainer container)
{
lock (ConfigurationSyncRoot)
UseContainer(SharedInstance, container);
}

/// <summary>
/// Instructs REstate to use a custom container for all components.
/// Defaults will be registered into the specified container.
/// </summary>
/// <param name="host">The host to modify</param>
/// <param name="container">The container to use.</param>
private static void UseContainer(REstateHost host, IComponentContainer container)
{
lock (host.ConfigurationSyncRoot)
{
if (HostConfiguration != null)
if (host.HostConfiguration != null)
throw new InvalidOperationException(
"Configuration has already been initialized; " +
"cannot replace container at this point.");

Logger.Debug("REstateHost configuration initializing...");

var configuration = new HostConfiguration(container);

configuration.RegisterDefaults();

HostConfiguration = configuration;

Logger.Debug("REstateHost configuration initialized");
host.HostConfiguration = configuration;
}
}
}

/// <summary>
/// Clears configuration and the Agent singleton.
/// </summary>
internal static void ResetAgent()
internal interface IHasAgentLazy
{
Lazy<IAgent> AgentLazy { get; set; }
}

public static class REstateHostExtensions
{
public static IAgent Agent(this REstateHost host)
{
HostConfiguration = null;
_agentLazy = new Lazy<IAgent>(CreateAgent);
return ((IHasAgentLazy)host).AgentLazy.Value;
}

public static string WriteStateMap<TState, TInput>(this Schematic<TState, TInput> schematic) =>
HostConfiguration.Container
.Resolve<ICartographer<TState, TInput>>()
.WriteMap(schematic.States);

}
}
50 changes: 50 additions & 0 deletions test/REstate.Tests/Features/Configuration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using LightBDD.Framework;
using LightBDD.Framework.Scenarios.Contextual;
using LightBDD.Framework.Scenarios.Extended;
using LightBDD.XUnit2;
using REstate.IoC.BoDi;
using REstate.Tests.Features.Context;
using Xunit.Abstractions;

// ReSharper disable InconsistentNaming

namespace REstate.Tests.Features
{
[FeatureDescription(@"
In order to extend REstate
As a developer
I want to use the configuration")]
[ScenarioCategory("Configuration")]
public class Configuration
: FeatureFixture
{
[Scenario]
public void Configuration_is_accessible_with_default_container()
{
Runner.WithContext<REstateContext>().RunScenario(
_ => _.Given_a_new_host(),
_ => _.When_configuration_is_accessed(),
_ => _.Then_configuration_is_not_null(),
_ => _.Then_configuration_has_a_container());
}

[Scenario]
public void Configuration_is_accessible_with_custom_container()
{
var customComponentContainer = new BoDiComponentContainer(new ObjectContainer());

Runner.WithContext<REstateContext>().RunScenario(
_ => _.Given_a_new_host_with_custom_ComponentContainer(customComponentContainer),
_ => _.When_configuration_is_accessed(),
_ => _.Then_configuration_is_not_null(),
_ => _.Then_configuration_has_a_container());
}

#region Constructor
public Configuration(ITestOutputHelper output)
: base(output)
{
}
#endregion
}
}
38 changes: 0 additions & 38 deletions test/REstate.Tests/Features/Configuration/Configuration.cs

This file was deleted.

42 changes: 0 additions & 42 deletions test/REstate.Tests/Features/Configuration/Configuration.steps.cs

This file was deleted.

24 changes: 24 additions & 0 deletions test/REstate.Tests/Features/Context/Configuration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Xunit;

namespace REstate.Tests.Features.Context
{
public partial class REstateContext
{
public IHostConfiguration CurrentHostConfiguration { get; set; }

public void When_configuration_is_accessed()
{
CurrentHostConfiguration = CurrentHost.Agent().Configuration;
}

public void Then_configuration_has_a_container()
{
Assert.NotNull(((HostConfiguration)CurrentHostConfiguration).Container);
}

public void Then_configuration_is_not_null()
{
Assert.NotNull(CurrentHostConfiguration);
}
}
}
21 changes: 21 additions & 0 deletions test/REstate.Tests/Features/Context/Host.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using REstate.IoC;
using REstate.IoC.BoDi;
using Xunit;

namespace REstate.Tests.Features.Context
{
public partial class REstateContext
{
public REstateHost CurrentHost { get; set; }

public void Given_a_new_host()
{
CurrentHost = new REstateHost();
}

public void Given_a_new_host_with_custom_ComponentContainer(IComponentContainer componentContainer)
{
CurrentHost = new REstateHost(componentContainer);
}
}
}
20 changes: 20 additions & 0 deletions test/REstate.Tests/Features/Context/Schematics.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using REstate.Schematics;

namespace REstate.Tests.Features.Context
{
public partial class REstateContext<TState, TInput>
: REstateContext
{
public Schematic<TState, TInput> CurrentSchematic { get; set; }

public void Given_a_simple_schematic_with_an_initial_state_INITIALSTATE(TState initialState)
{
CurrentSchematic = CurrentHost.Agent()
.CreateSchematic<TState, TInput>("simple")
.WithState(initialState, state => state
.AsInitialState())
.Build();
}
}
}
19 changes: 19 additions & 0 deletions test/REstate.Tests/Units/REstateHost.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
using Xunit;

namespace REstate.Tests.Units
{
public class REstateHostTests
{
[Fact]
public void REstateHost_Agent_Is_Accessible()
{
var agent = REstateHost.Agent;

Assert.NotNull(agent);
}

}
}