diff --git a/Liquid.NET.Tests/Constants/MissingValueTests.cs b/Liquid.NET.Tests/Constants/MissingValueTests.cs old mode 100755 new mode 100644 index ff9834a..94609d0 --- a/Liquid.NET.Tests/Constants/MissingValueTests.cs +++ b/Liquid.NET.Tests/Constants/MissingValueTests.cs @@ -5,22 +5,26 @@ namespace Liquid.NET.Tests.Constants { - public class MissingValueTests { [Theory] [InlineData("x", "x")] [InlineData("d.x", "x")] [InlineData("d[x]", "x")] - [InlineData("a.b.c", "a")] - public void It_Should_Display_An_Error_When_Dereferencing_Missing_Value(String varname, String missingVar) + public void It_Should_Display_An_Error_When_Value_Is_Undefined(String varname, String missingVar) { // Arrange - ITemplateContext ctx = new TemplateContext() + ITemplateContext ctx = new TemplateContext() .ErrorWhenValueMissing(); - ctx.DefineLocalVariable("d", new LiquidHash()); - + ctx.DefineLocalVariable("x", LiquidValue.None); + ctx.DefineLocalVariable("d", new LiquidHash() + { + { + "x", LiquidValue.None + } + }); + // Act var template = LiquidTemplate.Create("Result : {{ " + varname + " }}"); var result = template.LiquidTemplate.Render(ctx); @@ -28,19 +32,18 @@ public void It_Should_Display_An_Error_When_Dereferencing_Missing_Value(String v // Assert Assert.Equal("Result : ERROR: " + missingVar + " is undefined", result.Result); - } [Theory] - [InlineData("e[1]")] - [InlineData("e.first")] - [InlineData("e.last")] - public void It_Should_Display_An_Error_When_Dereferencing_Empty_Array(String varname) + [InlineData("e[1]", "1")] + [InlineData("e.first", "first")] + [InlineData("e.last", "last")] + public void It_Should_Display_An_Error_When_Array_Value_Is_Undefined(String varname, String missingVar) { // Arrange ITemplateContext ctx = new TemplateContext() .ErrorWhenValueMissing(); - ctx.DefineLocalVariable("e", new LiquidCollection()); + ctx.DefineLocalVariable("e", new LiquidCollection(new[] { LiquidValue.None, LiquidValue.None, LiquidValue.None })); // Act //var result = RenderingHelper.RenderTemplate("Result : {{ " + varname + " }}", ctx); @@ -48,74 +51,33 @@ public void It_Should_Display_An_Error_When_Dereferencing_Empty_Array(String var var result = template.LiquidTemplate.Render(ctx); // Assert - Assert.Equal("Result : ERROR: cannot dereference empty array", result.Result); - - } - - - - [Fact] - public void It_Should_Display_Error_When_Dereferencing_Array_With_Non_Int() - { - // Arrange - ITemplateContext ctx = new TemplateContext() - .ErrorWhenValueMissing(); - ctx.DefineLocalVariable("e", new LiquidCollection()); - - // Act - var template = LiquidTemplate.Create("Result : {{ e.x }}"); - var result = template.LiquidTemplate.Render(ctx); - //var result = RenderingHelper.RenderTemplate("Result : {{ e.x }}", ctx); - - // Assert - Assert.Contains("invalid index: 'x'", result.Result); - - } - - [Fact] - public void It_Should_Display_Error_When_Dereferencing_Primitive_With_Index() - { - // Arrange - ITemplateContext ctx = new TemplateContext() - .ErrorWhenValueMissing(); - ctx.DefineLocalVariable("e", LiquidString.Create("Hello")); - - // Act - var template = LiquidTemplate.Create("Result : {{ e.x }}"); - var result = template.LiquidTemplate.Render(ctx); - - Assert.True(result.HasRenderingErrors); - var errorMessage = String.Join(",", result.RenderingErrors.Select(x => x.Message)); - // Assert - Assert.Contains("invalid string index: 'x'", errorMessage); - + Assert.Equal("Result : ERROR: " + missingVar + " is undefined", result.Result); } - [Theory] [InlineData("x")] + [InlineData("e.first")] [InlineData("e[1]")] - [InlineData("e.x")] [InlineData("d.x")] [InlineData("d[x]")] - [InlineData("a.b.c")] - public void It_Should_Not_Display_An_Error_When_Dereferencing_Missing_Value(String varname) + public void It_Should_Not_Display_An_Error_When_Values_Are_Missing(String varname) { // Arrange //Console.WriteLine(varname); - TemplateContext ctx = new TemplateContext(); - ctx.DefineLocalVariable("e", new LiquidCollection()); - ctx.DefineLocalVariable("d", new LiquidHash()); + ITemplateContext ctx = new TemplateContext() + .ErrorWhenVariableMissing(); + ctx.DefineLocalVariable("e", new LiquidCollection(new[] { LiquidValue.None, LiquidValue.None, LiquidValue.None })); + ctx.DefineLocalVariable("d", new LiquidHash() + { + { "x", LiquidValue.None } + }); + ctx.DefineLocalVariable("x", LiquidValue.None); // Act var result = RenderingHelper.RenderTemplate("Result : {{ " + varname + " }}", ctx); // Assert Assert.Equal("Result : ", result); - } - - - } } diff --git a/Liquid.NET.Tests/Constants/MissingVariableTests.cs b/Liquid.NET.Tests/Constants/MissingVariableTests.cs new file mode 100644 index 0000000..22aa282 --- /dev/null +++ b/Liquid.NET.Tests/Constants/MissingVariableTests.cs @@ -0,0 +1,121 @@ +using System; +using System.Linq; +using Liquid.NET.Constants; +using Xunit; + +namespace Liquid.NET.Tests.Constants +{ + public class MissingVariableTests + { + [Theory] + [InlineData("x", "x")] + [InlineData("d.x", "x")] + [InlineData("d[x]", "x")] + [InlineData("a.b.c", "a")] + public void It_Should_Display_An_Error_When_Dereferencing_Missing_Variable(String varname, String missingVar) + { + // Arrange + + ITemplateContext ctx = new TemplateContext() + .ErrorWhenVariableMissing(); + ctx.DefineLocalVariable("d", new LiquidHash()); + + // Act + var template = LiquidTemplate.Create("Result : {{ " + varname + " }}"); + var result = template.LiquidTemplate.Render(ctx); + Console.WriteLine(result); + + // Assert + Assert.Equal("Result : ERROR: " + missingVar + " is undefined", result.Result); + + } + + [Theory] + [InlineData("e[1]")] + [InlineData("e.first")] + [InlineData("e.last")] + public void It_Should_Display_An_Error_When_Dereferencing_Empty_Array(String varname) + { + // Arrange + ITemplateContext ctx = new TemplateContext() + .ErrorWhenVariableMissing(); + ctx.DefineLocalVariable("e", new LiquidCollection()); + + // Act + //var result = RenderingHelper.RenderTemplate("Result : {{ " + varname + " }}", ctx); + var template = LiquidTemplate.Create("Result : {{ " + varname + " }}"); + var result = template.LiquidTemplate.Render(ctx); + + // Assert + Assert.Equal("Result : ERROR: cannot dereference empty array", result.Result); + + } + + + + [Fact] + public void It_Should_Display_Error_When_Dereferencing_Array_With_Non_Int() + { + // Arrange + ITemplateContext ctx = new TemplateContext() + .ErrorWhenVariableMissing(); + ctx.DefineLocalVariable("e", new LiquidCollection()); + + // Act + var template = LiquidTemplate.Create("Result : {{ e.x }}"); + var result = template.LiquidTemplate.Render(ctx); + //var result = RenderingHelper.RenderTemplate("Result : {{ e.x }}", ctx); + + // Assert + Assert.Contains("invalid index: 'x'", result.Result); + + } + + [Fact] + public void It_Should_Display_Error_When_Dereferencing_Primitive_With_Index() + { + // Arrange + ITemplateContext ctx = new TemplateContext() + .ErrorWhenVariableMissing(); + ctx.DefineLocalVariable("e", LiquidString.Create("Hello")); + + // Act + var template = LiquidTemplate.Create("Result : {{ e.x }}"); + var result = template.LiquidTemplate.Render(ctx); + + Assert.True(result.HasRenderingErrors); + var errorMessage = String.Join(",", result.RenderingErrors.Select(x => x.Message)); + // Assert + Assert.Contains("invalid string index: 'x'", errorMessage); + + } + + + [Theory] + [InlineData("x")] + [InlineData("e[1]")] + [InlineData("e.x")] + [InlineData("d.x")] + [InlineData("d[x]")] + [InlineData("a.b.c")] + public void It_Should_Not_Display_An_Error_When_Dereferencing_Missing_Variable(String varname) + { + // Arrange + Console.WriteLine(varname); + ITemplateContext ctx = new TemplateContext() + .ErrorWhenValueMissing(); + ctx.DefineLocalVariable("e", new LiquidCollection()); + ctx.DefineLocalVariable("d", new LiquidHash()); + + // Act + var result = RenderingHelper.RenderTemplate("Result : {{ " + varname + " }}", ctx); + + // Assert + Assert.Equal("Result : ", result); + + } + + + + } +} diff --git a/Liquid.NET/src/Constants/IndexDereferencer.cs b/Liquid.NET/src/Constants/IndexDereferencer.cs index e062cdc..171ac8c 100755 --- a/Liquid.NET/src/Constants/IndexDereferencer.cs +++ b/Liquid.NET/src/Constants/IndexDereferencer.cs @@ -42,7 +42,7 @@ public LiquidExpressionResult Lookup( private LiquidExpressionResult DoLookup(ITemplateContext ctx, LiquidCollection liquidCollection, ILiquidValue indexProperty) { - bool errorOnEmpty = ctx.Options.ErrorWhenValueMissing && liquidCollection.Count == 0; + bool errorOnEmpty = ctx.Options.ErrorWhenVariableMissing && liquidCollection.Count == 0; @@ -75,7 +75,7 @@ private LiquidExpressionResult DoLookup(ITemplateContext ctx, LiquidCollection l if (!success) { - if (ctx.Options.ErrorWhenValueMissing) + if (ctx.Options.ErrorWhenVariableMissing) { return LiquidExpressionResult.Error("invalid index: '" + propertyNameString + "'"); } @@ -103,6 +103,11 @@ private LiquidExpressionResult DoLookup(ITemplateContext ctx, LiquidCollection l } var result = liquidCollection.ValueAt(index); + if (!result.HasValue) + { + return LiquidExpressionResult.ErrorOrNone(ctx, propertyNameString); + } + return LiquidExpressionResult.Success(result); } @@ -115,15 +120,22 @@ private LiquidExpressionResult DoLookup(ITemplateContext ctx, LiquidHash liquidH return LiquidExpressionResult.Success(LiquidNumeric.Create(liquidHash.Keys.Count)); } - var valueAt = liquidHash.ValueAt(indexProperty.Value.ToString()); - if (valueAt.HasValue) + var key = indexProperty.Value.ToString(); + if (liquidHash.ContainsKey(key)) { - return LiquidExpressionResult.Success(valueAt); + var value = liquidHash[key]; + if (value.HasValue) + { + return LiquidExpressionResult.Success(value); + } + else + { + return LiquidExpressionResult.ErrorOrNone(ctx, indexProperty.ToString()); + } } else { - return LiquidExpressionResult.ErrorOrNone(ctx, indexProperty.ToString()); - + return LiquidExpressionResult.MissingOrNone(ctx, indexProperty.ToString()); } } @@ -152,7 +164,7 @@ private LiquidExpressionResult DoLookup(ITemplateContext ctx, LiquidString str, if (numericIndexProperty == null) { - return ctx.Options.ErrorWhenValueMissing ? + return ctx.Options.ErrorWhenVariableMissing ? LiquidExpressionResult.Error("invalid string index: '" + propertyNameString + "'") : LiquidExpressionResult.Success(new None()); } @@ -167,7 +179,14 @@ private LiquidExpressionResult DoLookup(ITemplateContext ctx, LiquidString str, //return LiquidExpressionResult.Error("Empty string: " + propertyNameString); return LiquidExpressionResult.Success(new None()); // not an error in Ruby liquid. } - return LiquidExpressionResult.Success(CollectionIndexer.ValueAt(strValues, index)); + + var result = CollectionIndexer.ValueAt(strValues, index); + if (!result.HasValue) + { + return LiquidExpressionResult.ErrorOrNone(ctx, propertyNameString); + } + + return LiquidExpressionResult.Success(result); } diff --git a/Liquid.NET/src/Expressions/VariableReference.cs b/Liquid.NET/src/Expressions/VariableReference.cs index f25d2b6..960beeb 100755 --- a/Liquid.NET/src/Expressions/VariableReference.cs +++ b/Liquid.NET/src/Expressions/VariableReference.cs @@ -19,21 +19,9 @@ public VariableReference(String name) public override LiquidExpressionResult Eval(ITemplateContext templateContext, IEnumerable> childresults) { var lookupResult= templateContext.SymbolTableStack.Reference(Name); - return lookupResult.IsSuccess ? - lookupResult : - ErrorOrNone(templateContext, lookupResult); - } - - private LiquidExpressionResult ErrorOrNone(ITemplateContext templateContext, LiquidExpressionResult failureResult) - { - if (templateContext.Options.ErrorWhenValueMissing) - { - return failureResult; - } - else - { - return LiquidExpressionResult.Success(new None()); - } + return lookupResult.IsSuccess + ? (lookupResult.SuccessResult.HasValue ? lookupResult : LiquidExpressionResult.ErrorOrNone(templateContext, Name)) + : LiquidExpressionResult.MissingOrNone(templateContext, Name); } } } diff --git a/Liquid.NET/src/ITemplateContext.cs b/Liquid.NET/src/ITemplateContext.cs index 59d9f98..a55fa45 100755 --- a/Liquid.NET/src/ITemplateContext.cs +++ b/Liquid.NET/src/ITemplateContext.cs @@ -24,6 +24,8 @@ public interface ITemplateContext ITemplateContext WithNoForLimit(); ITemplateContext WithASTGenerator(Func, LiquidAST> astGeneratorFunc); + ITemplateContext ErrorWhenValueMissing(); + ITemplateContext ErrorWhenVariableMissing(); IFileSystem FileSystem { get; } IDictionary Registers { get; } diff --git a/Liquid.NET/src/TemplateContext.cs b/Liquid.NET/src/TemplateContext.cs index dc68f62..3234541 100755 --- a/Liquid.NET/src/TemplateContext.cs +++ b/Liquid.NET/src/TemplateContext.cs @@ -263,12 +263,19 @@ public ITemplateContext ErrorWhenValueMissing() _options.ErrorWhenValueMissing = true; return this; } + + public ITemplateContext ErrorWhenVariableMissing() + { + _options.ErrorWhenVariableMissing = true; + return this; + } } public class LiquidOptions { public bool NoForLimit { get; internal set; } public bool ErrorWhenValueMissing { get; set; } + public bool ErrorWhenVariableMissing { get; set; } } } diff --git a/Liquid.NET/src/Utils/LiquidExpressionResult.cs b/Liquid.NET/src/Utils/LiquidExpressionResult.cs index 8e00bf3..eda6539 100755 --- a/Liquid.NET/src/Utils/LiquidExpressionResult.cs +++ b/Liquid.NET/src/Utils/LiquidExpressionResult.cs @@ -27,6 +27,18 @@ public static LiquidExpressionResult ErrorOrNone(ITemplateContext ctx, String va } } + public static LiquidExpressionResult MissingOrNone(ITemplateContext ctx, String varname) + { + if (ctx.Options.ErrorWhenVariableMissing) + { + return Error(SymbolTable.NotFoundError(varname)); + } + else + { + return Success(new None()); + } + } + public LiquidExpressionResult WhenError(Action fn) { if (IsLeft)