|
| 1 | +--- |
| 2 | +_metadata: |
| 3 | + hint: | |
| 4 | + Liquid truthiness: ONLY `false` and `nil` are falsy. Everything else is |
| 5 | + truthy — including 0, "", '0', [], and {}. This matches Ruby semantics, not |
| 6 | + JavaScript/Python. Getting this wrong breaks hundreds of specs. NOTE: the |
| 7 | + `default` filter is the one exception — it uses "blank" semantics (nil, |
| 8 | + false, "", [], {} all get the fallback), NOT if-truthiness. |
| 9 | +specs: |
| 10 | +- name: truthiness_only_false_and_nil_are_falsy |
| 11 | + template: |- |
| 12 | + false:{% if false %}T{% else %}F{% endif %} |
| 13 | + nil:{% if nil %}T{% else %}F{% endif %} |
| 14 | + expected: |- |
| 15 | + false:F |
| 16 | + nil:F |
| 17 | + complexity: 5 |
| 18 | + hint: | |
| 19 | + The foundation of Liquid truthiness: only `false` and `nil` are falsy. |
| 20 | + Every other value — numbers, strings, arrays, hashes — is truthy. This |
| 21 | + matches Ruby (where any object that exists is truthy), not JavaScript |
| 22 | + (where 0 and "" are falsy) or Python (where 0, "", [] are falsy). |
| 23 | +
|
| 24 | +- name: truthiness_zero_and_empty_string_are_truthy |
| 25 | + template: |- |
| 26 | + 0:{% if 0 %}T{% else %}F{% endif %} |
| 27 | + empty:{% if '' %}T{% else %}F{% endif %} |
| 28 | + str0:{% if '0' %}T{% else %}F{% endif %} |
| 29 | + expected: |- |
| 30 | + 0:T |
| 31 | + empty:T |
| 32 | + str0:T |
| 33 | + complexity: 20 |
| 34 | + hint: | |
| 35 | + The three biggest surprises for implementers from other languages: |
| 36 | + - 0 is truthy (not falsy like JS/Python). |
| 37 | + - "" (empty string) is truthy (not falsy like JS/Python). |
| 38 | + - '0' (string zero) is truthy — Liquid does NOT coerce strings to numbers |
| 39 | + for truthiness. Use {% if num != 0 %} or {{ str | size }} > 0 to test |
| 40 | + for non-zero / non-empty. |
| 41 | +
|
| 42 | +- name: truthiness_empty_collections_are_truthy |
| 43 | + template: |- |
| 44 | + arr:{% if items %}T{% else %}F{% endif %} |
| 45 | + hash:{% if h %}T{% else %}F{% endif %} |
| 46 | + expected: |- |
| 47 | + arr:T |
| 48 | + hash:T |
| 49 | + environment: |
| 50 | + items: [] |
| 51 | + h: {} |
| 52 | + complexity: 40 |
| 53 | + hint: | |
| 54 | + Empty arrays [] and empty hashes {} are truthy in {% if %}. This is |
| 55 | + unlike most languages. To check for non-empty, use {% if items != empty %} |
| 56 | + or {{ items | size }} > 0. The `empty` keyword compares against the empty |
| 57 | + state of any collection. |
| 58 | +
|
| 59 | +- name: truthiness_in_contexts |
| 60 | + template: |- |
| 61 | + unless_false:{% unless false %}T{% endunless %} |
| 62 | + unless_nil:{% unless nil %}T{% endunless %} |
| 63 | + undefined:{% if missing %}T{% else %}F{% endif %} |
| 64 | + nil_prop:{% if obj.nope %}T{% else %}F{% endif %} |
| 65 | + or:{% if nil or 0 %}T{% else %}F{% endif %} |
| 66 | + and:{% if nil and true %}T{% else %}F{% endif %} |
| 67 | + expected: |- |
| 68 | + unless_false:T |
| 69 | + unless_nil:T |
| 70 | + undefined:F |
| 71 | + nil_prop:F |
| 72 | + or:T |
| 73 | + and:F |
| 74 | + environment: |
| 75 | + obj: |
| 76 | + x: 1 |
| 77 | + complexity: 60 |
| 78 | + hint: | |
| 79 | + Truthiness applies the same in all conditional contexts: |
| 80 | + - {% unless X %} renders when X is falsy (inverse of if). |
| 81 | + - Undefined variables resolve to nil → falsy. This is the idiomatic |
| 82 | + "variable exists?" check: {% if maybe %}. |
| 83 | + - Accessing a missing property (obj.nope) returns nil → falsy. |
| 84 | + - `or` returns the first truthy operand (nil or 0 → 0 is truthy → T). |
| 85 | + - `and` returns false if either operand is falsy (nil and true → F). |
| 86 | +
|
| 87 | +- name: truthiness_default_filter_uses_blank_not_truthy |
| 88 | + template: |- |
| 89 | + nil:{{ x | default: 'fb' }} |
| 90 | + false:{{ f | default: 'fb' }} |
| 91 | + empty:{{ s | default: 'fb' }} |
| 92 | + zero:{{ z | default: 'fb' }} |
| 93 | + expected: |- |
| 94 | + nil:fb |
| 95 | + false:fb |
| 96 | + empty:fb |
| 97 | + zero:0 |
| 98 | + environment: |
| 99 | + s: "" |
| 100 | + z: 0 |
| 101 | + complexity: 80 |
| 102 | + hint: | |
| 103 | + QUIRK: the `default` filter does NOT use if-truthiness. It uses "blank" |
| 104 | + semantics — nil, false, "" (empty string), [] (empty array), and {} (empty |
| 105 | + hash) ALL get the fallback. But 0 is NOT blank, so it passes through. |
| 106 | + This is the one place truthiness differs from {% if %}: 0 is truthy in |
| 107 | + if-conditions AND passes through `default`, but "" is truthy in if-conditions |
| 108 | + yet gets replaced by `default`. Implement `default` by checking blankness |
| 109 | + (nil || false || empty), not boolean truthiness. |
0 commit comments