PropertyValueConverter - cleanup WIP
This commit is contained in:
@@ -16,15 +16,6 @@ namespace Umbraco.Tests.PublishedContent
|
||||
[TestFixture]
|
||||
public class ModelsAndConvertersTests
|
||||
{
|
||||
// fixme
|
||||
// naming: IPublishedProperty is IPropertySetProperty or IFacadeProperty of some sort
|
||||
// naming: general naming sucks at the moment
|
||||
// caching: re-think how properties are cached
|
||||
// - for true NuCache content it probably is OK (but needs explanation)
|
||||
// - for true Xml cache content it probably is OK (but needs explanation)
|
||||
// - for pure sets, I have no idea - should at least cache at content level?
|
||||
// hold on - PropertySetPropertyBase probably handles it - need to check
|
||||
|
||||
#region ModelType
|
||||
|
||||
[Test]
|
||||
@@ -417,7 +408,9 @@ namespace Umbraco.Tests.PublishedContent
|
||||
.Setup(x => x.CreateSetProperty(It.IsAny<PublishedPropertyType>(), It.IsAny<IPropertySet>(), It.IsAny<bool>(), It.IsAny<PropertyCacheLevel>(), It.IsAny<object>()))
|
||||
.Returns<PublishedPropertyType, IPropertySet, bool, PropertyCacheLevel, object>((propertyType, set, previewing, refCacheLevel, value) =>
|
||||
{
|
||||
// ReSharper disable AccessToModifiedClosure
|
||||
return new TestPropertySetProperty(propertyType, set, previewing, refCacheLevel, value, () => snapshotCache, () => facadeCache);
|
||||
// ReSharper restore AccessToModifiedClosure
|
||||
});
|
||||
var facadeService = facadeServiceMock.Object;
|
||||
|
||||
@@ -449,6 +442,7 @@ namespace Umbraco.Tests.PublishedContent
|
||||
|
||||
Assert.AreEqual(snapshotCount2, snapshotCache.Count);
|
||||
Assert.AreEqual(facadeCount2, facadeCache.Count);
|
||||
Assert.AreEqual(facadeCount2, oldFacadeCache.Count);
|
||||
|
||||
Assert.AreEqual((interConverts == 1 ? 1 : 3) + facadeCache.Count, converter.InterConverts);
|
||||
|
||||
@@ -459,6 +453,7 @@ namespace Umbraco.Tests.PublishedContent
|
||||
Assert.AreEqual(1, converter.SourceConverts);
|
||||
|
||||
Assert.AreEqual(snapshotCount2, snapshotCache.Count);
|
||||
Assert.AreEqual(snapshotCount2, oldSnapshotCache.Count);
|
||||
Assert.AreEqual(facadeCount2, facadeCache.Count);
|
||||
|
||||
Assert.AreEqual((interConverts == 1 ? 1 : 4) + facadeCache.Count + snapshotCache.Count, converter.InterConverts);
|
||||
@@ -481,7 +476,7 @@ namespace Umbraco.Tests.PublishedContent
|
||||
|
||||
Assert.Throws<Exception>(() =>
|
||||
{
|
||||
var set1 = new PropertySet(setType1, Guid.NewGuid(), new Dictionary<string, object> { { "prop1", "1234" } }, false);
|
||||
var unused = new PropertySet(setType1, Guid.NewGuid(), new Dictionary<string, object> { { "prop1", "1234" } }, false);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Umbraco.Tests.PublishedContent.StronglyTypedModels
|
||||
{
|
||||
[TestFixture]
|
||||
public class CallingMethodTests
|
||||
{
|
||||
private readonly Func<MethodBase> _myProperty = MethodBase.GetCurrentMethod;
|
||||
|
||||
public string AField
|
||||
{
|
||||
// that attribute is REQUIRED for the test to work in RELEASE mode
|
||||
// and then it kills performance, probably, so that whole way of
|
||||
// doing things is probably a Bad Thing.
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
get { return Resolve(_myProperty()); }
|
||||
}
|
||||
|
||||
public string AField2
|
||||
{
|
||||
get { return Resolve2(); }
|
||||
}
|
||||
|
||||
private string Resolve(MethodBase m)
|
||||
{
|
||||
return m.Name.Replace("get_", "");
|
||||
}
|
||||
|
||||
// that would be the correct way of doing it, works in RELEASE mode
|
||||
// as well as DEBUG and is optimized, etc
|
||||
public string Resolve2([CallerMemberName] string memberName = null)
|
||||
{
|
||||
return memberName;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetMyName()
|
||||
{
|
||||
Assert.AreEqual("AField", AField);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetMyName2()
|
||||
{
|
||||
Assert.AreEqual("AField2", AField2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
|
||||
namespace Umbraco.Tests.PublishedContent.StronglyTypedModels
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Subpage which acts as the strongly typed model for a Doc Type
|
||||
/// with alias "Subpage" and "Subpage" as the allowed child type.
|
||||
///
|
||||
/// Similar to the Textpage this model could also be generated, but it could also
|
||||
/// act as a Code-First model by using the attributes shown on the various properties,
|
||||
/// which decorate the model with information about the Document Type, its
|
||||
/// Property Groups and Property Types.
|
||||
/// </summary>
|
||||
public class Subpage : TypedModelBase
|
||||
{
|
||||
public Subpage(IPublishedContent publishedContent) : base(publishedContent)
|
||||
{
|
||||
}
|
||||
|
||||
public string Title { get { return Resolve<string>(Property()); } }
|
||||
|
||||
public string BodyText { get { return Resolve<string>(Property()); } }
|
||||
|
||||
public new Textpage Parent
|
||||
{
|
||||
get
|
||||
{
|
||||
return Parent<Textpage>();
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<Subpage> Subpages
|
||||
{
|
||||
get
|
||||
{
|
||||
return Children<Subpage>(ContentTypeAlias());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
|
||||
namespace Umbraco.Tests.PublishedContent.StronglyTypedModels
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Textpage which acts as the strongly typed model for a Doc Type
|
||||
/// with alias "Textpage" and "Subpage" as the allowed child type.
|
||||
///
|
||||
/// The basic properties are resolved by convention using the Resolve-Type-PropertyTypeAlias
|
||||
/// convention available through the base class' protected Resolve-method and Property-delegate.
|
||||
///
|
||||
/// The Textpage allows the use of Subpage and Textpage as child doc types, which are exposed as a
|
||||
/// collection using the Children-Type-ContentTypeAlias convention available through the
|
||||
/// base class' protected Children-method and ContentTypeAlias-delegate.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This code can easily be generated using simple conventions for the types and names
|
||||
/// of the properties that this type of strongly typed model exposes.
|
||||
/// </remarks>
|
||||
public class Textpage : TypedModelBase
|
||||
{
|
||||
public Textpage(IPublishedContent publishedContent) : base(publishedContent)
|
||||
{
|
||||
}
|
||||
|
||||
public string Title { get { return Resolve<string>(Property()); } }
|
||||
|
||||
public string BodyText { get { return Resolve<string>(Property()); } }
|
||||
|
||||
public string AuthorName { get { return Resolve<string>(Property()); } }
|
||||
|
||||
public DateTime Date { get { return Resolve<DateTime>(Property()); } }
|
||||
|
||||
public new Textpage Parent
|
||||
{
|
||||
get
|
||||
{
|
||||
return Parent<Textpage>();
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<Textpage> Textpages
|
||||
{
|
||||
get
|
||||
{
|
||||
return Children<Textpage>(ContentTypeAlias());
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<Subpage> Subpages
|
||||
{
|
||||
get
|
||||
{
|
||||
return Children<Subpage>(ContentTypeAlias());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Entity.Design.PluralizationServices;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Linq;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
using Umbraco.Web;
|
||||
|
||||
namespace Umbraco.Tests.PublishedContent.StronglyTypedModels
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the abstract base class for a 'TypedModel', which basically wraps IPublishedContent
|
||||
/// underneath a strongly typed model like "Textpage" and "Subpage".
|
||||
/// Because IPublishedContent is used under the hood there is no need for additional mapping, so the
|
||||
/// only added cost should be the creation of the objects, which the IPublishedContent instance is
|
||||
/// passed into.
|
||||
///
|
||||
/// This base class exposes a simple way to write property getters by convention without
|
||||
/// using the string alias of a PropertyType (this is resolved by the use of the Property delegate).
|
||||
///
|
||||
/// This base class also exposes query options like Parent, Children, Ancestors and Descendants,
|
||||
/// which can be used for collections of strongly typed child models/objects. These types of collections
|
||||
/// typically corresponds to 'allowed child content types' on a Doc Type (at different levels).
|
||||
///
|
||||
/// The IPublishedContent properties are also exposed through this base class, but only
|
||||
/// by casting the typed model to IPublishedContent, so the properties doesn't show up by default:
|
||||
/// ie. ((IPublishedContent)textpage).Url
|
||||
/// </summary>
|
||||
public abstract class TypedModelBase : PublishedContentWrapped // IPublishedContent
|
||||
{
|
||||
protected TypedModelBase(IPublishedContent publishedContent)
|
||||
: base(publishedContent)
|
||||
{ }
|
||||
|
||||
protected readonly Func<MethodBase> Property = MethodBase.GetCurrentMethod;
|
||||
protected readonly Func<MethodBase> ContentTypeAlias = MethodBase.GetCurrentMethod;
|
||||
|
||||
#region Properties
|
||||
|
||||
protected T Resolve<T>(MethodBase methodBase)
|
||||
{
|
||||
var propertyTypeAlias = methodBase.ToUmbracoAlias();
|
||||
return Resolve<T>(propertyTypeAlias);
|
||||
}
|
||||
|
||||
protected T Resolve<T>(string propertyTypeAlias)
|
||||
{
|
||||
return Content.Value<T>(propertyTypeAlias);
|
||||
}
|
||||
|
||||
protected T Resolve<T>(MethodBase methodBase, T ifCannotConvert)
|
||||
{
|
||||
var propertyTypeAlias = methodBase.ToUmbracoAlias();
|
||||
return Resolve<T>(propertyTypeAlias, ifCannotConvert);
|
||||
}
|
||||
|
||||
protected T Resolve<T>(string propertyTypeAlias, T ifCannotConvert)
|
||||
{
|
||||
return Content.Value<T>(propertyTypeAlias, false, ifCannotConvert);
|
||||
}
|
||||
|
||||
protected T Resolve<T>(MethodBase methodBase, bool recursive, T ifCannotConvert)
|
||||
{
|
||||
var propertyTypeAlias = methodBase.ToUmbracoAlias();
|
||||
return Resolve<T>(propertyTypeAlias, recursive, ifCannotConvert);
|
||||
}
|
||||
|
||||
protected T Resolve<T>(string propertyTypeAlias, bool recursive, T ifCannotConvert)
|
||||
{
|
||||
return Content.Value<T>(propertyTypeAlias, recursive, ifCannotConvert);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Querying
|
||||
protected T Parent<T>() where T : TypedModelBase
|
||||
{
|
||||
var constructorInfo = typeof(T).GetConstructor(new[] { typeof(IPublishedContent) });
|
||||
if (constructorInfo == null)
|
||||
throw new Exception("No valid constructor found");
|
||||
|
||||
return (T) constructorInfo.Invoke(new object[] {Content.Parent});
|
||||
}
|
||||
|
||||
protected IEnumerable<T> Children<T>(MethodBase methodBase) where T : TypedModelBase
|
||||
{
|
||||
var docTypeAlias = methodBase.CleanCallingMethodName();
|
||||
return Children<T>(docTypeAlias);
|
||||
}
|
||||
|
||||
protected IEnumerable<T> Children<T>(string docTypeAlias) where T : TypedModelBase
|
||||
{
|
||||
var constructorInfo = typeof(T).GetConstructor(new[] { typeof(IPublishedContent) });
|
||||
if(constructorInfo == null)
|
||||
throw new Exception("No valid constructor found");
|
||||
|
||||
string singularizedDocTypeAlias = docTypeAlias.ToSingular();
|
||||
|
||||
return Content.Children.Where(x => x.DocumentTypeAlias == singularizedDocTypeAlias)
|
||||
.Select(x => (T)constructorInfo.Invoke(new object[] { x }));
|
||||
}
|
||||
|
||||
protected IEnumerable<T> Ancestors<T>(MethodBase methodBase) where T : TypedModelBase
|
||||
{
|
||||
var docTypeAlias = methodBase.CleanCallingMethodName();
|
||||
return Ancestors<T>(docTypeAlias);
|
||||
}
|
||||
|
||||
protected IEnumerable<T> Ancestors<T>(string docTypeAlias) where T : TypedModelBase
|
||||
{
|
||||
var constructorInfo = typeof(T).GetConstructor(new[] { typeof(IPublishedContent) });
|
||||
if (constructorInfo == null)
|
||||
throw new Exception("No valid constructor found");
|
||||
|
||||
string singularizedDocTypeAlias = docTypeAlias.ToSingular();
|
||||
|
||||
return Content.Ancestors().Where(x => x.DocumentTypeAlias == singularizedDocTypeAlias)
|
||||
.Select(x => (T)constructorInfo.Invoke(new object[] { x }));
|
||||
}
|
||||
|
||||
protected IEnumerable<T> Descendants<T>(MethodBase methodBase) where T : TypedModelBase
|
||||
{
|
||||
var docTypeAlias = methodBase.CleanCallingMethodName();
|
||||
return Descendants<T>(docTypeAlias);
|
||||
}
|
||||
|
||||
protected IEnumerable<T> Descendants<T>(string docTypeAlias) where T : TypedModelBase
|
||||
{
|
||||
var constructorInfo = typeof(T).GetConstructor(new[] { typeof(IPublishedContent) });
|
||||
if (constructorInfo == null)
|
||||
throw new Exception("No valid constructor found");
|
||||
|
||||
string singularizedDocTypeAlias = docTypeAlias.ToSingular();
|
||||
|
||||
return Content.Descendants().Where(x => x.DocumentTypeAlias == singularizedDocTypeAlias)
|
||||
.Select(x => (T)constructorInfo.Invoke(new object[] { x }));
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for MethodBase, which are used to clean the name of the calling method "get_BodyText"
|
||||
/// to "BodyText" and then make it camel case according to the UmbracoAlias convention "bodyText".
|
||||
/// There is also a string extension for making plural words singular, which is used when going from
|
||||
/// something like "Subpages" to "Subpage" for Children/Ancestors/Descendants Doc Type aliases.
|
||||
/// </summary>
|
||||
public static class TypeExtensions
|
||||
{
|
||||
public static string CleanCallingMethodName(this MethodBase methodBase)
|
||||
{
|
||||
return methodBase.Name.Replace("get_", "");
|
||||
}
|
||||
|
||||
public static string ToUmbracoAlias(this MethodBase methodBase)
|
||||
{
|
||||
return methodBase.CleanCallingMethodName().ToSafeAlias();
|
||||
}
|
||||
|
||||
public static string ToSingular(this string pluralWord)
|
||||
{
|
||||
var service = PluralizationService.CreateService(new CultureInfo("en-US"));
|
||||
if (service.IsPlural(pluralWord))
|
||||
return service.Singularize(pluralWord);
|
||||
|
||||
return pluralWord;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -374,10 +374,6 @@
|
||||
<Compile Include="PropertyEditors\PropertyEditorValueEditorTests.cs" />
|
||||
<Compile Include="PublishedContent\PublishedContentRequestEngineTests.cs" />
|
||||
<Compile Include="PublishedContent\RootNodeTests.cs" />
|
||||
<Compile Include="PublishedContent\StronglyTypedModels\CallingMethodTests.cs" />
|
||||
<Compile Include="PublishedContent\StronglyTypedModels\Subpage.cs" />
|
||||
<Compile Include="PublishedContent\StronglyTypedModels\Textpage.cs" />
|
||||
<Compile Include="PublishedContent\StronglyTypedModels\TypedModelBase.cs" />
|
||||
<Compile Include="Scheduling\BackgroundTaskRunnerTests.cs" />
|
||||
<Compile Include="Misc\ApplicationUrlHelperTests.cs" />
|
||||
<Compile Include="Services\FileServiceTests.cs" />
|
||||
|
||||
Reference in New Issue
Block a user