mirror of
https://github.com/Shopify/liquid.git
synced 2026-10-03 09:05:13 -07:00
Fix MethodLiteral for updated blank & empty
This commit is contained in:
@@ -72,7 +72,7 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
def apply_method_literal(node, other)
|
def apply_method_literal(node, other)
|
||||||
other.send(node.method_name) if other.respond_to?(node.method_name)
|
node.apply(other)
|
||||||
end
|
end
|
||||||
|
|
||||||
def equal_variables(left, right)
|
def equal_variables(left, right)
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ module Liquid
|
|||||||
'' => nil,
|
'' => nil,
|
||||||
'true' => true,
|
'true' => true,
|
||||||
'false' => false,
|
'false' => false,
|
||||||
'blank' => MethodLiteral.new(:blank?, '').freeze,
|
'blank' => MethodLiteral::BLANK,
|
||||||
'empty' => MethodLiteral.new(:empty?, '').freeze,
|
'empty' => MethodLiteral::EMPTY,
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
DOT = ".".ord
|
DOT = ".".ord
|
||||||
|
|||||||
@@ -4,13 +4,46 @@ module Liquid
|
|||||||
class MethodLiteral
|
class MethodLiteral
|
||||||
attr_reader :method_name, :to_s
|
attr_reader :method_name, :to_s
|
||||||
|
|
||||||
def initialize(method_name, to_s)
|
def initialize(method_name, to_s, &evaluator)
|
||||||
@method_name = method_name
|
@method_name = method_name
|
||||||
@to_s = to_s
|
@to_s = to_s
|
||||||
|
@evaluator = evaluator
|
||||||
|
end
|
||||||
|
|
||||||
|
def apply(value)
|
||||||
|
if value.respond_to?(@method_name)
|
||||||
|
value.send(@method_name)
|
||||||
|
elsif @evaluator
|
||||||
|
@evaluator.call(value)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_liquid
|
def to_liquid
|
||||||
to_s
|
to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
|
BLANK = MethodLiteral.new(:blank?, '') do |value|
|
||||||
|
case value
|
||||||
|
when NilClass, FalseClass
|
||||||
|
true
|
||||||
|
when TrueClass, Numeric
|
||||||
|
false
|
||||||
|
when String
|
||||||
|
value.empty? || value.match?(/\A\s*\z/)
|
||||||
|
when Array, Hash
|
||||||
|
value.empty?
|
||||||
|
else
|
||||||
|
value.respond_to?(:empty?) ? value.empty? : false
|
||||||
|
end
|
||||||
|
end.freeze
|
||||||
|
|
||||||
|
EMPTY = MethodLiteral.new(:empty?, '') do |value|
|
||||||
|
case value
|
||||||
|
when String, Array, Hash
|
||||||
|
value.empty?
|
||||||
|
else
|
||||||
|
value.respond_to?(:empty?) ? value.empty? : nil
|
||||||
|
end
|
||||||
|
end.freeze
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -197,7 +197,7 @@ class ConditionUnitTest < Minitest::Test
|
|||||||
# Template authors expect " " to be blank since it has no visible content.
|
# Template authors expect " " to be blank since it has no visible content.
|
||||||
# This matches ActiveSupport's String#blank? which returns true for whitespace-only strings.
|
# This matches ActiveSupport's String#blank? which returns true for whitespace-only strings.
|
||||||
@context['whitespace'] = ' '
|
@context['whitespace'] = ' '
|
||||||
blank_literal = Condition.class_variable_get(:@@method_literals)['blank']
|
blank_literal = Expression::LITERALS['blank']
|
||||||
|
|
||||||
assert_evaluates_true(VariableLookup.parse('whitespace'), '==', blank_literal)
|
assert_evaluates_true(VariableLookup.parse('whitespace'), '==', blank_literal)
|
||||||
end
|
end
|
||||||
@@ -206,7 +206,7 @@ class ConditionUnitTest < Minitest::Test
|
|||||||
# An empty string has no content, so it should be considered blank.
|
# An empty string has no content, so it should be considered blank.
|
||||||
# This is the most basic case of a blank string.
|
# This is the most basic case of a blank string.
|
||||||
@context['empty_string'] = ''
|
@context['empty_string'] = ''
|
||||||
blank_literal = Condition.class_variable_get(:@@method_literals)['blank']
|
blank_literal = Expression::LITERALS['blank']
|
||||||
|
|
||||||
assert_evaluates_true(VariableLookup.parse('empty_string'), '==', blank_literal)
|
assert_evaluates_true(VariableLookup.parse('empty_string'), '==', blank_literal)
|
||||||
end
|
end
|
||||||
@@ -215,7 +215,7 @@ class ConditionUnitTest < Minitest::Test
|
|||||||
# Empty arrays have no elements, so they are blank.
|
# Empty arrays have no elements, so they are blank.
|
||||||
# Useful for checking if a collection has items: {% if products == blank %}
|
# Useful for checking if a collection has items: {% if products == blank %}
|
||||||
@context['empty_array'] = []
|
@context['empty_array'] = []
|
||||||
blank_literal = Condition.class_variable_get(:@@method_literals)['blank']
|
blank_literal = Expression::LITERALS['blank']
|
||||||
|
|
||||||
assert_evaluates_true(VariableLookup.parse('empty_array'), '==', blank_literal)
|
assert_evaluates_true(VariableLookup.parse('empty_array'), '==', blank_literal)
|
||||||
end
|
end
|
||||||
@@ -224,7 +224,7 @@ class ConditionUnitTest < Minitest::Test
|
|||||||
# Empty hashes have no key-value pairs, so they are blank.
|
# Empty hashes have no key-value pairs, so they are blank.
|
||||||
# Useful for checking if settings/options exist: {% if settings == blank %}
|
# Useful for checking if settings/options exist: {% if settings == blank %}
|
||||||
@context['empty_hash'] = {}
|
@context['empty_hash'] = {}
|
||||||
blank_literal = Condition.class_variable_get(:@@method_literals)['blank']
|
blank_literal = Expression::LITERALS['blank']
|
||||||
|
|
||||||
assert_evaluates_true(VariableLookup.parse('empty_hash'), '==', blank_literal)
|
assert_evaluates_true(VariableLookup.parse('empty_hash'), '==', blank_literal)
|
||||||
end
|
end
|
||||||
@@ -233,7 +233,7 @@ class ConditionUnitTest < Minitest::Test
|
|||||||
# nil represents "nothing" and is the canonical blank value.
|
# nil represents "nothing" and is the canonical blank value.
|
||||||
# Unassigned variables resolve to nil, so this enables: {% if missing_var == blank %}
|
# Unassigned variables resolve to nil, so this enables: {% if missing_var == blank %}
|
||||||
@context['nil_value'] = nil
|
@context['nil_value'] = nil
|
||||||
blank_literal = Condition.class_variable_get(:@@method_literals)['blank']
|
blank_literal = Expression::LITERALS['blank']
|
||||||
|
|
||||||
assert_evaluates_true(VariableLookup.parse('nil_value'), '==', blank_literal)
|
assert_evaluates_true(VariableLookup.parse('nil_value'), '==', blank_literal)
|
||||||
end
|
end
|
||||||
@@ -242,7 +242,7 @@ class ConditionUnitTest < Minitest::Test
|
|||||||
# false is considered blank to match ActiveSupport semantics.
|
# false is considered blank to match ActiveSupport semantics.
|
||||||
# This allows {% if some_flag == blank %} to work when flag is false.
|
# This allows {% if some_flag == blank %} to work when flag is false.
|
||||||
@context['false_value'] = false
|
@context['false_value'] = false
|
||||||
blank_literal = Condition.class_variable_get(:@@method_literals)['blank']
|
blank_literal = Expression::LITERALS['blank']
|
||||||
|
|
||||||
assert_evaluates_true(VariableLookup.parse('false_value'), '==', blank_literal)
|
assert_evaluates_true(VariableLookup.parse('false_value'), '==', blank_literal)
|
||||||
end
|
end
|
||||||
@@ -251,7 +251,7 @@ class ConditionUnitTest < Minitest::Test
|
|||||||
# true is a definite value, not blank.
|
# true is a definite value, not blank.
|
||||||
# Ensures {% if flag == blank %} works correctly for boolean flags.
|
# Ensures {% if flag == blank %} works correctly for boolean flags.
|
||||||
@context['true_value'] = true
|
@context['true_value'] = true
|
||||||
blank_literal = Condition.class_variable_get(:@@method_literals)['blank']
|
blank_literal = Expression::LITERALS['blank']
|
||||||
|
|
||||||
assert_evaluates_false(VariableLookup.parse('true_value'), '==', blank_literal)
|
assert_evaluates_false(VariableLookup.parse('true_value'), '==', blank_literal)
|
||||||
end
|
end
|
||||||
@@ -260,7 +260,7 @@ class ConditionUnitTest < Minitest::Test
|
|||||||
# Numbers (including zero) are never blank - they represent actual values.
|
# Numbers (including zero) are never blank - they represent actual values.
|
||||||
# 0 is a valid quantity, not the absence of a value.
|
# 0 is a valid quantity, not the absence of a value.
|
||||||
@context['number'] = 42
|
@context['number'] = 42
|
||||||
blank_literal = Condition.class_variable_get(:@@method_literals)['blank']
|
blank_literal = Expression::LITERALS['blank']
|
||||||
|
|
||||||
assert_evaluates_false(VariableLookup.parse('number'), '==', blank_literal)
|
assert_evaluates_false(VariableLookup.parse('number'), '==', blank_literal)
|
||||||
end
|
end
|
||||||
@@ -269,7 +269,7 @@ class ConditionUnitTest < Minitest::Test
|
|||||||
# A string with actual content is not blank.
|
# A string with actual content is not blank.
|
||||||
# This is the expected behavior for most template string comparisons.
|
# This is the expected behavior for most template string comparisons.
|
||||||
@context['string'] = 'hello'
|
@context['string'] = 'hello'
|
||||||
blank_literal = Condition.class_variable_get(:@@method_literals)['blank']
|
blank_literal = Expression::LITERALS['blank']
|
||||||
|
|
||||||
assert_evaluates_false(VariableLookup.parse('string'), '==', blank_literal)
|
assert_evaluates_false(VariableLookup.parse('string'), '==', blank_literal)
|
||||||
end
|
end
|
||||||
@@ -278,7 +278,7 @@ class ConditionUnitTest < Minitest::Test
|
|||||||
# An array with elements has content, so it's not blank.
|
# An array with elements has content, so it's not blank.
|
||||||
# Enables patterns like {% unless products == blank %}Show products{% endunless %}
|
# Enables patterns like {% unless products == blank %}Show products{% endunless %}
|
||||||
@context['array'] = [1, 2, 3]
|
@context['array'] = [1, 2, 3]
|
||||||
blank_literal = Condition.class_variable_get(:@@method_literals)['blank']
|
blank_literal = Expression::LITERALS['blank']
|
||||||
|
|
||||||
assert_evaluates_false(VariableLookup.parse('array'), '==', blank_literal)
|
assert_evaluates_false(VariableLookup.parse('array'), '==', blank_literal)
|
||||||
end
|
end
|
||||||
@@ -287,7 +287,7 @@ class ConditionUnitTest < Minitest::Test
|
|||||||
# A hash with key-value pairs has content, so it's not blank.
|
# A hash with key-value pairs has content, so it's not blank.
|
||||||
# Useful for checking if configuration exists: {% if config != blank %}
|
# Useful for checking if configuration exists: {% if config != blank %}
|
||||||
@context['hash'] = { 'a' => 1 }
|
@context['hash'] = { 'a' => 1 }
|
||||||
blank_literal = Condition.class_variable_get(:@@method_literals)['blank']
|
blank_literal = Expression::LITERALS['blank']
|
||||||
|
|
||||||
assert_evaluates_false(VariableLookup.parse('hash'), '==', blank_literal)
|
assert_evaluates_false(VariableLookup.parse('hash'), '==', blank_literal)
|
||||||
end
|
end
|
||||||
@@ -303,7 +303,7 @@ class ConditionUnitTest < Minitest::Test
|
|||||||
# An empty string ("") has length 0, so it's empty.
|
# An empty string ("") has length 0, so it's empty.
|
||||||
# Different from blank - empty is a stricter check.
|
# Different from blank - empty is a stricter check.
|
||||||
@context['empty_string'] = ''
|
@context['empty_string'] = ''
|
||||||
empty_literal = Condition.class_variable_get(:@@method_literals)['empty']
|
empty_literal = Expression::LITERALS['empty']
|
||||||
|
|
||||||
assert_evaluates_true(VariableLookup.parse('empty_string'), '==', empty_literal)
|
assert_evaluates_true(VariableLookup.parse('empty_string'), '==', empty_literal)
|
||||||
end
|
end
|
||||||
@@ -313,7 +313,7 @@ class ConditionUnitTest < Minitest::Test
|
|||||||
# This is the key difference between empty and blank:
|
# This is the key difference between empty and blank:
|
||||||
# " ".empty? => false, but " ".blank? => true
|
# " ".empty? => false, but " ".blank? => true
|
||||||
@context['whitespace'] = ' '
|
@context['whitespace'] = ' '
|
||||||
empty_literal = Condition.class_variable_get(:@@method_literals)['empty']
|
empty_literal = Expression::LITERALS['empty']
|
||||||
|
|
||||||
assert_evaluates_false(VariableLookup.parse('whitespace'), '==', empty_literal)
|
assert_evaluates_false(VariableLookup.parse('whitespace'), '==', empty_literal)
|
||||||
end
|
end
|
||||||
@@ -322,7 +322,7 @@ class ConditionUnitTest < Minitest::Test
|
|||||||
# An array with no elements is empty.
|
# An array with no elements is empty.
|
||||||
# [].empty? => true
|
# [].empty? => true
|
||||||
@context['empty_array'] = []
|
@context['empty_array'] = []
|
||||||
empty_literal = Condition.class_variable_get(:@@method_literals)['empty']
|
empty_literal = Expression::LITERALS['empty']
|
||||||
|
|
||||||
assert_evaluates_true(VariableLookup.parse('empty_array'), '==', empty_literal)
|
assert_evaluates_true(VariableLookup.parse('empty_array'), '==', empty_literal)
|
||||||
end
|
end
|
||||||
@@ -331,7 +331,7 @@ class ConditionUnitTest < Minitest::Test
|
|||||||
# A hash with no key-value pairs is empty.
|
# A hash with no key-value pairs is empty.
|
||||||
# {}.empty? => true
|
# {}.empty? => true
|
||||||
@context['empty_hash'] = {}
|
@context['empty_hash'] = {}
|
||||||
empty_literal = Condition.class_variable_get(:@@method_literals)['empty']
|
empty_literal = Expression::LITERALS['empty']
|
||||||
|
|
||||||
assert_evaluates_true(VariableLookup.parse('empty_hash'), '==', empty_literal)
|
assert_evaluates_true(VariableLookup.parse('empty_hash'), '==', empty_literal)
|
||||||
end
|
end
|
||||||
@@ -341,7 +341,7 @@ class ConditionUnitTest < Minitest::Test
|
|||||||
# nil is not a collection, so it cannot be empty.
|
# nil is not a collection, so it cannot be empty.
|
||||||
# This differs from blank: nil IS blank, but nil is NOT empty.
|
# This differs from blank: nil IS blank, but nil is NOT empty.
|
||||||
@context['nil_value'] = nil
|
@context['nil_value'] = nil
|
||||||
empty_literal = Condition.class_variable_get(:@@method_literals)['empty']
|
empty_literal = Expression::LITERALS['empty']
|
||||||
|
|
||||||
assert_evaluates_false(VariableLookup.parse('nil_value'), '==', empty_literal)
|
assert_evaluates_false(VariableLookup.parse('nil_value'), '==', empty_literal)
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user