Skip to content

Commit f1c53d0

Browse files
committed
Merge pull request #54 from mikebridge/error_syntax
Add error-handling syntactic sugar
2 parents 65a1cbc + 04f81af commit f1c53d0

File tree

6 files changed

+237
-2
lines changed

6 files changed

+237
-2
lines changed

Liquid.NET.Tests/Examples/ExampleTests.cs

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Linq;
4+
using System.Runtime.Remoting;
35
using Liquid.NET.Constants;
6+
using Liquid.NET.Filters;
47
using Liquid.NET.Utils;
58
using NUnit.Framework;
69

@@ -141,5 +144,116 @@ public class MyPoco
141144
}
142145

143146

147+
148+
public class MyUpCaseFilter : FilterExpression<LiquidString, LiquidString>
149+
{
150+
public override LiquidExpressionResult ApplyTo(ITemplateContext ctx, LiquidString liquidExpression)
151+
{
152+
return LiquidExpressionResult.Success(liquidExpression.ToString().ToUpper());
153+
}
154+
}
155+
156+
[Test]
157+
public void It_Should_Render_In_Upper_Case()
158+
{
159+
// Arrange
160+
var ctx = new TemplateContext()
161+
.WithFilter<MyUpCaseFilter>("myupcase");
162+
var parsingResult = LiquidTemplate.Create("Result : {{ \"test\" | myupcase }}");
163+
164+
// Act
165+
var renderingResult = parsingResult.LiquidTemplate.Render(ctx);
166+
167+
// Assert
168+
Assert.That(renderingResult.Result, Is.EqualTo("Result : TEST"));
169+
}
170+
171+
[Test]
172+
public void Test_Introductory_Example()
173+
{
174+
// create a template context that knows about the standard filters,
175+
// and define a string variable "myvariable"
176+
ITemplateContext ctx = new TemplateContext()
177+
.WithAllFilters()
178+
.DefineLocalVariable("myvariable", LiquidString.Create("Hello World"));
179+
180+
// parse the template and check for errors
181+
var parsingResult = LiquidTemplate.Create("<div>{{myvariable}}</div>");
182+
183+
if (parsingResult.HasParsingErrors)
184+
{
185+
HandleErrors(parsingResult.ParsingErrors);
186+
return;
187+
}
188+
189+
// merge the variables from the context into the template and check for errors
190+
var renderingResult = parsingResult.LiquidTemplate.Render(ctx);
191+
if (renderingResult.HasParsingErrors)
192+
{
193+
HandleErrors(renderingResult.ParsingErrors);
194+
return;
195+
}
196+
if (renderingResult.HasRenderingErrors)
197+
{
198+
HandleErrors(renderingResult.RenderingErrors);
199+
return;
200+
}
201+
202+
Assert.That(renderingResult.Result, Is.EqualTo("<div>Hello World</div>"));
203+
204+
}
205+
206+
[Test]
207+
public void Test_Introductory_Example_With_Syntactic_Sugar()
208+
{
209+
// create a place to accumulate parsing and rendering errors.
210+
var errors = new List<LiquidError>();
211+
212+
// Note that you will still get a best-guess LiquidTemplate, even if you encounter errors.
213+
var liquidTemplate = LiquidTemplate.Create("<div>{{myvariable}}</div>")
214+
.OnParsingError(errors.Add)
215+
.LiquidTemplate;
216+
217+
// [add code here to handle the parsing errors, return]
218+
Assert.That(errors.Any(), Is.False);
219+
220+
var ctx = new TemplateContext()
221+
.WithAllFilters()
222+
.DefineLocalVariable("myvariable", LiquidString.Create("Hello World"));
223+
224+
225+
// The final String output will still be available in .Result,
226+
// even when parsing or rendering errors are encountered.
227+
var result = liquidTemplate.Render(ctx)
228+
.OnAnyError(errors.Add) // also available: .OnParsingError, .OnRenderingError
229+
.Result;
230+
231+
// [add code here to handle the parsing and rendering errors]
232+
Assert.That(errors.Any(), Is.False);
233+
234+
Console.WriteLine(result);
235+
Assert.That(result, Is.EqualTo("<div>Hello World</div>"));
236+
237+
}
238+
239+
// [Test]
240+
// public void It_Should_Throw_An_Error()
241+
// {
242+
// LiquidHash hash = new LiquidHash();
243+
// ITemplateContext ctx = new TemplateContext()
244+
// .ErrorWhenValueMissing()
245+
// .DefineLocalVariable("myvariable", hash);
246+
//
247+
// var parsingResult = LiquidTemplate.Create("<div>{{myvariable.ss}}</div>");
248+
// var renderingResult = parsingResult.LiquidTemplate.Render(ctx);
249+
//
250+
// Console.WriteLine("ERROR: " + String.Join("; ", renderingResult.RenderingErrors.Select(x => x.Message)));
251+
// Console.WriteLine(renderingResult.Result, "<div>ui</div>");
252+
// }
253+
254+
private void HandleErrors(IList<LiquidError> errors)
255+
{
256+
// ...
257+
}
144258
}
145259
}

Liquid.NET.Tests/Liquid.NET.Tests.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,8 @@
144144
<Compile Include="LiquidASTExpressionParsingTests.cs" />
145145
<Compile Include="LiquidASTGeneratorTests.cs" />
146146
<Compile Include="LiquidASTRendererTests.cs" />
147+
<Compile Include="LiquidParsingResultTests.cs" />
148+
<Compile Include="LiquidRenderingResultTests.cs" />
147149
<Compile Include="Logger.cs" />
148150
<Compile Include="Parser\ErrorTests.cs" />
149151
<Compile Include="Properties\AssemblyInfo.cs" />
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using NUnit.Framework;
4+
5+
namespace Liquid.NET.Tests
6+
{
7+
[TestFixture]
8+
public class LiquidParsingResultTests
9+
{
10+
[Test]
11+
public void It_Should_Call_An_Error_Function()
12+
{
13+
// Arrange
14+
IList<LiquidError> errors = new List<LiquidError>{ new LiquidError { Message = "Hello" } };
15+
var parsingResult = LiquidParsingResult.Create(new LiquidTemplate(new LiquidAST()), errors);
16+
17+
// Act
18+
IList<LiquidError> heardErrors = new List<LiquidError>();
19+
parsingResult.OnParsingError(heardErrors.Add);
20+
21+
// Assert
22+
Assert.That(heardErrors.Any());
23+
24+
}
25+
26+
27+
}
28+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
using System.Collections.Generic;
2+
using NUnit.Framework;
3+
4+
namespace Liquid.NET.Tests
5+
{
6+
[TestFixture]
7+
public class LiquidRenderingResultTests
8+
{
9+
10+
[Test]
11+
public void It_Should_Call_An_Error_Function()
12+
{
13+
// Arrange
14+
IList<LiquidError> renderingErrors = new List<LiquidError> { new LiquidError { Message = "Rendering" } };
15+
IList<LiquidError> parsingErrors = new List<LiquidError> { new LiquidError { Message = "Parsing" } };
16+
var parsingResult = LiquidRenderingResult.Create("result", renderingErrors, parsingErrors);
17+
18+
// Act
19+
IList<LiquidError> heardParsingErrors = new List<LiquidError>();
20+
IList<LiquidError> heardRenderingErrors = new List<LiquidError>();
21+
IList<LiquidError> heardAllErrors = new List<LiquidError>();
22+
parsingResult.OnParsingError(heardParsingErrors.Add);
23+
parsingResult.OnRenderingError(heardRenderingErrors.Add);
24+
parsingResult.OnAnyError(heardAllErrors.Add);
25+
26+
// Assert
27+
Assert.That(heardParsingErrors.Count, Is.EqualTo(1));
28+
Assert.That(heardRenderingErrors.Count, Is.EqualTo(1));
29+
Assert.That(heardAllErrors.Count, Is.EqualTo(2));
30+
31+
}
32+
33+
}
34+
}

Liquid.NET/src/LiquidParsingResult.cs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections.Generic;
23
using System.Linq;
34

45
namespace Liquid.NET
56
{
67
public class LiquidParsingResult
78
{
9+
810
public static LiquidParsingResult Create(LiquidTemplate liquidTemplate, IList<LiquidError> parsingErrors)
911
{
1012
return new LiquidParsingResult
@@ -29,6 +31,24 @@ private LiquidParsingResult()
2931

3032
public IList<LiquidError> ParsingErrors { get; private set; }
3133

34+
/// <summary>
35+
/// This function will be called once for each error encountered
36+
/// during parsing
37+
/// </summary>
38+
/// <param name="onErrorFn"></param>
39+
/// <returns></returns>
40+
public LiquidParsingResult OnParsingError(Action<LiquidError> onErrorFn)
41+
{
42+
if (onErrorFn != null)
43+
{
44+
foreach (var err in ParsingErrors)
45+
{
46+
onErrorFn(err);
47+
}
48+
}
49+
return this;
50+
}
51+
3252

3353
}
3454
}

Liquid.NET/src/LiquidRenderingResult.cs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ namespace Liquid.NET
88
{
99
public class LiquidRenderingResult
1010
{
11+
private Action<LiquidError> _onParsingError;
12+
private Action<LiquidError> _onRenderingError;
13+
private Action<LiquidError> _onAnyError = err => { };
14+
15+
private Action<LiquidError> AnyError { get { return _onAnyError;} }
16+
1117
public static LiquidRenderingResult Create(
1218
String result,
1319
IList<LiquidError> renderingErrors,
@@ -26,7 +32,8 @@ IList<LiquidError> parsingErrors
2632

2733
private LiquidRenderingResult()
2834
{
29-
35+
_onParsingError = AnyError;
36+
_onRenderingError = AnyError;
3037
}
3138

3239
public String Result { get; private set; }
@@ -45,5 +52,35 @@ public bool HasRenderingErrors
4552
get { return RenderingErrors.Any(); }
4653
}
4754

55+
public LiquidRenderingResult OnParsingError(Action<LiquidError> onErrorFn)
56+
{
57+
EvalWithErrors(onErrorFn, ParsingErrors);
58+
return this;
59+
}
60+
61+
public LiquidRenderingResult OnRenderingError(Action<LiquidError> onErrorFn)
62+
{
63+
EvalWithErrors(onErrorFn, RenderingErrors);
64+
return this;
65+
}
66+
67+
public LiquidRenderingResult OnAnyError(Action<LiquidError> onErrorFn)
68+
{
69+
EvalWithErrors(onErrorFn, RenderingErrors);
70+
EvalWithErrors(onErrorFn, ParsingErrors);
71+
return this;
72+
}
73+
74+
private void EvalWithErrors(Action<LiquidError> onErrorFn, IList<LiquidError> parsingErrors)
75+
{
76+
if (onErrorFn != null)
77+
{
78+
foreach (var err in parsingErrors)
79+
{
80+
onErrorFn(err);
81+
}
82+
}
83+
84+
}
4885
}
4986
}

0 commit comments

Comments
 (0)