Three related behaviors diverge from Jinja2 on templates that use bare builtin names, boolean operators, or undefined operands.
- Bare builtin names resolve to functions in the run scope, and functions are truthy. Jinja2 does not expose filters or tests as variables, so those names are
Undefined (falsy):
Jinja2 renders empty. ginger renders 1.
or and and compile to any/all and return a boolean instead of the deciding operand:
with empty = []: Jinja2 renders [], ginger renders empty.
- Comparisons involving undefined or null operands compare by truthiness:
Jinja2 renders False. ginger renders 1.
Root cause
defaultScope is seeded into the run scope (src/Text/Ginger/Run.hs:641-650); fromFunction gives functions asBoolean = True (src/Text/Ginger/GVal.hs:686-695); or/and compile to any/all (src/Text/Ginger/Parse.hs:1072-1080, src/Text/Ginger/Run/Builtins.hs:136-140); looseEquals compares null operands by truthiness (src/Text/Ginger/Run/Builtins.hs:200-209, line 208).
Three related behaviors diverge from Jinja2 on templates that use bare builtin names, boolean operators, or undefined operands.
Undefined(falsy):{{ f or divisibleby }}Jinja2 renders empty. ginger renders
1.orandandcompile toany/alland return a boolean instead of the deciding operand:{{ f or empty }}with
empty = []: Jinja2 renders[], ginger renders empty.{{ number != upper }}Jinja2 renders
False. ginger renders1.Root cause
defaultScopeis seeded into the run scope (src/Text/Ginger/Run.hs:641-650);fromFunctiongives functionsasBoolean = True(src/Text/Ginger/GVal.hs:686-695);or/andcompile toany/all(src/Text/Ginger/Parse.hs:1072-1080,src/Text/Ginger/Run/Builtins.hs:136-140);looseEqualscompares null operands by truthiness (src/Text/Ginger/Run/Builtins.hs:200-209, line 208).