Skip to content

Commit

Permalink
Merge pull request #3 from wlindley/models
Browse files Browse the repository at this point in the history
Implemented models for bantam.
  • Loading branch information
wlindley authored Jul 6, 2016
2 parents 9484167 + 6dbb58b commit c3f5aba
Show file tree
Hide file tree
Showing 7 changed files with 300 additions and 0 deletions.
4 changes: 4 additions & 0 deletions Bantam/Bantam.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@
<Compile Include="CommandChainExecutor.cs" />
<Compile Include="CommandRelay.cs" />
<Compile Include="CommandAllocator.cs" />
<Compile Include="ModelRegistry.cs" />
<Compile Include="Model.cs" />
<Compile Include="ModelCreatedEvent.cs" />
<Compile Include="ModelDestroyedEvent.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>
6 changes: 6 additions & 0 deletions Bantam/Model.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Bantam
{
public interface Model : Poolable
{
}
}
17 changes: 17 additions & 0 deletions Bantam/ModelCreatedEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System;

namespace Bantam
{
public class ModelCreatedEvent : Event
{
public Model model;
public Type type;

public void Reset()
{
model = null;
type = null;
}
}
}

16 changes: 16 additions & 0 deletions Bantam/ModelDestroyedEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;

namespace Bantam
{
public class ModelDestroyedEvent : Event
{
public Model model;
public Type type;

public void Reset()
{
model = null;
type = null;
}
}
}
100 changes: 100 additions & 0 deletions Bantam/ModelRegistry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace Bantam
{
public delegate void ModelInitializer<T>(T model) where T : Model;

public class ModelRegistry
{
private Dictionary<Type, ModelListWrapper> modelLists;
private EventBus eventBus;
private ObjectPool pool;

public ModelRegistry(ObjectPool pool, EventBus eventBus)
{
this.pool = pool;
this.eventBus = eventBus;
modelLists = new Dictionary<Type, ModelListWrapper>();
}

public void Create<T>(ModelInitializer<T> initializer = null) where T : class, Model, new()
{
EnsureTypeExists<T>();
(modelLists[typeof(T)] as ModelListWrapper<T>).CreateModel(initializer);
}

public IEnumerable<T> Get<T>() where T : class, Model, new()
{
EnsureTypeExists<T>();
return modelLists[typeof(T)].GetModels<T>();
}

public void Destroy<T>(T model) where T : class, Model, new()
{
EnsureTypeExists<T>();
(modelLists[typeof(T)] as ModelListWrapper<T>).DestroyModel(model);
}

private void EnsureTypeExists<T>() where T : class, Model, new()
{
var type = typeof(T);
if (!modelLists.ContainsKey(type))
modelLists[type] = new ModelListWrapper<T>(pool, eventBus);
}

private interface ModelListWrapper
{
IEnumerable<T> GetModels<T>() where T : class, Model, new();
}

private class ModelListWrapper<T> : ModelListWrapper where T : class, Model, new()
{
private List<T> models;
private ObjectPool pool;
private EventBus eventBus;

public ModelListWrapper(ObjectPool pool, EventBus eventBus)
{
this.pool = pool;
this.eventBus = eventBus;
models = new List<T>();
}

public IEnumerable<U> GetModels<U>() where U : class, Model, new()
{
return models as IEnumerable<U>;
}

public void CreateModel(ModelInitializer<T> initializer = null)
{
var instance = pool.Allocate<T>();
if (null != initializer)
initializer(instance);
models.Add(instance);
eventBus.Dispatch<ModelCreatedEvent>(evt => {
evt.model = instance;
evt.type = typeof(T);
});
}

public void DestroyModel(T model)
{
if (null == model)
throw new NullModelException();
if (!models.Contains(model))
return;
models.Remove(model);
eventBus.Dispatch<ModelDestroyedEvent>(evt => {
evt.model = model;
evt.type = typeof(T);
});
pool.Free(model);
}
}
}

public class ModelRegistryException : Exception {}
public class NullModelException : ModelRegistryException {}
}
1 change: 1 addition & 0 deletions BantamTest/BantamTest.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,6 @@
<Compile Include="EventBusTest.cs" />
<Compile Include="CommandRelayTest.cs" />
<Compile Include="IntegrationTest.cs" />
<Compile Include="ModelRegistryTest.cs" />
</ItemGroup>
</Project>
156 changes: 156 additions & 0 deletions BantamTest/ModelRegistryTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
using NUnit.Framework;

namespace Bantam.Test
{
public class ModelRegistryTest
{
private ModelRegistry testObj;
private EventBus eventBus;

[SetUp]
public void SetUp()
{
var pool = new ObjectPool();
eventBus = new EventBus(pool);
testObj = new ModelRegistry(pool, eventBus);
}

[Test]
public void CreatingModelAllowsItToBeRetrieved()
{
var modelCount = 0;
testObj.Create<DummyModel>();
foreach (var model in testObj.Get<DummyModel>())
modelCount++;
Assert.AreEqual(1, modelCount);
}

[Test]
public void CreateCallsInitializerWhenProvided()
{
var modelCount = 0;
var expectedValue = "a random string";
testObj.Create<DummyModel>((model) => {
model.value = expectedValue;
});
foreach (var model in testObj.Get<DummyModel>())
if (expectedValue == model.value)
modelCount++;
Assert.AreEqual(1, modelCount);
}

[Test]
public void CreateFiresEvent()
{
var eventFired = false;
var expectedValue = "another random string";
eventBus.AddListener<ModelCreatedEvent>((evt) => {
var model = (DummyModel)evt.model;
Assert.AreEqual(expectedValue, model.value);
Assert.AreEqual(typeof(DummyModel), evt.type);
eventFired = true;
});
testObj.Create<DummyModel>((model) => {
model.value = expectedValue;
});
Assert.IsTrue(eventFired);
}

[Test]
public void GetReturnsEmptyEnumerableWhenNoModelsCreatedForType()
{
var wasCalled = false;
foreach (var model in testObj.Get<DummyModel>())
wasCalled = true;
Assert.IsFalse(wasCalled);
}

[Test]
public void DestroyCausesSpecifiedModelToNotBeReturnedByGet()
{
var matchingCount = 0;
var totalCount = 0;
var expectedValue = "yet another random string";

DummyModel specifiedModel = null;
testObj.Create<DummyModel>(model => {
model.value = expectedValue;
specifiedModel = model;
});
testObj.Create<DummyModel>();

testObj.Destroy<DummyModel>(specifiedModel);

foreach (var model in testObj.Get<DummyModel>())
{
totalCount++;
if (expectedValue == model.value)
matchingCount++;
}
Assert.AreEqual(0, matchingCount);
Assert.AreEqual(1, totalCount);
}

[Test]
public void DestroyDoesNothingSilentlyWhenSpecifiedModelIsNotInModelRegistry()
{
var model = new DummyModel();
testObj.Destroy<DummyModel>(model);
}

[Test]
public void DestroyThrowsNullModelExceptionWhenSpecifiedModelIsNull()
{
Assert.Throws<NullModelException>(() => testObj.Destroy<DummyModel>(null));
}

[Test]
public void DestroyFiresEvent()
{
var eventFired = false;
var expectedValue = "the most random string";

DummyModel actualModel = null;
eventBus.AddListener<ModelDestroyedEvent>((evt) => {
var model = (DummyModel)evt.model;
Assert.AreEqual(expectedValue, model.value);
Assert.AreEqual(typeof(DummyModel), evt.type);
eventFired = true;
});
testObj.Create<DummyModel>((model) => {
model.value = expectedValue;
actualModel = model;
});

testObj.Destroy<DummyModel>(actualModel);

Assert.IsTrue(eventFired);
}

[Test]
public void DestroyDoesNotFireAnEventWhenSpecifiedModelIsNotInModelRegistry()
{
var wasCalled = false;
var model = new DummyModel();
eventBus.AddListener<ModelDestroyedEvent>(evt => {
wasCalled = true;
});

testObj.Destroy<DummyModel>(model);

Assert.IsFalse(wasCalled);
}
}

public class DummyModel : Model
{
public string value = "";

public void Reset() {}
}

public class DummyModel2 : Model
{
public void Reset() {}
}
}

0 comments on commit c3f5aba

Please sign in to comment.