diff --git a/Nustache.Core/DefaultContext.cs b/Nustache.Core/DefaultContext.cs deleted file mode 100644 index 04919de..0000000 --- a/Nustache.Core/DefaultContext.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Reflection; - -namespace Nustache.Core -{ - public class DefaultContext : IContext - { - private const BindingFlags DefaultBindingFlags = - BindingFlags.Instance | - BindingFlags.Public; - - public DefaultContext(object data) - { - Current = data; - } - - public object GetValue(string name) - { - if (name == ".") return Current; - - if (Current == null) return null; - - var propertyInfo = Current.GetType().GetProperty(name, DefaultBindingFlags); - - if (propertyInfo == null) return ""; - - var value = propertyInfo.GetValue(Current, null); - - return value; - } - - public object Current { get; set; } - } -} \ No newline at end of file diff --git a/Nustache.Core/EndSection.cs b/Nustache.Core/EndSection.cs index a87bccf..e228edc 100644 --- a/Nustache.Core/EndSection.cs +++ b/Nustache.Core/EndSection.cs @@ -1,5 +1,3 @@ -using System.IO; - namespace Nustache.Core { public class EndSection : Part @@ -16,7 +14,7 @@ public string Name get { return _name; } } - public override void Render(TextWriter writer, IContext context) + public override void Render(RenderContext context) { } diff --git a/Nustache.Core/IContext.cs b/Nustache.Core/IContext.cs deleted file mode 100644 index 07a377f..0000000 --- a/Nustache.Core/IContext.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Nustache.Core -{ - public interface IContext - { - object GetValue(string name); - object Current { get; set; } - } -} \ No newline at end of file diff --git a/Nustache.Core/LiteralText.cs b/Nustache.Core/LiteralText.cs index 107dadb..113bcd6 100644 --- a/Nustache.Core/LiteralText.cs +++ b/Nustache.Core/LiteralText.cs @@ -1,5 +1,3 @@ -using System.IO; - namespace Nustache.Core { public class LiteralText : Part @@ -11,9 +9,9 @@ public LiteralText(string text) _text = text; } - public override void Render(TextWriter writer, IContext context) + public override void Render(RenderContext context) { - writer.Write(_text); + context.Write(_text); } #region Boring stuff diff --git a/Nustache.Core/Nustache.Core.csproj b/Nustache.Core/Nustache.Core.csproj index d371841..96e65f1 100644 --- a/Nustache.Core/Nustache.Core.csproj +++ b/Nustache.Core/Nustache.Core.csproj @@ -45,9 +45,8 @@ - + - diff --git a/Nustache.Core/Part.cs b/Nustache.Core/Part.cs index 1d214e5..d28d837 100644 --- a/Nustache.Core/Part.cs +++ b/Nustache.Core/Part.cs @@ -1,9 +1,7 @@ -using System.IO; - namespace Nustache.Core { public abstract class Part { - public abstract void Render(TextWriter writer, IContext context); + public abstract void Render(RenderContext context); } } \ No newline at end of file diff --git a/Nustache.Core/RenderContext.cs b/Nustache.Core/RenderContext.cs new file mode 100644 index 0000000..40be207 --- /dev/null +++ b/Nustache.Core/RenderContext.cs @@ -0,0 +1,79 @@ +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Reflection; + +namespace Nustache.Core +{ + public class RenderContext + { + private const BindingFlags DefaultBindingFlags = + BindingFlags.Instance | + BindingFlags.Public; + + private readonly Stack _stack = new Stack(); + private object _data; + private readonly TextWriter _writer; + + public RenderContext(object data, TextWriter writer) + { + _data = data; + _writer = writer; + } + + public object GetValue(string name) + { + if (name == ".") return _data; + + if (_data == null) return null; + + var propertyInfo = _data.GetType().GetProperty(name, DefaultBindingFlags); + + if (propertyInfo == null) return ""; + + var value = propertyInfo.GetValue(_data, null); + + return value; + } + + public IEnumerable GetValues(string name) + { + object value = GetValue(name); + + if (value is bool) + { + if ((bool)value) + { + yield return value; + } + } + else if (value is IEnumerable && !(value is string)) + { + foreach (var item in ((IEnumerable)value)) + { + yield return item; + } + } + else if (value != null) + { + yield return value; + } + } + + public void Push(object data) + { + _stack.Push(_data); + _data = data; + } + + public void Pop() + { + _data = _stack.Pop(); + } + + public void Write(string text) + { + _writer.Write(text); + } + } +} \ No newline at end of file diff --git a/Nustache.Core/StartSection.cs b/Nustache.Core/StartSection.cs index de9f436..0707630 100644 --- a/Nustache.Core/StartSection.cs +++ b/Nustache.Core/StartSection.cs @@ -1,6 +1,4 @@ -using System.Collections; using System.Collections.Generic; -using System.IO; namespace Nustache.Core { @@ -19,46 +17,20 @@ public string Name get { return _name; } } - public List Children { get { return _children; } } + public IList Children { get { return _children; } } - public override void Render(TextWriter writer, IContext context) + public override void Render(RenderContext context) { - object value = context.GetValue(_name); - - object current = context.Current; - - foreach (var item in GetItems(value)) + foreach (var value in context.GetValues(_name)) { - context.Current = item; + context.Push(value); foreach (var child in _children) { - child.Render(writer, context); + child.Render(context); } - } - - context.Current = current; - } - private static IEnumerable GetItems(object value) - { - if (value is bool) - { - if ((bool)value) - { - yield return value; - } - } - else if (value is IEnumerable && !(value is string)) - { - foreach (var item in ((IEnumerable)value)) - { - yield return item; - } - } - else if (value != null) - { - yield return value; + context.Pop(); } } diff --git a/Nustache.Core/Template.cs b/Nustache.Core/Template.cs index b5158d4..418c53b 100644 --- a/Nustache.Core/Template.cs +++ b/Nustache.Core/Template.cs @@ -1,25 +1,63 @@ -using System.IO; +using System; +using System.Collections.Generic; +using System.IO; namespace Nustache.Core { - public static class Template + public class Template { - public static string Render(string template, object data) + private IEnumerable _parts; + + public void Load(TextReader reader) { - var writer = new StringWriter(); - var context = data as IContext ?? new DefaultContext(data); + string template = reader.ReadToEnd(); var scanner = new Scanner(); var parser = new Parser(); - foreach (var part in parser.Parse(scanner.Scan(template))) + _parts = parser.Parse(scanner.Scan(template)); + } + + public void Render(object data, TextWriter writer) + { + var context = new RenderContext(data, writer); + + foreach (var part in _parts) { - part.Render(writer, context); + part.Render(context); } writer.Flush(); + } + public static string RenderStringToString(string template, object data) + { + var reader = new StringReader(template); + var writer = new StringWriter(); + Render(reader, data, writer); return writer.GetStringBuilder().ToString(); } + + public static string RenderFileToString(string templatePath, object data) + { + throw new NotImplementedException(); + } + + public static void RenderStringToFile(string template, object data, string outputPath) + { + throw new NotImplementedException(); + } + + public static void RenderFileToFile(string templatePath, object data, string outputPath) + { + throw new NotImplementedException(); + } + + public static void Render(TextReader reader, object data, TextWriter writer) + { + var template = new Template(); + template.Load(reader); + template.Render(data, writer); + } } } \ No newline at end of file diff --git a/Nustache.Core/TemplateInclude.cs b/Nustache.Core/TemplateInclude.cs index 06e4626..2e44a5a 100644 --- a/Nustache.Core/TemplateInclude.cs +++ b/Nustache.Core/TemplateInclude.cs @@ -1,5 +1,4 @@ using System; -using System.IO; namespace Nustache.Core { @@ -12,7 +11,7 @@ public TemplateInclude(string name) _name = name; } - public override void Render(TextWriter writer, IContext context) + public override void Render(RenderContext context) { throw new NotImplementedException(); } diff --git a/Nustache.Core/VariableMarker.cs b/Nustache.Core/VariableMarker.cs index 5967547..38c03af 100644 --- a/Nustache.Core/VariableMarker.cs +++ b/Nustache.Core/VariableMarker.cs @@ -1,5 +1,3 @@ -using System.IO; - namespace Nustache.Core { public class VariableMarker : Part @@ -11,13 +9,13 @@ public VariableMarker(string name) _name = name; } - public override void Render(TextWriter writer, IContext context) + public override void Render(RenderContext context) { object value = context.GetValue(_name); if (value != null) { - writer.Write(value.ToString()); + context.Write(value.ToString()); } } diff --git a/Nustache.Tests/Template_Render.cs b/Nustache.Tests/Template_Render.cs index 02b3df8..04fe5ef 100644 --- a/Nustache.Tests/Template_Render.cs +++ b/Nustache.Tests/Template_Render.cs @@ -9,98 +9,98 @@ public class Describe_Template_Render [Test] public void It_renders_empty_strings_as_empty_strings() { - var result = Template.Render("", null); + var result = Template.RenderStringToString("", null); Assert.AreEqual("", result); } [Test] public void It_replaces_undefined_variables_with_empty_strings_when_there_is_no_data() { - var result = Template.Render("before{{foo}}after", null); + var result = Template.RenderStringToString("before{{foo}}after", null); Assert.AreEqual("beforeafter", result); } [Test] public void It_replaces_undefined_variables_with_empty_strings_when_there_is_data() { - var result = Template.Render("before{{foo}}after", new { bar = "baz" }); + var result = Template.RenderStringToString("before{{foo}}after", new { bar = "baz" }); Assert.AreEqual("beforeafter", result); } [Test] public void It_replaces_defined_variables_with_values() { - var result = Template.Render("before{{foo}}after", new { foo = "FOO" }); + var result = Template.RenderStringToString("before{{foo}}after", new { foo = "FOO" }); Assert.AreEqual("beforeFOOafter", result); } [Test] public void It_handles_two_variables_correctly() { - var result = Template.Render("before{{foo}}inside{{bar}}after", new { foo = "FOO", bar = "BAR" }); + var result = Template.RenderStringToString("before{{foo}}inside{{bar}}after", new { foo = "FOO", bar = "BAR" }); Assert.AreEqual("beforeFOOinsideBARafter", result); } [Test] public void It_does_not_render_sections_mapped_to_false() { - var result = Template.Render("before{{#foo}}FOO{{/foo}}after", new { foo = false }); + var result = Template.RenderStringToString("before{{#foo}}FOO{{/foo}}after", new { foo = false }); Assert.AreEqual("beforeafter", result); } [Test] public void It_renders_sections_mapped_to_true() { - var result = Template.Render("before{{#foo}}FOO{{/foo}}after", new { foo = true }); + var result = Template.RenderStringToString("before{{#foo}}FOO{{/foo}}after", new { foo = true }); Assert.AreEqual("beforeFOOafter", result); } [Test] public void It_does_not_render_sections_mapped_to_null() { - var result = Template.Render("before{{#foo}}FOO{{/foo}}after", new { foo = (string)null }); + var result = Template.RenderStringToString("before{{#foo}}FOO{{/foo}}after", new { foo = (string)null }); Assert.AreEqual("beforeafter", result); } [Test] public void It_renders_sections_mapped_to_non_null() { - var result = Template.Render("before{{#foo}}FOO{{/foo}}after", new { foo = "bar" }); + var result = Template.RenderStringToString("before{{#foo}}FOO{{/foo}}after", new { foo = "bar" }); Assert.AreEqual("beforeFOOafter", result); } [Test] public void It_does_not_render_sections_mapped_to_empty_collections() { - var result = Template.Render("before{{#foo}}FOO{{/foo}}after", new { foo = new int[] { } }); + var result = Template.RenderStringToString("before{{#foo}}FOO{{/foo}}after", new { foo = new int[] { } }); Assert.AreEqual("beforeafter", result); } [Test] public void It_renders_sections_mapped_to_non_empty_collections() { - var result = Template.Render("before{{#foo}}FOO{{/foo}}after", new { foo = new [] { 1 } }); + var result = Template.RenderStringToString("before{{#foo}}FOO{{/foo}}after", new { foo = new [] { 1 } }); Assert.AreEqual("beforeFOOafter", result); } [Test] public void It_renders_sections_mapped_to_non_empty_collections_for_each_item_in_the_collection() { - var result = Template.Render("before{{#foo}}FOO{{/foo}}after", new { foo = new [] { 1, 2, 3 } }); + var result = Template.RenderStringToString("before{{#foo}}FOO{{/foo}}after", new { foo = new [] { 1, 2, 3 } }); Assert.AreEqual("beforeFOOFOOFOOafter", result); } [Test] public void It_changes_the_context_for_each_item_in_the_collection() { - var result = Template.Render("before{{#foo}}{{.}}{{/foo}}after", new { foo = new [] { 1, 2, 3 } }); + var result = Template.RenderStringToString("before{{#foo}}{{.}}{{/foo}}after", new { foo = new [] { 1, 2, 3 } }); Assert.AreEqual("before123after", result); } [Test] public void It_lets_you_reference_properties_of_items_in_the_collection() { - var result = Template.Render( + var result = Template.RenderStringToString( "before{{#foo}}{{bar}}{{/foo}}after", new { foo = new [] { new { bar = 1 }, new { bar = 2 }, new { bar = 3 } } }); Assert.AreEqual("before123after", result);