Skip to content

Commit

Permalink
Merge pull request #90 from jdiamond/dev
Browse files Browse the repository at this point in the history
Release 1.15.0
  • Loading branch information
Romanx committed Apr 30, 2015
2 parents 459c798 + 70c00a6 commit 97a21c8
Show file tree
Hide file tree
Showing 13 changed files with 231 additions and 22 deletions.
2 changes: 1 addition & 1 deletion Nustache.Compilation/CompilePartVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public void Visit(Block block)
{
context.Push(block, value);

if (typeof(Lambda).BaseType.IsAssignableFrom(value.Type))
if (typeof(Lambda<string>).BaseType.IsAssignableFrom(value.Type))
{
return
Expression.Call(
Expand Down
25 changes: 19 additions & 6 deletions Nustache.Core.Tests/Describe_Lambda.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,33 @@ public void The_text_passed_is_the_literal_block_unrendered()
{
var result = Render.StringToString("{{#wrapped}}{{name}} is awesome.{{/wrapped}}", new
{
wrapped = (Lambda)((body, context, render) => string.Format("<b>{0}</b>", body))
wrapped = (Lambda<string, object>)((body) => string.Format("<b>{0}</b>", body)),
name = "Nustache"
});
Assert.AreEqual("<b>{{name}} is awesome.</b>", result);
Assert.AreEqual("<b>Nustache is awesome.</b>", result);
}

[Test]
public void It_can_use_context_and_render_delegate_inside_lambda()
public void It_can_render_nontyped_delegate_functions()
{
var result = Render.StringToString("{{foo}}, {{bar}}", new
{
foo = "bar",
bar = new System.Func<string>(() => { return "foo"; })
});

Assert.AreEqual("bar, foo", result);
}

[Test]
public void It_can_render_nontyped_delegate_functions_with_body()
{
var result = Render.StringToString("{{#wrapped}}{{name}} is awesome.{{/wrapped}}", new
{
wrapped = (Lambda)((body, context, render) => string.Format("<b>{0}</b>", render(context))),
name = "Lukasz"
wrapped = new System.Func<string, string>((body) => string.Format("<b>{0}</b>", body)),
name = "Nustache"
});
Assert.AreEqual("<b>Lukasz is awesome.</b>", result);
Assert.AreEqual("<b>Nustache is awesome.</b>", result);
}
}
}
16 changes: 16 additions & 0 deletions Nustache.Core.Tests/Describe_Render.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,5 +76,21 @@ public void It_can_use_an_ampersand_to_render_unencoded_text()

Assert.AreEqual("<bar>", result);
}

[Test]
public void It_ignores_Newtonsoft_IEnumerable_results_with_no_values()
{
const string template = @"{{#link}}<a href=""{{{url}}}"" {{#classname}}class=""{{{.}}}""{{/classname}}>{{{title}}}</a>{{/link}}";
const string json = @"{
""link"": {
""url"": ""https://github.com/jdiamond/Nustache/"",
""title"": ""Nustache Main"",
""classname"": ""nustache--logo""
}
}";
var result = Render.StringToString(template, Newtonsoft.Json.Linq.JObject.Parse(json));

Assert.AreEqual(@"<a href=""https://github.com/jdiamond/Nustache/"" class=""nustache--logo"">Nustache Main</a>", result);
}
}
}
10 changes: 10 additions & 0 deletions Nustache.Core.Tests/Describe_ValueGetter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,16 @@ public void It_gets_case_insensitive_DataRow_Values()
Assert.AreEqual(123, ValueGetter.GetValue(target, "intcolumn"));
}

[Test]
public void It_gets_DataRow_values_using_property_names()
{
DataTable dt = new DataTable();
dt.Columns.Add("item", typeof(int));
dt.Rows.Add(new object[] { 123 });
var target = dt.Rows[0];
Assert.AreEqual(123, ValueGetter.GetValue(target, "item"));
}

[Test]
public void It_gets_NameValueCollection_values()
{
Expand Down
71 changes: 71 additions & 0 deletions Nustache.Core.Tests/Mustache_Spec.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Text.RegularExpressions;
using DynamicExpresso;
using Microsoft.CSharp;
using NUnit.Framework;
using YamlDotNet.RepresentationModel.Serialization;

Expand All @@ -11,13 +19,16 @@ namespace Nustache.Core.Tests
[TestFixture]
public class Mustache_Spec
{
public static Int32 GlobalCalls;

[Test]
[TestCaseSource("Comments")]
[TestCaseSource("Delimiters")]
[TestCaseSource("Interpolation")]
[TestCaseSource("Inverted")]
[TestCaseSource("Partials")]
[TestCaseSource("Sections")]
[TestCaseSource("Lambdas")]
public void AllTests(string name, Dictionary<object, object> data, string template, Dictionary<object, string> partials, string expected)
{
FixData(data);
Expand Down Expand Up @@ -46,6 +57,7 @@ private void FixData(Dictionary<object, object> data)
{
FixNumbers(data);
FixFalseValues(data);
FixLambdas(data);
}

private void FixNumbers(Dictionary<object, object> data)
Expand All @@ -62,6 +74,57 @@ private void FixFalseValues(Dictionary<object, object> data)
value => false);
}

private void FixLambdas(Dictionary<object, object> data)
{
if (data.ContainsKey("lambda"))
{
var res = (Dictionary<object, object>)data["lambda"];

//Hack for Interpolation Multiple calls as it uses globals which the library doesn't support entirely.
if (((String)res["js"]).Contains(".calls"))
{
data["lambda"] = (Lambda<object>)(() =>
{
return ++Mustache_Spec.GlobalCalls;
});
}
else
{
data["lambda"] = res["js"];
}

}

Visit(data,
value => (value.Contains("function()") || value.Contains("function(txt)")),
value =>
{
if (value.Contains("function()"))
{
var match = Regex.Match(value, @"function\(\)\s*{\s*return\s*([A-Za-z0-9 \"">=|{(}?+#._:;)]*)\s* }");
if(match.Success)
{
var body = match.Groups[1].Value;

return new Interpreter().ParseAsDelegate<Lambda<object>>(body);
}

}
else if (value.Contains("function(txt)"))
{
var match = Regex.Match(value, @"function\((\w*)\)\s*{\s*return\s*([A-Za-z0-9 \"">=|{(}?+#._:;)]*)\s* }");
if(match.Success)
{
var argumentName = match.Groups[1].Value;
var body = match.Groups[2].Value;

return new Interpreter().ParseAsDelegate<Lambda<string, object>>(body, argumentName);
}
}
return null;
});
}

private void Visit(object value, Func<string, bool> pred, Func<string, object> func)
{
if (value is List<object>)
Expand Down Expand Up @@ -108,17 +171,25 @@ private void Visit(object value, Func<string, bool> pred, Func<string, object> f
public IEnumerable<ITestCaseData> Inverted() { return GetTestCases("inverted"); }
public IEnumerable<ITestCaseData> Partials() { return GetTestCases("partials"); }
public IEnumerable<ITestCaseData> Sections() { return GetTestCases("sections"); }
public IEnumerable<ITestCaseData> Lambdas() { return GetTestCases("~lambdas"); }

public IEnumerable<ITestCaseData> GetTestCases(string file)
{
var text = File.ReadAllText(string.Format("../../../spec/specs/{0}.yml", file));
if (file.Equals("~lambdas")) text = CleanLambdaFile(text);

var deserializer = new Deserializer();
var doc = deserializer.Deserialize<SpecDoc>(new StringReader(text));

return doc.tests
.Select(test => new TestCaseData(test.name, test.data, test.template, test.partials, test.expected)
.SetName(file + ": " + test.name));
}

private string CleanLambdaFile(String fileContents)
{
return fileContents.Replace("!code", "");
}
}

public class SpecDoc
Expand Down
6 changes: 6 additions & 0 deletions Nustache.Core.Tests/Nustache.Core.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,16 @@
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="DynamicExpresso.Core">
<HintPath>..\packages\DynamicExpresso.Core.1.3.0.0\lib\net40\DynamicExpresso.Core.dll</HintPath>
</Reference>
<Reference Include="Moq, Version=4.0.10827.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Moq.4.0.10827\lib\NET40\Moq.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>..\packages\Newtonsoft.Json.6.0.8\lib\net40\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="nunit.framework, Version=2.6.2.12296, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\NUnit.2.6.2\lib\nunit.framework.dll</HintPath>
Expand Down
2 changes: 2 additions & 0 deletions Nustache.Core.Tests/packages.config
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="DynamicExpresso.Core" version="1.3.0.0" targetFramework="net40" />
<package id="Moq" version="4.0.10827" targetFramework="net40" />
<package id="Newtonsoft.Json" version="6.0.8" targetFramework="net40" />
<package id="NUnit" version="2.6.2" targetFramework="net40" />
<package id="YamlDotNet.Core" version="2.1.1" targetFramework="net40" />
<package id="YamlDotNet.RepresentationModel" version="2.1.1" targetFramework="net40" />
Expand Down
33 changes: 25 additions & 8 deletions Nustache.Core/Block.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,22 @@ public override void Render(RenderContext context)
{
var value = context.GetValue(Name);

var lambda = value as Lambda;
var lambda = CheckValueIsDelegateOrLambda(value);

if (lambda != null)
{
RenderFunc render = c =>
var lambdaResult = lambda(InnerSource()).ToString();
using(TextReader sr = new StringReader(lambdaResult))
{
var textWriter = new StringWriter();
var lambdaContext = new RenderContext(context, textWriter);
RenderParts(lambdaContext);
return textWriter.GetStringBuilder().ToString();
};
var template = new Template();
template.StartDelimiter = context.ActiveStartDelimiter;
template.EndDelimiter = context.ActiveEndDelimiter;

context.Write(lambda(InnerSource(), context, render).ToString());
template.Load(sr);
context.Enter(template);
template.Render(context);
context.Exit();
}

return;
}
Expand Down Expand Up @@ -75,6 +78,20 @@ public override void Render(RenderContext context)
}
}

public Lambda<string, object> CheckValueIsDelegateOrLambda(object value)
{
var lambda = value as Lambda<string, object>;
if (lambda != null) return lambda;

if (value is Delegate && !(value is HelperProxy))
{
var delegateValue = (Delegate)value;
return (Lambda<string, object>)((body) => (object)delegateValue.DynamicInvoke(body));
}

return null;
}

public override string ToString()
{
return string.Format("Block(\"{0}\")", Name);
Expand Down
9 changes: 8 additions & 1 deletion Nustache.Core/RenderContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ namespace Nustache.Core
{
public delegate Template TemplateLocator(string name);

public delegate Object Lambda(string text, RenderContext context, RenderFunc render);
public delegate TResult Lambda<TResult>();
public delegate TResult Lambda<T, TResult>(T arg);

public delegate string RenderFunc(RenderContext context);

Expand All @@ -26,6 +27,8 @@ public class RenderContext
private string _indent;
private bool _lineEnded;
private readonly Regex _indenter = new Regex("\n(?!$)");
public string ActiveStartDelimiter { get; set; }
public string ActiveEndDelimiter { get; set; }

public RenderContext(Section section, object data, TextWriter writer, TemplateLocator templateLocator, RenderContextBehaviour renderContextBehaviour = null)
{
Expand Down Expand Up @@ -171,6 +174,10 @@ public IEnumerable<object> GetValues(string path)
yield return value;
}
}
else if (value.GetType().ToString().Equals("Newtonsoft.Json.Linq.JValue"))
{
yield return value;
}
else if (GenericIDictionaryUtil.IsInstanceOfGenericIDictionary(value))
{
if ((value as IEnumerable).GetEnumerator().MoveNext())
Expand Down
18 changes: 17 additions & 1 deletion Nustache.Core/Scanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,23 @@ namespace Nustache.Core
public class Scanner
{
private static readonly Regex DelimitersRegex = new Regex(@"^=\s*(\S+)\s+(\S+)\s*=$");
public string StartDelimiter { get; set; }
public string EndDelimiter { get; set; }

public Scanner()
: this("{{", "}}")
{
}

public Scanner(string startDelimiter, string endDelimiter)
{
StartDelimiter = startDelimiter;
EndDelimiter = endDelimiter;
}

public IEnumerable<Part> Scan(string template)
{
var regex = MakeRegex("{{", "}}");
var regex = MakeRegex(StartDelimiter, EndDelimiter);
var i = 0;
var lineEnded = false;

Expand Down Expand Up @@ -40,6 +53,9 @@ public IEnumerable<Part> Scan(string template)
{
var start = delimiters.Groups[1].Value;
var end = delimiters.Groups[2].Value;
this.StartDelimiter = start;
this.EndDelimiter = end;

regex = MakeRegex(start, end);
}
}
Expand Down
Loading

0 comments on commit 97a21c8

Please sign in to comment.