From 3ac520fb169137f1c25c536f28d707cbacb14885 Mon Sep 17 00:00:00 2001 From: Sebastien Ros Date: Tue, 5 Feb 2013 10:36:38 -0800 Subject: [PATCH] Fixing NilBehavior --HG-- branch : 1.x --- .../DisplayManagement/CompositeTests.cs | 1 + .../DisplayManagement/NilTests.cs | 41 ++++++++++++++ .../DisplayManagement/ZoneHoldingTests.cs | 6 +- .../Orchard.Framework.Tests.csproj | 1 + .../DisplayManagement/Shapes/Composite.cs | 56 +++++++++++++++++-- src/Orchard/UI/Zones/ZoneHoldingBehavior.cs | 14 ++--- 6 files changed, 106 insertions(+), 13 deletions(-) create mode 100644 src/Orchard.Tests/DisplayManagement/NilTests.cs diff --git a/src/Orchard.Tests/DisplayManagement/CompositeTests.cs b/src/Orchard.Tests/DisplayManagement/CompositeTests.cs index 5fcba7567..cd767841a 100644 --- a/src/Orchard.Tests/DisplayManagement/CompositeTests.cs +++ b/src/Orchard.Tests/DisplayManagement/CompositeTests.cs @@ -52,6 +52,7 @@ namespace Orchard.Tests.DisplayManagement { Assert.That(foo.Bar, Is.EqualTo("bar")); Assert.That(foo.Bar == null, Is.False); } + } public class Animal : Composite { diff --git a/src/Orchard.Tests/DisplayManagement/NilTests.cs b/src/Orchard.Tests/DisplayManagement/NilTests.cs new file mode 100644 index 000000000..ed12a5362 --- /dev/null +++ b/src/Orchard.Tests/DisplayManagement/NilTests.cs @@ -0,0 +1,41 @@ +using NUnit.Framework; +using Orchard.DisplayManagement.Shapes; + +namespace Orchard.Tests.DisplayManagement { + [TestFixture] + public class NilTests { + + [Test] + public void NilShouldEqualToNull() { + var nil = Nil.Instance; + + Assert.That(nil == null, Is.True); + Assert.That(nil != null, Is.False); + + Assert.That(nil == Nil.Instance, Is.True); + Assert.That(nil != Nil.Instance, Is.False); + } + + [Test] + public void NilShouldBeRecursive() { + dynamic nil = Nil.Instance; + + Assert.That(nil == null, Is.True); + Assert.That(nil.Foo == null, Is.True); + Assert.That(nil.Foo.Bar == null, Is.True); + } + + + [Test] + public void CallingToStringOnNilShouldReturnEmpty() { + var nil = Nil.Instance; + Assert.That(nil.ToString(), Is.EqualTo("")); + } + + [Test] + public void CallingToStringOnDynamicNilShouldReturnEmpty() { + dynamic nil = Nil.Instance; + Assert.That(nil.Foo.Bar.ToString(), Is.EqualTo("")); + } + } +} diff --git a/src/Orchard.Tests/DisplayManagement/ZoneHoldingTests.cs b/src/Orchard.Tests/DisplayManagement/ZoneHoldingTests.cs index ac3c4bff3..a27969920 100644 --- a/src/Orchard.Tests/DisplayManagement/ZoneHoldingTests.cs +++ b/src/Orchard.Tests/DisplayManagement/ZoneHoldingTests.cs @@ -71,10 +71,14 @@ namespace Orchard.Tests.DisplayManagement { } [Test] - public void NoneEmptyZonesShouldBeNull() { + public void NoneEmptyZonesShouldNotBeNull() { Func factory = () => new Shape(); dynamic foo = new ZoneHolding(factory); + + Assert.That(foo.Header == null, Is.True); + Assert.That(foo.Header != null, Is.False); + foo.Header.Add("blah"); Assert.That(foo.Header == null, Is.False); diff --git a/src/Orchard.Tests/Orchard.Framework.Tests.csproj b/src/Orchard.Tests/Orchard.Framework.Tests.csproj index a2a640d29..ca4784e09 100644 --- a/src/Orchard.Tests/Orchard.Framework.Tests.csproj +++ b/src/Orchard.Tests/Orchard.Framework.Tests.csproj @@ -217,6 +217,7 @@ + diff --git a/src/Orchard/DisplayManagement/Shapes/Composite.cs b/src/Orchard/DisplayManagement/Shapes/Composite.cs index a25f3761a..e993603a4 100644 --- a/src/Orchard/DisplayManagement/Shapes/Composite.cs +++ b/src/Orchard/DisplayManagement/Shapes/Composite.cs @@ -12,7 +12,7 @@ namespace Orchard.DisplayManagement.Shapes { return TryGetMemberImpl(binder.Name, out result); } - protected bool TryGetMemberImpl(string name, out object result) { + protected virtual bool TryGetMemberImpl(string name, out object result) { if (_props.Contains(name)) { result = _props[name]; return true; @@ -94,14 +94,22 @@ namespace Orchard.DisplayManagement.Shapes { return true; } + public static bool operator ==(Composite a, Nil b) { + return ReferenceEquals(a, b) || null == a; + } + + public static bool operator !=(Composite a, Nil b) { + return !(a == b); + } + public IDictionary Properties { get { return _props; } } } public class Nil : DynamicObject { - static readonly object Singleton = new Nil(); - public static object Instance { get { return Singleton; } } + static readonly Nil Singleton = new Nil(); + public static Nil Instance { get { return Singleton; } } private Nil() { } @@ -116,22 +124,60 @@ namespace Orchard.DisplayManagement.Shapes { return true; } + public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { + result = Nil.Instance; + return true; + } + + public override bool TryBinaryOperation(BinaryOperationBinder binder, object arg, out object result) { switch (binder.Operation) { case ExpressionType.Equal: - result = ReferenceEquals(arg, Nil.Instance) || arg == null; + result = ReferenceEquals(arg, Nil.Instance) || (object)arg == null; return true; case ExpressionType.NotEqual: - result = !ReferenceEquals(arg, Nil.Instance) && arg != null; + result = !ReferenceEquals(arg, Nil.Instance) && (object)arg != null; return true; } return base.TryBinaryOperation(binder, arg, out result); } + public static bool operator ==(Nil a, Nil b) { + return true; + } + + public static bool operator !=(Nil a, Nil b) { + return false; + } + + public static bool operator ==(Nil a, object b) { + return ReferenceEquals(a, b) || (object)b == null; + } + + public static bool operator !=(Nil a, object b) { + return !(a == b); + } + + public override bool Equals(object obj) { + if (obj == null) { + return true; + } + + return ReferenceEquals(obj, Nil.Instance); + } + + public override int GetHashCode() { + return 0; + } + public override bool TryConvert(ConvertBinder binder, out object result) { result = null; return true; } + + public override string ToString() { + return string.Empty; + } } } diff --git a/src/Orchard/UI/Zones/ZoneHoldingBehavior.cs b/src/Orchard/UI/Zones/ZoneHoldingBehavior.cs index 0da38e11f..d1a6f9a50 100644 --- a/src/Orchard/UI/Zones/ZoneHoldingBehavior.cs +++ b/src/Orchard/UI/Zones/ZoneHoldingBehavior.cs @@ -38,7 +38,7 @@ namespace Orchard.UI.Zones { public override bool TryGetMember(System.Dynamic.GetMemberBinder binder, out object result) { var name = binder.Name; - if (!base.TryGetMember(binder, out result) || ((dynamic)result) == null) { + if (!base.TryGetMember(binder, out result) || (null == result)) { // substitute nil results with a robot that turns adds a zone on // the parent when .Add is invoked result = new ZoneOnDemand(_zoneFactory, this, name); @@ -67,7 +67,7 @@ namespace Orchard.UI.Zones { return TryGetMemberImpl(binder.Name, out result); } - private bool TryGetMemberImpl(string name, out object result) { + protected override bool TryGetMemberImpl(string name, out object result) { var parentMember = ((dynamic)_parent)[name]; if (parentMember == null) { @@ -139,18 +139,18 @@ namespace Orchard.UI.Zones { } public override bool TryConvert(System.Dynamic.ConvertBinder binder, out object result) { - result = null; + result = Nil.Instance; return true; } - public static bool operator ==(ZoneOnDemand expr, object arg) { + public static bool operator ==(ZoneOnDemand a, object b) { // if ZoneOnDemand is compared to null it must return true - return arg == null || ReferenceEquals(arg, Nil.Instance); + return b == null || ReferenceEquals(b, Nil.Instance); } - public static bool operator !=(ZoneOnDemand expr, object arg) { + public static bool operator !=(ZoneOnDemand a, object b) { // if ZoneOnDemand is compared to null it must return true - return arg != null && !ReferenceEquals(arg, Nil.Instance); + return !(a == b); } public override bool Equals(object obj) {