Before I start implementing this I want to show you some pseudo code
to get some early reactions. Apologies for the huge code blocks ...
There are (currently) three main interfaces that could be implemented
and provide the configuration
public interface IUrlActionScannerConvention
public interface IUrlActionBuilderConvention
public interface ISetupUrlActionConventions
The rest of the code is a short idea n how it could be implemented. I
am thinking about having my own semantic model to 'prepare' everything
and then create it in one go.
Please let me know what you guys think.
-Mark
---- Huge code block :) ----
namespace FubuMVC.Configuration.Conventional.Conventions
{
public interface IUrlActionScannerConvention
{
void Apply(IUrlActionScanner urlActionScanner);
}
public class AddsUrlActionsForPublicSite : IUrlActionScannerConvention
{
public void Apply(IUrlActionScanner urlActionScanner)
{
urlActionScanner
.AddFromAssembly<Object>()
.Where(x =>
x.Namespace.StartsWith("Some.NameSpace.UrlActions"));
}
}
public class AddsUrlActionsForAdminSite : IUrlActionScannerConvention
{
public void Apply(IUrlActionScanner urlActionScanner)
{
urlActionScanner
.AddFromAssembly<Object>()
.Where(x =>
x.Namespace.StartsWith("Some.NameSpace.Admin.UrlActions"));
}
}
}
namespace FubuMVC.Configuration.Conventional.SemanticModel
{
public interface IUrlActionScanner
{
void Add(Type urlActionType);
ITypeFilter AddFromAssembly<TTypeInAssembly>() where
TTypeInAssembly : class;
}
public class UrlActionScanner : IUrlActionScanner
{
public void Add(Type urlActionType)
{
// Add to Semantic Model
// Trigger the ISetupUrlActionConventions
// Trigger the IOverrideUrlActions
}
public ITypeFilter AddFromAssembly<TTypeInAssembly>() where
TTypeInAssembly : class
{
return new AddUrlActionTypeFilter<TTypeInAssembly>(this);
}
}
public class AddUrlActionTypeFilter<TTypeInAssembly> : ITypeFilter
{
private readonly Assembly _assembly;
private readonly IUrlActionScanner _urlActionScanner;
public AddUrlActionTypeFilter(IUrlActionScanner urlActionScanner)
{
_urlActionScanner = urlActionScanner;
_assembly = typeof(TTypeInAssembly).Assembly;
}
public void Where(Func<Type, bool> evalTypeFunction)
{
_assembly.GetExportedTypes().Each(type =>
{
if (type.IsAbstract) return;
if (type.IsValueType) return;
if (!type.IsInstanceOfType(typeof(UrlAction))) return;
if (evalTypeFunction(type)) _urlActionScanner.Add(type);
});
}
}
}
namespace FubuMVC.Configuration.Conventional.Conventions
{
public interface IUrlActionBuilderConvention
{
void Apply(IUrlActionBuilder urlActionBuilder);
}
public class CreateUrlActionsForPublicSite : IUrlActionBuilderConvention
{
public void Apply(IUrlActionBuilder urlActionBuilder)
{
urlActionBuilder
.AddFromAssembly<Object>()
.Where(x =>
x.Namespace.StartsWith("Some.NameSpace.UrlActions"));
}
}
public class CreateUrlActionsForAdminSite : IUrlActionBuilderConvention
{
public void Apply(IUrlActionBuilder urlActionBuilder)
{
urlActionBuilder
.AddFromAssembly<Object>()
.Where(x =>
x.Namespace.StartsWith("Some.NameSpace.Admin.UrlActions"));
}
}
}
namespace FubuMVC.Configuration.Conventional.SemanticModel
{
public interface IUrlActionBuilder
{
void Add(UrlAction urlAction);
ITypeFilter AddFromAssembly<TTypeInAssembly>() where
TTypeInAssembly : class;
Func<Type, UrlAction> UrlActionBuildFunction { get; set; }
string ViewToRender { get; set; }
void AddBehavior<TBehavior>();
}
public class UrlActionBuilder : IUrlActionBuilder
{
public Func<Type, UrlAction> UrlActionBuildFunction { get; set; }
public string ViewToRender { get; set; }
public UrlActionBuilder()
{
UrlActionBuildFunction = type => new UrlAction{
PrimaryUrlStub = type.Name };
}
public void Add(UrlAction urlAction)
{
// Add to Semantic Model
// Trigger the ISetupUrlActionConventions
// Trigger the IOverrideUrlActions
}
public ITypeFilter AddFromAssembly<TTypeInAssembly>() where
TTypeInAssembly : class
{
return new BuildUrlActionTypeFilter<TTypeInAssembly>(this);
}
public void AddBehavior<TBehavior>()
{
}
}
public class BuildUrlActionTypeFilter<TTypeInAssembly> : ITypeFilter
{
private readonly Assembly _assembly;
private readonly IUrlActionBuilder _urlActionBuilder;
public BuildUrlActionTypeFilter(IUrlActionBuilder urlActionBuilder)
{
_urlActionBuilder = urlActionBuilder;
_assembly = typeof(TTypeInAssembly).Assembly;
}
public void Where(Func<Type, bool> evalTypeFunction)
{
_assembly.GetExportedTypes().Each(type =>
{
if (type.IsAbstract) return;
if (type.IsValueType) return;
if (!type.IsInstanceOfType(typeof(UrlAction))) return;
if (!evalTypeFunction(type)) return;
_urlActionBuilder.Add(_urlActionBuilder.UrlActionBuildFunction(type));
});
}
}
}
namespace FubuMVC.Configuration.Conventional.SemanticModel
{
public interface ITypeFilter
{
void Where(Func<Type, bool> evalTypeFunction);
}
}
namespace FubuMVC.Configuration.Conventional.Conventions
{
public interface ISetupUrlActionConventions
{
void Apply(Type urlActionOrControllerType, IUrlActionBuilder
urlActionBuilder);
}
public class SetupUrlActionsForPublicSite : ISetupUrlActionConventions
{
public void Apply(Type urlActionOrControllerType,
IUrlActionBuilder urlActionBuilder)
{
if (!urlActionOrControllerType.Namespace.StartsWith("Some.NameSpace.UrlActions"))
return;
urlActionBuilder.ViewToRender =
"~/Views/{0}/index".ToFormat(urlActionOrControllerType.Name);
urlActionBuilder.AddBehavior<Object>();
}
}
public class SetupUrlActionsForAdminSite : ISetupUrlActionConventions
{
public void Apply(Type urlActionOrControllerType,
IUrlActionBuilder urlActionBuilder)
{
if (!urlActionOrControllerType.Namespace.StartsWith("Some.NameSpace.Admin.UrlActions"))
return;
urlActionBuilder.ViewToRender =
"~/Views/admin/{0}/index".ToFormat(urlActionOrControllerType.Name);
urlActionBuilder.AddBehavior<Object>();
urlActionBuilder.AddBehavior<Object>();
}
}
}
On other thing currently there are only three conventions but I am
thinking that this will expand quit a bit, and also that you could
have three different implementations of the same convention
configuring different elements of your actions. F.ex. one configures
the Views and an other one configures the behaviors that need to be
applied. And you may have noticed that there is not IUrlActionOverride
interface yet. This one is a little bit tricky for when you use the
IUrlActionBuilderConvention because I don't know yet how to identify
one UrlAction except by its Url I guess, but that seems a little bit
flacky.
Also currently all I am thinking on running all conventions for each
UrlAction that gets added, I am thinking that this could perhaps be
more elegantly done after the adding of UrlActions but then again I
need a good way of identifying the UrlAction. So what would you guys
think of adding the type and when available the method information to
the UrlAction that where used to build the UrlAction? F.ex. A
controller with multiple actions would result in multiple UrlActions
all containing the controller type and particular method, if it is
only a UrlAction that determined this than type would be itself
without a method information. This way I can split up the scanning of
the UrlActions from setting them up, but still use the same type based
conventions to determine if and how to apply the setup conventions.
Make sense?
-Mark