From 90a696d96520e2285cbc58b96980ab66bd21020e Mon Sep 17 00:00:00 2001 From: Gordon MacKenzie-Leigh Date: Tue, 12 Jun 2012 09:05:43 +0100 Subject: [PATCH 01/21] Added fix to allow proper rendering of multi-level include hierarchies Now supports internal template definitions and uses across multiple files --- .../Describe_Template_Render.cs | 38 +++++++++++++++++++ Nustache.Core/RenderContext.cs | 4 ++ 2 files changed, 42 insertions(+) diff --git a/Nustache.Core.Tests/Describe_Template_Render.cs b/Nustache.Core.Tests/Describe_Template_Render.cs index fc9185c..719a00d 100644 --- a/Nustache.Core.Tests/Describe_Template_Render.cs +++ b/Nustache.Core.Tests/Describe_Template_Render.cs @@ -1,5 +1,7 @@ using System.IO; using NUnit.Framework; +using System.Collections.Generic; +using System; namespace Nustache.Core.Tests { @@ -182,6 +184,42 @@ public void It_can_include_templates_defined_in_outer_templates() Assert.AreEqual("beforeOUTSIDEafter", result); } + [Test] + public void It_can_include_templates_over_three_levels() + { + var result = Render.StringToString("{{t1}}Two{{/t2}}{{t2}}Three{{/t3}}{{>t3}}", null); + + Assert.AreEqual("OneTwoThree", result); + } + + + [Test] + public void It_can_include_templates_over_three_levels_with_external_includes() + { + var baseTemplate = new Template("Base"); + baseTemplate.Load(new StringReader("Base{{>BaseContent}}")); + + var masterTemplate = new Template("Master"); + masterTemplate.Load(new StringReader("{{MasterContent}}{{/BaseContent}}{{>Base}}")); + + var templates = new Dictionary(); + templates.Add("Base", baseTemplate); + templates.Add("Master", masterTemplate); + + TemplateLocator locateTemplate = + name => + { + Template ret; + templates.TryGetValue(name, out ret); + if (ret == null) throw new KeyNotFoundException(string.Format("The view '{0}' could not be found.", name)); + return ret; + }; + + var result = Render.StringToString("{{Master}}", null, locateTemplate); + + Assert.AreEqual("BaseMasterHello", result); + } + [Test] public void It_allows_templates_to_be_overridden_in_sections() { diff --git a/Nustache.Core/RenderContext.cs b/Nustache.Core/RenderContext.cs index 0a6bb34..4e4c271 100644 --- a/Nustache.Core/RenderContext.cs +++ b/Nustache.Core/RenderContext.cs @@ -133,7 +133,11 @@ public void Include(string templateName) if (template != null) { + // push the included template on the stack so that internally defined templates can be resolved properly later. + // designed to pass test Describe_Template_Render.It_can_include_templates_over_three_levels_with_external_includes() + this.Push(template, null); template.Render(this); + this.Pop(); } } From ab26cf4f8d51eaed1e862b0a107aa1a4323f9779 Mon Sep 17 00:00:00 2001 From: dhgoldman Date: Mon, 7 Oct 2013 13:42:56 -0500 Subject: [PATCH 02/21] Take first matching property from list of properties If an object has both generic and non-generic versions of the same property, they will have the same property name, causing an AmbiguousMatchException to be thrown. To prevent this, iterate all properties matching the binding flag and assign the first property with the specified name. --- Nustache.Core/ValueGetterFactory.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Nustache.Core/ValueGetterFactory.cs b/Nustache.Core/ValueGetterFactory.cs index a21b7fb..f52ede2 100644 --- a/Nustache.Core/ValueGetterFactory.cs +++ b/Nustache.Core/ValueGetterFactory.cs @@ -157,7 +157,15 @@ internal class PropertyInfoValueGetterFactory : ValueGetterFactory { public override ValueGetter GetValueGetter(object target, Type targetType, string name) { - PropertyInfo property = targetType.GetProperty(name, DefaultBindingFlags); + PropertyInfo property = null; + foreach (var p in targetType.GetProperties(DefaultBindingFlags)) + { + if (p.Name.Equals(name)) + { + property = p; + break; + } + } if (property != null && PropertyCanGetValue(property)) { From 6719d65a6be5c8571f78f358af80dc07d957845a Mon Sep 17 00:00:00 2001 From: dhgoldman Date: Mon, 7 Oct 2013 13:46:20 -0500 Subject: [PATCH 03/21] Case-insensitive name comparisons --- Nustache.Core/ValueGetterFactory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nustache.Core/ValueGetterFactory.cs b/Nustache.Core/ValueGetterFactory.cs index f52ede2..0277888 100644 --- a/Nustache.Core/ValueGetterFactory.cs +++ b/Nustache.Core/ValueGetterFactory.cs @@ -160,7 +160,7 @@ public override ValueGetter GetValueGetter(object target, Type targetType, strin PropertyInfo property = null; foreach (var p in targetType.GetProperties(DefaultBindingFlags)) { - if (p.Name.Equals(name)) + if (p.Name.Equals(name, StringComparison.OrdinalIgnoreCase)) { property = p; break; From e71da5baa40f2eb0b71a704d977feee58df5eb3f Mon Sep 17 00:00:00 2001 From: Raoul Millais Date: Fri, 1 Nov 2013 15:00:15 +0000 Subject: [PATCH 04/21] Use assembly version for MVC3 nuget core dependency - The nuget package for Mvc3 should depend on the current version of the Nustache.Core library. --- Nustache.Mvc3/Nustache.Mvc3.nuspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nustache.Mvc3/Nustache.Mvc3.nuspec b/Nustache.Mvc3/Nustache.Mvc3.nuspec index 71edb0c..fb8f97b 100644 --- a/Nustache.Mvc3/Nustache.Mvc3.nuspec +++ b/Nustache.Mvc3/Nustache.Mvc3.nuspec @@ -11,7 +11,7 @@ $description$ Template Mustache MVC - + From 8ccec3dad1777b305cf10ac7cb2349cf9bf52b59 Mon Sep 17 00:00:00 2001 From: rafarhat Date: Thu, 23 Jan 2014 15:38:53 -0800 Subject: [PATCH 05/21] Issue #62 1.13.8.22 breaks nested fully qualified access Changed the logic to determine if a partial match has occurred. Existing logic returned true even if the first name from the dotted notation doesn't get any value (it could be a full qualified name that exists outside of the immediate context). --- Nustache.Core/RenderContext.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Nustache.Core/RenderContext.cs b/Nustache.Core/RenderContext.cs index 453c297..3f7dd1f 100644 --- a/Nustache.Core/RenderContext.cs +++ b/Nustache.Core/RenderContext.cs @@ -109,13 +109,17 @@ private static object GetValueFromPath(object data, string path, out bool partia if (names.Length > 1) { - foreach (var name in names) + for (int i = 0; i < names.Length; i++ ) { - data = ValueGetter.GetValue(data, name); + data = ValueGetter.GetValue(data, names[i]); if (data == null || ReferenceEquals(data, ValueGetter.NoValue)) { - partialMatch = true; + if (i>0) + { + partialMatch = true; + } + break; } } From 6fdf1ff94cf00e6ddf096a5917e7a7b007ac1733 Mon Sep 17 00:00:00 2001 From: rafarhat Date: Thu, 23 Jan 2014 15:45:14 -0800 Subject: [PATCH 06/21] Issue #62 Minor Style Change --- Nustache.Core/RenderContext.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nustache.Core/RenderContext.cs b/Nustache.Core/RenderContext.cs index 3f7dd1f..5519d8b 100644 --- a/Nustache.Core/RenderContext.cs +++ b/Nustache.Core/RenderContext.cs @@ -115,7 +115,7 @@ private static object GetValueFromPath(object data, string path, out bool partia if (data == null || ReferenceEquals(data, ValueGetter.NoValue)) { - if (i>0) + if (i > 0) { partialMatch = true; } From 2eddc347e20e82e4b9a134e58ea3bdc6582f544d Mon Sep 17 00:00:00 2001 From: rafarhat Date: Fri, 28 Mar 2014 16:46:30 -0700 Subject: [PATCH 07/21] ValueGetter Support for NameValueCollections Added values getter support for NameValueCollections --- Nustache.Core.Tests/Describe_ValueGetter.cs | 9 +++++++++ Nustache.Core/ValueGetter.cs | 19 ++++++++++++++++++ Nustache.Core/ValueGetterFactory.cs | 22 ++++++++++++++++++++- 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/Nustache.Core.Tests/Describe_ValueGetter.cs b/Nustache.Core.Tests/Describe_ValueGetter.cs index 110e3e7..5068d12 100644 --- a/Nustache.Core.Tests/Describe_ValueGetter.cs +++ b/Nustache.Core.Tests/Describe_ValueGetter.cs @@ -4,6 +4,7 @@ using System.Xml; using Moq; using NUnit.Framework; +using System.Collections.Specialized; namespace Nustache.Core.Tests { @@ -152,6 +153,14 @@ public void It_gets_XmlNode_multiple_child_element_values_as_a_list() Assert.AreEqual("text2", elements[1].InnerText); } + [Test] + public void It_gets_NameValueCollection_values() + { + NameValueCollection target = new NameValueCollection(); + target["IntKey"] = "123"; + Assert.AreEqual("123", ValueGetter.GetValue(target, "IntKey")); + } + public class ReadWriteInts { public int IntField = -1; diff --git a/Nustache.Core/ValueGetter.cs b/Nustache.Core/ValueGetter.cs index d79638b..722cf1f 100644 --- a/Nustache.Core/ValueGetter.cs +++ b/Nustache.Core/ValueGetter.cs @@ -1,5 +1,6 @@ using System; using System.Collections; +using System.Collections.Specialized; using System.ComponentModel; using System.Reflection; using System.Xml; @@ -226,6 +227,24 @@ public override object GetValue() } } + + internal class NameValueCollectionValueGetter : ValueGetter + { + private readonly NameValueCollection _target; + private readonly string _key; + + internal NameValueCollectionValueGetter(NameValueCollection target, string key) + { + _target = target; + _key = key; + } + + public override object GetValue() + { + return _target[_key]; + } + } + internal class NoValueGetter : ValueGetter { public override object GetValue() diff --git a/Nustache.Core/ValueGetterFactory.cs b/Nustache.Core/ValueGetterFactory.cs index a21b7fb..8f84460 100644 --- a/Nustache.Core/ValueGetterFactory.cs +++ b/Nustache.Core/ValueGetterFactory.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Collections.Specialized; using System.ComponentModel; using System.Reflection; using System.Xml; @@ -85,7 +86,8 @@ public static class ValueGetterFactories new DictionaryValueGetterFactory(), new MethodInfoValueGatterFactory(), new PropertyInfoValueGetterFactory(), - new FieldInfoValueGetterFactory() + new FieldInfoValueGetterFactory(), + new NameValueCollectionGetterFactory() }; public static ValueGetterFactoryCollection Factories @@ -247,4 +249,22 @@ private static Type GetSupportedInterfaceType(Type targetType) } } } + + internal class NameValueCollectionGetterFactory : ValueGetterFactory + { + public override ValueGetter GetValueGetter(object target, Type targetType, string name) + { + if (target is NameValueCollection) + { + var nameValueCollection = (NameValueCollection)target; + + if (nameValueCollection[name] != null) + { + return new NameValueCollectionValueGetter(nameValueCollection, name); + } + } + + return null; + } + } } From d9095b582d17e1a4b6b2110c4fb898e107ffbe4a Mon Sep 17 00:00:00 2001 From: joaompneves Date: Thu, 7 Aug 2014 17:43:26 +0100 Subject: [PATCH 08/21] Fixed escaping lost when using lambdas Escaping information was lost when using lambdas --- Nustache.Core/VariableReference.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Nustache.Core/VariableReference.cs b/Nustache.Core/VariableReference.cs index ebcd105..749260e 100644 --- a/Nustache.Core/VariableReference.cs +++ b/Nustache.Core/VariableReference.cs @@ -56,7 +56,7 @@ public override void Render(RenderContext context) public override string Source() { - return "{{" + _path + "}}"; + return "{{" + (!Escaped ? "&" : "") + _path + "}}"; } public override string ToString() @@ -64,4 +64,4 @@ public override string ToString() return string.Format("VariableReference(\"{0}\")", _path); } } -} \ No newline at end of file +} From e85e0a944b5fe41feedc82066eb7bcdc61aadcd8 Mon Sep 17 00:00:00 2001 From: jmn Date: Thu, 13 Nov 2014 17:59:33 +0000 Subject: [PATCH 09/21] Fixed encoding of unicode surrogates --- Nustache.Core/Encoders.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Nustache.Core/Encoders.cs b/Nustache.Core/Encoders.cs index 4ba57ee..6ca0579 100644 --- a/Nustache.Core/Encoders.cs +++ b/Nustache.Core/Encoders.cs @@ -47,10 +47,17 @@ public static string DefaultHtmlEncode(string text) sb.Append("&"); break; default: - if (text[i] > 159) + var ch = text[i]; + if (ch > 159) { sb.Append("&#"); - sb.Append(((int)text[i]).ToString(CultureInfo.InvariantCulture)); + if (char.IsHighSurrogate(ch) && (i + 1) < len) { + // convert surrogates to their decimal value + sb.Append(char.ConvertToUtf32(ch, text[i+1])); + i++; + } else { + sb.Append(((int)ch).ToString(CultureInfo.InvariantCulture)); + } sb.Append(";"); } else From f1e7d6e06b245b436df30fb48a2fef926d022b6a Mon Sep 17 00:00:00 2001 From: lukasz-lysik Date: Tue, 3 Feb 2015 00:00:09 +0000 Subject: [PATCH 10/21] #36 Context for lambda function added --- Nustache.Core.Tests/Describe_Lambda.cs | 13 ++++++++++++- Nustache.Core/Block.cs | 13 ++++++++++++- Nustache.Core/RenderContext.cs | 16 +++++++++++++++- 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/Nustache.Core.Tests/Describe_Lambda.cs b/Nustache.Core.Tests/Describe_Lambda.cs index b9a07f0..062e027 100644 --- a/Nustache.Core.Tests/Describe_Lambda.cs +++ b/Nustache.Core.Tests/Describe_Lambda.cs @@ -10,9 +10,20 @@ public void The_text_passed_is_the_literal_block_unrendered() { var result = Render.StringToString("{{#wrapped}}{{name}} is awesome.{{/wrapped}}", new { - wrapped = (Lambda)((s) => string.Format("{0}", s)) + wrapped = (Lambda)((body, context, render) => string.Format("{0}", body)) }); Assert.AreEqual("{{name}} is awesome.", result); } + + [Test] + public void It_can_use_context_and_render_delegate_inside_lambda() + { + var result = Render.StringToString("{{#wrapped}}{{name}} is awesome.{{/wrapped}}", new + { + wrapped = (Lambda)((body, context, render) => string.Format("{0}", render(context))), + name = "Lukasz" + }); + Assert.AreEqual("Lukasz is awesome.", result); + } } } \ No newline at end of file diff --git a/Nustache.Core/Block.cs b/Nustache.Core/Block.cs index 9b7fafb..009af41 100644 --- a/Nustache.Core/Block.cs +++ b/Nustache.Core/Block.cs @@ -1,4 +1,7 @@ +using System; +using System.IO; + namespace Nustache.Core { public class Block : Section @@ -17,7 +20,15 @@ public override void Render(RenderContext context) if (lambda != null) { - context.Write(lambda(InnerSource()).ToString()); + RenderFunc render = c => + { + var textWriter = new StringWriter(); + var lambdaContext = new RenderContext(context, textWriter); + RenderParts(lambdaContext); + return textWriter.GetStringBuilder().ToString(); + }; + + context.Write(lambda(InnerSource(), context, render).ToString()); return; } diff --git a/Nustache.Core/RenderContext.cs b/Nustache.Core/RenderContext.cs index 453c297..d3a8ab9 100644 --- a/Nustache.Core/RenderContext.cs +++ b/Nustache.Core/RenderContext.cs @@ -9,7 +9,9 @@ namespace Nustache.Core { public delegate Template TemplateLocator(string name); - public delegate Object Lambda(string text); + public delegate Object Lambda(string text, RenderContext context, RenderFunc render); + + public delegate string RenderFunc(RenderContext context); public class RenderContext { @@ -36,6 +38,18 @@ public RenderContext(Section section, object data, TextWriter writer, TemplateLo RenderContextBehaviour.GetDefaultRenderContextBehaviour(); } + public RenderContext(RenderContext baseContext, TextWriter writer) + { + _sectionStack = baseContext._sectionStack; + _dataStack = baseContext._dataStack; + _writer = writer; + _templateLocator = baseContext._templateLocator; + _renderContextBehaviour = baseContext._renderContextBehaviour; + _includeLevel = baseContext._includeLevel; + _indent = baseContext._indent; + _lineEnded = baseContext._lineEnded; + } + public object GetValue(string path) { if (path == ".") From 0182bf5de6605b9796ad61136ffe1468d03a6c1c Mon Sep 17 00:00:00 2001 From: Romanx Date: Sun, 5 Apr 2015 11:58:53 +0100 Subject: [PATCH 11/21] Added markdown readme to solution --- Nustache.sln | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nustache.sln b/Nustache.sln index 154333a..c1f342e 100644 --- a/Nustache.sln +++ b/Nustache.sln @@ -7,7 +7,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ProjectSection(SolutionItems) = preProject HISTORY.txt = HISTORY.txt LICENSE.txt = LICENSE.txt - README.txt = README.txt + README.md = README.md TODO.txt = TODO.txt EndProjectSection EndProject From 05009c7f689ae74d42172acecfcf73af6c16d904 Mon Sep 17 00:00:00 2001 From: Romanx Date: Sun, 5 Apr 2015 11:59:57 +0100 Subject: [PATCH 12/21] Added a yield for DataTables to retrive Rows #79 This works like a dictionary getting each value in turn for inspection --- .../Nustache.Compilation.Tests.csproj | 3 +++ Nustache.Core.Tests/Nustache.Core.Tests.csproj | 3 +++ Nustache.Core/Nustache.Core.csproj | 1 + Nustache.Core/RenderContext.cs | 8 ++++++++ 4 files changed, 15 insertions(+) diff --git a/Nustache.Compilation.Tests/Nustache.Compilation.Tests.csproj b/Nustache.Compilation.Tests/Nustache.Compilation.Tests.csproj index 697e3c6..96c89ae 100644 --- a/Nustache.Compilation.Tests/Nustache.Compilation.Tests.csproj +++ b/Nustache.Compilation.Tests/Nustache.Compilation.Tests.csproj @@ -80,6 +80,9 @@ Nustache.Core + + +