Starting an implementation of a composite model management system

--HG--
extra : convert_revision : svn%3A5ff7c347-ad56-4c35-b696-ccb81de16e03/trunk%4038935
This commit is contained in:
loudej
2009-11-08 05:27:47 +00:00
parent 8eec669684
commit 72851737cc
24 changed files with 327 additions and 7 deletions

View File

@@ -5,7 +5,7 @@ using System.Linq;
using NHibernate;
using NUnit.Framework;
using Orchard.Data;
using Orchard.Tests.Models;
using Orchard.Tests.Records;
namespace Orchard.Tests.Data {
[TestFixture]

View File

@@ -9,7 +9,7 @@ using NHibernate;
using NHibernate.Criterion;
using NHibernate.Tool.hbm2ddl;
using NUnit.Framework;
using Orchard.Tests.Models;
using Orchard.Tests.Records;
namespace Orchard.Tests {
[TestFixture]

View File

@@ -2,7 +2,7 @@
using NHibernate;
using NHibernate.Linq;
using NUnit.Framework;
using Orchard.Tests.Models;
using Orchard.Tests.Records;
namespace Orchard.Tests {
[TestFixture]

View File

@@ -0,0 +1,64 @@
using System;
using Autofac;
using Autofac.Builder;
using Autofac.Modules;
using NUnit.Framework;
using Orchard.Models;
using Orchard.Models.Driver;
using Orchard.Tests.Models.Stubs;
namespace Orchard.Tests.Models {
[TestFixture]
public class DefaultModelBuilderTests {
private IContainer _container;
private IModelManager _manager;
[SetUp]
public void Init() {
var builder = new ContainerBuilder();
builder.RegisterModule(new ImplicitCollectionSupportModule());
builder.Register<DefaultModelManager>().As<IModelManager>();
builder.Register<AlphaDriver>().As<IModelDriver>();
builder.Register<BetaDriver>().As<IModelDriver>();
builder.Register<FlavoredDriver>().As<IModelDriver>();
builder.Register<StyledDriver>().As<IModelDriver>();
_container = builder.Build();
_manager = _container.Resolve<IModelManager>();
}
[Test]
public void AlphaDriverShouldWeldItsPart() {
var foo = _manager.New("alpha");
Assert.That(foo.Is<Alpha>(), Is.True);
Assert.That(foo.As<Alpha>(), Is.Not.Null);
Assert.That(foo.Is<Beta>(), Is.False);
Assert.That(foo.As<Beta>(), Is.Null);
}
[Test]
public void StronglyTypedNewShouldTypeCast() {
var foo = _manager.New<Alpha>("alpha");
Assert.That(foo, Is.Not.Null);
Assert.That(foo.GetType(), Is.EqualTo(typeof(Alpha)));
}
[Test, ExpectedException(typeof(InvalidCastException))]
public void StronglyTypedNewShouldThrowCastExceptionIfNull() {
var foo = _manager.New<Beta>("alpha");
}
[Test]
public void AlphaIsFlavoredAndStyledAndBetaIsFlavoredOnly() {
var alpha = _manager.New<Alpha>("alpha");
var beta = _manager.New<Beta>("beta");
Assert.That(alpha.Is<Flavored>(), Is.True);
Assert.That(alpha.Is<Styled>(), Is.True);
Assert.That(beta.Is<Flavored>(), Is.True);
Assert.That(beta.Is<Styled>(), Is.False);
}
}
}

View File

@@ -0,0 +1,6 @@
using Orchard.Models;
namespace Orchard.Tests.Models.Stubs {
public class Alpha : ModelPart {
}
}

View File

@@ -0,0 +1,11 @@
using Orchard.Models.Driver;
namespace Orchard.Tests.Models.Stubs {
public class AlphaDriver : ModelDriverBase {
protected override void New(NewModelContext context) {
if (context.ModelType == "alpha") {
WeldModelPart<Alpha>(context);
}
}
}
}

View File

@@ -0,0 +1,6 @@
using Orchard.Models;
namespace Orchard.Tests.Models.Stubs {
public class Beta : ModelPart {
}
}

View File

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Orchard.Models.Driver;
namespace Orchard.Tests.Models.Stubs {
public class BetaDriver : ModelDriverBase {
protected override void New(NewModelContext context) {
if (context.ModelType == "beta") {
WeldModelPart<Beta>(context);
}
}
}
}

View File

@@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Orchard.Models;
namespace Orchard.Tests.Models.Stubs {
class Flavored : ModelPart {
}
}

View File

@@ -0,0 +1,11 @@
using Orchard.Models.Driver;
namespace Orchard.Tests.Models.Stubs {
public class FlavoredDriver : ModelDriverBase {
protected override void New(NewModelContext context) {
if (context.ModelType == "beta" || context.ModelType == "alpha") {
WeldModelPart<Flavored>(context);
}
}
}
}

View File

@@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Orchard.Models;
namespace Orchard.Tests.Models.Stubs {
public class Styled : ModelPart {
}
}

View File

@@ -0,0 +1,11 @@
using Orchard.Models.Driver;
namespace Orchard.Tests.Models.Stubs {
public class StyledDriver : ModelDriverBase {
protected override void New(NewModelContext context) {
if (context.ModelType == "alpha") {
WeldModelPart<Styled>(context);
}
}
}
}

View File

@@ -104,6 +104,15 @@
<Compile Include="Environment\DefaultOrchardRuntimeTests.cs" />
<Compile Include="Environment\OrchardStarterTests.cs" />
<Compile Include="Logging\LoggingModuleTests.cs" />
<Compile Include="Models\DefaultModelBuilderTests.cs" />
<Compile Include="Models\Stubs\Alpha.cs" />
<Compile Include="Models\Stubs\AlphaDriver.cs" />
<Compile Include="Models\Stubs\Beta.cs" />
<Compile Include="Models\Stubs\BetaDriver.cs" />
<Compile Include="Models\Stubs\Flavored.cs" />
<Compile Include="Models\Stubs\FlavoredDriver.cs" />
<Compile Include="Models\Stubs\Styled.cs" />
<Compile Include="Models\Stubs\StyledDriver.cs" />
<Compile Include="Mvc\ModelBinders\KeyedListModelBinderTests.cs" />
<Compile Include="Mvc\OrchardControllerFactoryTests.cs" />
<Compile Include="Mvc\OrchardControllerIdentificationStrategyTests.cs" />
@@ -117,8 +126,8 @@
<Compile Include="FakeTests.cs" />
<Compile Include="FluentDbTests.cs" />
<Compile Include="LinqToNHibernateTests.cs" />
<Compile Include="Models\Bar.cs" />
<Compile Include="Models\Foo.cs" />
<Compile Include="Records\Bar.cs" />
<Compile Include="Records\Foo.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Stubs\StubHttpContext.cs" />
</ItemGroup>

View File

@@ -1,4 +1,4 @@
namespace Orchard.Tests.Models {
namespace Orchard.Tests.Records {
public class Bar {
public virtual int Id { get; set; }
public virtual decimal Height { get; set; }

View File

@@ -1,4 +1,4 @@
namespace Orchard.Tests.Models {
namespace Orchard.Tests.Records {
public class Foo {
public virtual int Id { get; set; }
public virtual string Name { get; set; }

View File

@@ -0,0 +1,29 @@
using System.Collections.Generic;
using Orchard.Models.Driver;
namespace Orchard.Models {
public class DefaultModelManager : IModelManager {
private readonly IEnumerable<IModelDriver> _drivers;
public DefaultModelManager(IEnumerable<IModelDriver> drivers) {
_drivers = drivers;
}
public virtual IModel New(string modelType) {
// create a new kernel for the model instance
var context = new NewModelContext {
ModelType = modelType,
Instance = new ModelRoot(modelType)
};
// invoke drivers to weld aspects onto kernel
foreach(var driver in _drivers) {
driver.New(context);
}
// composite result is returned
return context.Instance;
}
}
}

View File

@@ -0,0 +1,27 @@
using Orchard.Logging;
namespace Orchard.Models.Driver {
public interface IModelDriver : IDependency {
void New(NewModelContext context);
}
public abstract class ModelDriverBase : IModelDriver {
protected ModelDriverBase() {
Logger = NullLogger.Instance;
}
public ILogger Logger{ get; set;}
void IModelDriver.New(NewModelContext context) {New(context);}
protected virtual void New(NewModelContext context) {
}
protected void WeldModelPart<TPart>(NewModelContext context) where TPart : class,IModel,new() {
var newPart = new TPart();
newPart.Weld(context.Instance);
context.Instance = newPart;
}
}
}

View File

@@ -0,0 +1,6 @@
namespace Orchard.Models.Driver {
public class NewModelContext {
public string ModelType { get; set; }
public IModel Instance { get; set; }
}
}

View File

@@ -0,0 +1,11 @@
namespace Orchard.Models {
public interface IModel {
int Id { get; }
string ModelType { get; }
bool Is<T>() where T : class, IModel;
T As<T>() where T : class, IModel;
void Weld(IModel model);
}
}

View File

@@ -0,0 +1,5 @@
namespace Orchard.Models {
public interface IModelManager {
IModel New(string modelType);
}
}

View File

@@ -0,0 +1,13 @@
using System;
namespace Orchard.Models {
public static class ModelExtensions {
public static T New<T>(this IModelManager manager, string modelType) where T : class, IModel {
var t = manager.New(modelType).As<T>();
if (t == null)
throw new InvalidCastException();
return t;
}
}
}

View File

@@ -0,0 +1,33 @@
using System;
namespace Orchard.Models {
public class ModelPart : IModel {
protected IModel Next { get; set; }
protected ModelRoot Root { get; set; }
void IModel.Weld(IModel model) {
Next = model;
Root = model.As<ModelRoot>();
Root.Welded = this;
}
public int Id { get { return Root.Id; } }
public string ModelType { get { return Root.ModelType; } }
bool IModel.Is<T>() {
return this is T ? true : Next.Is<T>();
}
T IModel.As<T>() {
return this is T ? this as T : Next.As<T>();
}
public bool Is<T>() where T : class, IModel {
return Root.WeldedIs<T>();
}
public T As<T>() where T : class, IModel {
return Root.WeldedAs<T>();
}
}
}

View File

@@ -0,0 +1,35 @@
using System;
namespace Orchard.Models {
public class ModelRoot : IModel {
public ModelRoot(string modelType) {
Welded = this;
ModelType = modelType;
}
public IModel Welded { get; set; }
public int Id { get; set; }
public string ModelType { get; set; }
bool IModel.Is<T>() {
return this is T;
}
T IModel.As<T>() {
return this as T;
}
public bool WeldedIs<T>() where T : class, IModel {
return Welded.Is<T>();
}
public T WeldedAs<T>() where T : class, IModel {
return Welded.As<T>();
}
void IModel.Weld(IModel model) {
// this method is not called on root
}
}
}

View File

@@ -122,6 +122,14 @@
<Compile Include="Logging\LoggingModule.cs" />
<Compile Include="Logging\NullLogger.cs" />
<Compile Include="Logging\NullLoggerFactory.cs" />
<Compile Include="Models\DefaultModelManager.cs" />
<Compile Include="Models\Driver\NewModelContext.cs" />
<Compile Include="Models\IModelManager.cs" />
<Compile Include="Models\Driver\IModelDriver.cs" />
<Compile Include="Models\IModel.cs" />
<Compile Include="Models\ModelExtensions.cs" />
<Compile Include="Models\ModelPart.cs" />
<Compile Include="Models\ModelRoot.cs" />
<Compile Include="Mvc\Html\HtmlHelperExtensions.cs" />
<Compile Include="Mvc\Filters\FilterProvider.cs" />
<Compile Include="Mvc\Filters\FilterResolvingActionInvoker.cs" />