Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions source/Handlebars.Test/BasicIntegrationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1439,6 +1439,38 @@ public void BasicNumericTruthy(IHandlebars handlebars)
Assert.Equal("Hello, Truthy!", result);
}

[Theory, ClassData(typeof(HandlebarsEnvGenerator))]
public void BasicNumericZeroWhenIncludeZeroTrue(IHandlebars handlebars)
{
string source = "Hello, {{#if zero includeZero=true}}Truthy!{{/if}}";

var template = handlebars.Compile(source);

var data = new
{
zero = 0
};

var result = template(data);
Assert.Equal("Hello, Truthy!", result);
}

[Theory, ClassData(typeof(HandlebarsEnvGenerator))]
public void BasicNumericZeroWhenIncludeZeroFalse(IHandlebars handlebars)
{
string source = "Hello, {{#if zero includeZero=false}}Truthy!{{/if}}";

var template = handlebars.Compile(source);

var data = new
{
zero = 0
};

var result = template(data);
Assert.Equal("Hello, ", result);
}

[Theory, ClassData(typeof(HandlebarsEnvGenerator))]
public void BasicStringFalsy(IHandlebars handlebars)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ public ConditionalBlockAccumulatorContext(Expression startingNode)
throw new HandlebarsCompilerException($"Tried to convert {BlockName} expression to conditional block", helperExpression.Context);
}

var argument = HandlebarsExpression.Boolish(helperExpression.Arguments.Single());
var (value, hashParameters) = UnwrapBoolishHelperArguments(helperExpression.Arguments, helperExpression.Context);
var argument = HandlebarsExpression.Boolish(value, hashParameters);

_currentCondition = BlockName switch
{
Expand Down Expand Up @@ -115,7 +116,9 @@ private bool IsElseIfBlock(Expression item)
private Expression GetElseIfTestExpression(Expression item)
{
item = UnwrapStatement(item);
return HandlebarsExpression.Boolish(((HelperExpression)item).Arguments.Skip(1).Single());
var helperExpression = (HelperExpression)item;
var (value, hashParameters) = UnwrapBoolishHelperArguments(helperExpression.Arguments.Skip(1), helperExpression.Context);
return HandlebarsExpression.Boolish(value, hashParameters);
}

private bool IsClosingNode(Expression item)
Expand All @@ -133,6 +136,24 @@ private static Expression SinglifyExpressions(IEnumerable<Expression> expression

return expressions.SingleOrDefault() ?? Expression.Empty();
}

private static (Expression Value, HashParametersExpression HashParameters) UnwrapBoolishHelperArguments(IEnumerable<Expression> arguments, IReaderContext context)
{
var value = arguments.First();
if (arguments.Count() == 1)
{
var blankHashParameters = HandlebarsExpression.HashParametersExpression(new Dictionary<string, Expression>());
return (value, blankHashParameters);
}

if (arguments.Count() == 2 && arguments.Last() is HashParametersExpression hashParameters)
{
return (value, hashParameters);
}

throw new HandlebarsCompilerException("Invalid argument list for conditional block.", context);

}
}
}

5 changes: 4 additions & 1 deletion source/Handlebars/Compiler/Structure/BoolishExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ namespace HandlebarsDotNet.Compiler
{
internal class BoolishExpression : HandlebarsExpression
{
public BoolishExpression(Expression condition)
public BoolishExpression(Expression condition, HashParametersExpression hashParameters)
{
Condition = condition;
HashParameters = hashParameters;
}

public new Expression Condition { get; }

public HashParametersExpression HashParameters { get; set; }

public override ExpressionType NodeType => (ExpressionType)HandlebarsExpressionType.BoolishExpression;

public override Type Type => typeof(bool);
Expand Down
4 changes: 2 additions & 2 deletions source/Handlebars/Compiler/Structure/HandlebarsExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,9 @@ public static PartialExpression Partial(Expression partialName, Expression argum
return new PartialExpression(partialName, argument, fallback);
}

public static BoolishExpression Boolish(Expression condition)
public static BoolishExpression Boolish(Expression condition, HashParametersExpression hashParameters)
{
return new BoolishExpression(condition);
return new BoolishExpression(condition, hashParameters);
}

public static SubExpressionExpression SubExpression(Expression expression)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using static Expressions.Shortcuts.ExpressionShortcuts;

Expand All @@ -11,13 +13,24 @@ public BoolishConverter(CompilationContext compilationContext)
{
_compilationContext = compilationContext;
}


private const string IncludeZero = "includeZero";

protected override Expression VisitBoolishExpression(BoolishExpression bex)
{
var condition = Visit(bex.Condition);
condition = FunctionBuilder.Reduce(condition, _compilationContext, out _);
var hashParameters = (HashParametersExpression)VisitHashParametersExpression(bex.HashParameters);
// Assert that if there is a hashParameter, it is "includeZero" and has a boolean value
if (hashParameters.Parameters.Count > 1 || (hashParameters.Parameters.Count == 1 && !hashParameters.Parameters.ContainsKey(IncludeZero)))
{
throw new HandlebarsCompilerException($"Boolish expression can only have up to one hash parameter, '{IncludeZero}'");
}

var @object = Arg<object>(condition);
return Call(() => HandlebarsUtils.IsTruthyOrNonEmpty(@object));
var includeZero = Arg<bool>(hashParameters.Parameters.Count == 1 ? hashParameters.Parameters[IncludeZero] : Expression.Constant(false));
return Call(() => HandlebarsUtils.IsTruthyOrNonEmpty(@object, includeZero));

}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,10 @@ protected virtual Expression VisitPartialExpression(PartialExpression pex)
protected virtual Expression VisitBoolishExpression(BoolishExpression bex)
{
Expression condition = Visit(bex.Condition);
HashParametersExpression hashParameters = (HashParametersExpression)VisitHashParametersExpression(bex.HashParameters);
if (condition != bex.Condition)
{
return HandlebarsExpression.Boolish(condition);
return HandlebarsExpression.Boolish(condition, hashParameters);
}
return bex;
}
Expand Down
20 changes: 11 additions & 9 deletions source/Handlebars/HandlebarsUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,20 @@ public static class HandlebarsUtils
/// Implementation of JS's `==`
/// </summary>
/// <param name="value"></param>
/// <param name="includeZero">Determined whether the numerical <c>0</c> is considered to be truthy. Default <c>false</c></param>
/// <returns></returns>
public static bool IsTruthy(object value)
public static bool IsTruthy(object value, bool includeZero = false)
{
return !IsFalsy(value);
return !IsFalsy(value, includeZero);
}

/// <summary>
/// Implementation of JS's `!=`
/// </summary>
/// <param name="value"></param>
/// <param name="value">The value whose falsy-ness is to be determined</param>
/// <param name="includeZero">Determined whether the numerical <c>0</c> is considered to be truthy</param>
/// <returns></returns>
public static bool IsFalsy(object value)
public static bool IsFalsy(object value, bool includeZero)
{
switch (value)
{
Expand All @@ -34,21 +36,21 @@ public static bool IsFalsy(object value)
return s == string.Empty;
}

if (IsNumber(value))
if (IsNumber(value) && !includeZero)
{
return !Convert.ToBoolean(value);
}
return false;
}

public static bool IsTruthyOrNonEmpty(object value)
public static bool IsTruthyOrNonEmpty(object value, bool includeZero = false)
{
return !IsFalsyOrEmpty(value);
return !IsFalsyOrEmpty(value, includeZero);
}

public static bool IsFalsyOrEmpty(object value)
public static bool IsFalsyOrEmpty(object value, bool includeZero = false)
{
if(IsFalsy(value))
if(IsFalsy(value, includeZero))
{
return true;
}
Expand Down
Loading