From e0f10275b20967267f6b7a3e28d224c8e2852887 Mon Sep 17 00:00:00 2001 From: Louis DeJardin Date: Thu, 10 Feb 2011 12:32:28 -0800 Subject: [PATCH] Adding support for dynamic content expressions ContentItem as dynamic will respond to get property which matches a ContentPart's metadata definition name ContentPart as dynamic will respond to get property which matches a ContentField's metadata instance name --HG-- branch : dev extra : transplant_source : Qu0%9E%09%9B%97rbq%EE%9B%1Ax3%C8p%ED%91%3E --- .../DynamicContentItemTests.cs | 80 +++++++++++++++++++ .../Orchard.Framework.Tests.csproj | 1 + src/Orchard/ContentManagement/ContentItem.cs | 17 +++- .../ContentManagement/ContentItemBehavior.cs | 21 +++++ src/Orchard/ContentManagement/ContentPart.cs | 17 +++- .../ContentManagement/ContentPartBehavior.cs | 20 +++++ .../ContentManagement/IContentBehavior.cs | 7 ++ src/Orchard/Orchard.Framework.csproj | 3 + 8 files changed, 164 insertions(+), 2 deletions(-) create mode 100644 src/Orchard.Tests/ContentManagement/DynamicContentItemTests.cs create mode 100644 src/Orchard/ContentManagement/ContentItemBehavior.cs create mode 100644 src/Orchard/ContentManagement/ContentPartBehavior.cs create mode 100644 src/Orchard/ContentManagement/IContentBehavior.cs diff --git a/src/Orchard.Tests/ContentManagement/DynamicContentItemTests.cs b/src/Orchard.Tests/ContentManagement/DynamicContentItemTests.cs new file mode 100644 index 000000000..ba90a629e --- /dev/null +++ b/src/Orchard.Tests/ContentManagement/DynamicContentItemTests.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using Orchard.ContentManagement; +using Orchard.ContentManagement.MetaData.Models; + +namespace Orchard.Tests.ContentManagement { + [TestFixture] + public class DynamicContentItemTests { + + [Test] + public void ContentItemProjectsPartNamesAsProperties() { + var contentItem = new ContentItem(); + var testingPart = new ContentPart { TypePartDefinition = new ContentTypePartDefinition(new ContentPartDefinition("TestingPart"), new SettingsDictionary()) }; + contentItem.Weld(testingPart); + + dynamic contentItemDynamic = contentItem; + dynamic testingPartDynamic = contentItemDynamic.TestingPart; + + Assert.That((object)testingPartDynamic, Is.SameAs(testingPart)); + } + + + [Test] + public void ContentPartsAlsoProjectPartNamesAsProperties() { + var contentItem = new ContentItem(); + var testingPart = new ContentPart { TypePartDefinition = new ContentTypePartDefinition(new ContentPartDefinition("TestingPart"), new SettingsDictionary()) }; + var anotherPart = new ContentPart { TypePartDefinition = new ContentTypePartDefinition(new ContentPartDefinition("AnotherPart"), new SettingsDictionary()) }; + contentItem.Weld(testingPart); + contentItem.Weld(anotherPart); + + dynamic contentItemDynamic = contentItem; + dynamic testingPartDynamic = contentItemDynamic.TestingPart; + dynamic anotherPartDynamic = contentItemDynamic.AnotherPart; + dynamic testingPartDynamic2 = testingPartDynamic.TestingPart; + dynamic anotherPartDynamic2 = testingPartDynamic.AnotherPart; + + Assert.That((object)testingPartDynamic, Is.SameAs(testingPart)); + Assert.That((object)anotherPartDynamic, Is.SameAs(anotherPart)); + Assert.That((object)testingPartDynamic2, Is.SameAs(testingPart)); + Assert.That((object)anotherPartDynamic2, Is.SameAs(anotherPart)); + } + + + [Test] + public void ContentItemPropertyOnPartRootsYou() { + var contentItem = new ContentItem(); + var testingPart = new ContentPart { TypePartDefinition = new ContentTypePartDefinition(new ContentPartDefinition("TestingPart"), new SettingsDictionary()) }; + var anotherPart = new ContentPart { TypePartDefinition = new ContentTypePartDefinition(new ContentPartDefinition("AnotherPart"), new SettingsDictionary()) }; + contentItem.Weld(testingPart); + contentItem.Weld(anotherPart); + + dynamic contentItemDynamic = contentItem; + dynamic testingPartDynamic = contentItemDynamic.TestingPart; + dynamic anotherPartDynamic = contentItemDynamic.AnotherPart; + + dynamic contentItemDynamic1 = testingPartDynamic.ContentItem; + dynamic contentItemDynamic2 = anotherPartDynamic.ContentItem; + + Assert.That((object)contentItemDynamic1, Is.SameAs(contentItem)); + Assert.That((object)contentItemDynamic2, Is.SameAs(contentItem)); + } + + + [Test] + public void ActualPropertiesTakePriority() { + var contentItem = new ContentItem(); + var testingPart = new ContentPart { TypePartDefinition = new ContentTypePartDefinition(new ContentPartDefinition("Parts"), new SettingsDictionary()) }; + contentItem.Weld(testingPart); + + dynamic contentItemDynamic = contentItem; + dynamic testingPartDynamic = contentItemDynamic.Parts; + + + Assert.That((object)testingPartDynamic, Is.AssignableTo>()); + } + } +} diff --git a/src/Orchard.Tests/Orchard.Framework.Tests.csproj b/src/Orchard.Tests/Orchard.Framework.Tests.csproj index bee36fa25..7c7b052db 100644 --- a/src/Orchard.Tests/Orchard.Framework.Tests.csproj +++ b/src/Orchard.Tests/Orchard.Framework.Tests.csproj @@ -173,6 +173,7 @@ + diff --git a/src/Orchard/ContentManagement/ContentItem.cs b/src/Orchard/ContentManagement/ContentItem.cs index b34545a2d..22a6efa43 100644 --- a/src/Orchard/ContentManagement/ContentItem.cs +++ b/src/Orchard/ContentManagement/ContentItem.cs @@ -1,16 +1,21 @@ using System; using System.Collections.Generic; +using System.Dynamic; using System.Linq; +using System.Linq.Expressions; +using ClaySharp; using Orchard.ContentManagement.MetaData.Models; using Orchard.ContentManagement.Records; namespace Orchard.ContentManagement { - public class ContentItem : IContent { + public class ContentItem : IContent, IContentBehavior, IDynamicMetaObjectProvider { public ContentItem() { + _behavior = new ClayBehaviorCollection(new[] { new ContentItemBehavior(this) }); _parts = new List(); } private readonly IList _parts; + ContentItem IContent.ContentItem { get { return this; } } public int Id { get { return Record == null ? 0 : Record.Id; } } @@ -39,5 +44,15 @@ namespace Orchard.ContentManagement { part.ContentItem = this; _parts.Add(part); } + + + private readonly IClayBehavior _behavior; + IClayBehavior IContentBehavior.Behavior { + get { return _behavior; } + } + DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(Expression parameter) { + return new ClayMetaObject(this, parameter, ex => Expression.Property(Expression.Convert(ex, typeof(IContentBehavior)), "Behavior")); + } + } } \ No newline at end of file diff --git a/src/Orchard/ContentManagement/ContentItemBehavior.cs b/src/Orchard/ContentManagement/ContentItemBehavior.cs new file mode 100644 index 000000000..d5d91d7ad --- /dev/null +++ b/src/Orchard/ContentManagement/ContentItemBehavior.cs @@ -0,0 +1,21 @@ +using System; +using ClaySharp; + +namespace Orchard.ContentManagement { + public class ContentItemBehavior : ClayBehavior { + private readonly IContent _content; + + public ContentItemBehavior(IContent content) { + _content = content; + } + + public override object GetMemberMissing(Func proceed, object self, string name) { + var contentItem = _content.ContentItem; + foreach (var part in contentItem.Parts) { + if (part.PartDefinition.Name == name) + return part; + } + return base.GetMemberMissing(proceed, self, name); + } + } +} diff --git a/src/Orchard/ContentManagement/ContentPart.cs b/src/Orchard/ContentManagement/ContentPart.cs index 1d7133754..32acc9464 100644 --- a/src/Orchard/ContentManagement/ContentPart.cs +++ b/src/Orchard/ContentManagement/ContentPart.cs @@ -1,19 +1,24 @@ using System; using System.Collections.Generic; +using System.Dynamic; using System.Linq; +using System.Linq.Expressions; using System.Web.Mvc; +using ClaySharp; using Orchard.ContentManagement.MetaData.Models; using Orchard.ContentManagement.Utilities; using Orchard.UI; namespace Orchard.ContentManagement { - public class ContentPart : IContent { + public class ContentPart : IContent, IContentBehavior, IDynamicMetaObjectProvider { private readonly IList _fields; public ContentPart() { + _behavior = new ClayBehaviorCollection(new[] { new ContentPartBehavior(this) }); _fields = new List(); } + public virtual ContentItem ContentItem { get; set; } //interesting thought, should/could parts also have zones (would then have zones on the page, content item and parts...)? @@ -24,6 +29,14 @@ namespace Orchard.ContentManagement { } } + private readonly IClayBehavior _behavior; + IClayBehavior IContentBehavior.Behavior { + get { return _behavior; } + } + DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(Expression parameter) { + return new ClayMetaObject(this, parameter, ex => Expression.Property(Expression.Convert(ex, typeof(IContentBehavior)), "Behavior")); + } + /// /// The ContentItem's identifier. /// @@ -51,6 +64,8 @@ namespace Orchard.ContentManagement { public void Weld(ContentField field) { _fields.Add(field); } + + } public class ContentPart : ContentPart { diff --git a/src/Orchard/ContentManagement/ContentPartBehavior.cs b/src/Orchard/ContentManagement/ContentPartBehavior.cs new file mode 100644 index 000000000..ece924a64 --- /dev/null +++ b/src/Orchard/ContentManagement/ContentPartBehavior.cs @@ -0,0 +1,20 @@ +using System; + +namespace Orchard.ContentManagement { + public class ContentPartBehavior : ContentItemBehavior { + private readonly ContentPart _contentPart; + + public ContentPartBehavior(ContentPart contentPart) + : base(contentPart) { + _contentPart = contentPart; + } + + public override object GetMemberMissing(Func proceed, object self, string name) { + foreach (var field in _contentPart.Fields) { + if (field.PartFieldDefinition.Name == name) + return field; + } + return base.GetMemberMissing(proceed, self, name); + } + } +} \ No newline at end of file diff --git a/src/Orchard/ContentManagement/IContentBehavior.cs b/src/Orchard/ContentManagement/IContentBehavior.cs new file mode 100644 index 000000000..6d01fe4c8 --- /dev/null +++ b/src/Orchard/ContentManagement/IContentBehavior.cs @@ -0,0 +1,7 @@ +using ClaySharp; + +namespace Orchard.ContentManagement { + public interface IContentBehavior { + IClayBehavior Behavior { get; } + } +} \ No newline at end of file diff --git a/src/Orchard/Orchard.Framework.csproj b/src/Orchard/Orchard.Framework.csproj index 8906c8d52..813f85198 100644 --- a/src/Orchard/Orchard.Framework.csproj +++ b/src/Orchard/Orchard.Framework.csproj @@ -150,9 +150,12 @@ + + +