-
Notifications
You must be signed in to change notification settings - Fork 15
Truthiness
Shopify's basic definition of truthiness is fairly straightforward: nil and false are FALSE and everything else is TRUE.
However, Shopify liquid has evolved a proliferation of inconsistent ad-hoc value comparison strategies, many of which confuse most designers and developers. For example, some comparisons will act differently depending on which Ruby libraries are enabled, meaning that liquid code is not portable from one environment to another. Also, some comparisons will return a value which is neither true nor false. Some comparisons are inconsistent when compared with nil. Some liquid types have Liskov violations and depend on underlying object implementations of the object being compared---and sometimes will return an unexpected error. Worse, comparisons may return different results depending on whitespace around the comparison operator. Also, there's a difference between variables which are unset and return something called an EmptyDrop (i.e. "truthy" but "empty"), and values that are nil ("not truthy" but "not empty" either).
Ultimately, Liquid.NET is mostly compatible with Shopify Liquid, but greatly simplifies the rules to minimize the number of unpleasant surprises.
Firstly, all variables in Liquid.Net are either string, numeric, boolean, collection, hash, date or the special range type (which won't be discussed here). A variable can take the value nil. There's no such thing as a "Drop" or any sort of custom object, so any object-like thing you encounter will have already been converted to a hash. Unset values are nil.
The simplest comparison uses no operators---it just checks for the truthy value of a variable, i.e. whether it's nil or false.
{% if '1' %}truthy{% else %}falsy{% endif %}
--> truthy
{% if false %}truthy{% else %}falsy{% endif %}
--> falsy
Comparing two values with == will check to see if the type and value are the same.
{% if 3 == 3 %}truthy{% else %}falsy{% endif %}
--> truthy
{% if 3 == '3' %}truthy{% else %}falsy{% endif %}
--> falsySome keywords in ActiveSupport-enhanced Shopify Liquid are empty, blank, present and nil. You can check to see if a variable IS empty, blank, present or nil by using the comparison operator. To be clear, {% if x == empty %} does not mean if value x equals the value empty, instead it means if value x is empty. You might have noticed some people using the Ruby-ish syntax {% x.empty? %}, {% x.blank? %}, or {% x.present? %}.
In Liquid.NET, the "?" operators (such as .blank? work exactly as do the equivalent "double equals" operators (such as == blank), and they operate on all values. Liquid.NET also allows the use of "!=".
The empty, blank, present keywords return true when used with the == operator the following cases:
-
empty: Returns true if 1) a value isnil, OR 2) a value is an emptystringwithout whitespace OR 3) The value iscollectionwith no elements OR 4) The value is ahashwith no keys. -
blank: Returns true if 1) a value isnil, OR 2) a value is an emptystringor only whitespace OR 3) The value iscollectionwith no elements OR 4) The value is ahashwith no keys. You would useblankwhere you would useString.IsNullOrWhiteSpace()in C# on strings, or! x.Any()on collections. -
present: returns true if the value is not blank. Present is intended to be used as "not nil and not whitespace", i.e. "Is there a value present that can be displayed".
In my experience, the main source of confusion in Shopify liquid is that the "==" operator is overloaded and does two different things. Whenever you see == empty, == blank or == present, you should think "IS empty", "IS blank" or "IS present". If you see myvar == nil, or myvar == true you are specifically checking if myvar has the value nil or myvar is false.
Another way of looking at it is that in Liquid.NET, == works like JavaScript's === operator in that it does not do type casting, except when used as an alias for checking for blank, empty, or present.