mirror of
https://github.com/Shopify/liquid.git
synced 2026-09-13 07:50:43 -07:00
Compare commits
23
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de603f07af | ||
|
|
0b9318222b | ||
|
|
21d6197533 | ||
|
|
5e92b3a89a | ||
|
|
7f2cf1fe67 | ||
|
|
10e0fb795e | ||
|
|
546dd9bc06 | ||
|
|
9a77e3e923 | ||
|
|
c44d1d9193 | ||
|
|
dd7bbf26bc | ||
|
|
cca24a2226 | ||
|
|
98ce25cb40 | ||
|
|
77293d4524 | ||
|
|
af66bc8a5f | ||
|
|
42e5c52336 | ||
|
|
649cca1349 | ||
|
|
81ed65f2a1 | ||
|
|
6ca06c22b8 | ||
|
|
80bc7ffdf2 | ||
|
|
48cb643c02 | ||
|
|
1d97389fb0 | ||
|
|
3ff4170cb0 | ||
|
|
a75517e2c7 |
@@ -5,7 +5,7 @@
|
||||
|
||||
* [Contributing guidelines](CONTRIBUTING.md)
|
||||
* [Version history](History.md)
|
||||
* [Liquid documentation from Shopify](https://shopify.dev/docs/docs/api/liquid)
|
||||
* [Liquid documentation from Shopify](https://shopify.dev/docs/api/liquid)
|
||||
* [Liquid Wiki at GitHub](https://github.com/Shopify/liquid/wiki)
|
||||
* [Website](http://liquidmarkup.org/)
|
||||
|
||||
@@ -111,4 +111,4 @@ template.render!({ 'x' => 1}, { strict_variables: true })
|
||||
|
||||
To help track usages of a feature or code path in production, we have released opt-in usage tracking. To enable this, we provide an empty `Liquid:: Usage.increment` method which you can customize to your needs. The feature is well suited to https://github.com/Shopify/statsd-instrument. However, the choice of implementation is up to you.
|
||||
|
||||
Once you have enabled usage tracking, we recommend reporting any events through Github Issues that your system may be logging. It is highly likely this event has been added to consider deprecating or improving code specific to this event, so please raise any concerns.
|
||||
Once you have enabled usage tracking, we recommend reporting any events through Github Issues that your system may be logging. It is highly likely this event has been added to consider deprecating or improving code specific to this event, so please raise any concerns.
|
||||
|
||||
@@ -45,6 +45,12 @@ module Liquid
|
||||
end
|
||||
tag_name = Regexp.last_match(1)
|
||||
markup = Regexp.last_match(2)
|
||||
|
||||
if tag_name == 'liquid'
|
||||
parse_context.line_number -= 1
|
||||
next parse_liquid_tag(markup, parse_context)
|
||||
end
|
||||
|
||||
unless (tag = registered_tags[tag_name])
|
||||
# end parsing if we reach an unknown tag and let the caller decide
|
||||
# determine how to proceed
|
||||
|
||||
+17
-2
@@ -69,9 +69,9 @@ module Liquid
|
||||
|
||||
case condition.child_relation
|
||||
when :or
|
||||
break if result
|
||||
break if Liquid::Utils.to_liquid_value(result)
|
||||
when :and
|
||||
break unless result
|
||||
break unless Liquid::Utils.to_liquid_value(result)
|
||||
else
|
||||
break
|
||||
end
|
||||
@@ -112,6 +112,8 @@ module Liquid
|
||||
if left.is_a?(MethodLiteral)
|
||||
if right.respond_to?(left.method_name)
|
||||
return right.send(left.method_name)
|
||||
elsif (res = fallback_simulation_of_active_support(left.method_name, right))
|
||||
return res
|
||||
else
|
||||
return nil
|
||||
end
|
||||
@@ -120,6 +122,8 @@ module Liquid
|
||||
if right.is_a?(MethodLiteral)
|
||||
if left.respond_to?(right.method_name)
|
||||
return left.send(right.method_name)
|
||||
elsif (res = fallback_simulation_of_active_support(right.method_name, left))
|
||||
return res
|
||||
else
|
||||
return nil
|
||||
end
|
||||
@@ -128,6 +132,17 @@ module Liquid
|
||||
left == right
|
||||
end
|
||||
|
||||
# ActiveSupport creates #blank? as an alias for #empty? on Hash and Array.
|
||||
# Without this simulation, [] == blank behaves differently when AS is loaded vs. not.
|
||||
def fallback_simulation_of_active_support(method_name, obj)
|
||||
return nil unless method_name == :blank?
|
||||
|
||||
case obj
|
||||
when Array, Hash
|
||||
obj.empty?
|
||||
end # else nil
|
||||
end
|
||||
|
||||
def interpret_condition(left, right, op, context)
|
||||
# If the operator is empty this means that the decision statement is just
|
||||
# a single variable. We can just poll this variable from the context and
|
||||
|
||||
@@ -4,7 +4,8 @@ module Liquid
|
||||
class PartialCache
|
||||
def self.load(template_name, context:, parse_context:)
|
||||
cached_partials = context.registers[:cached_partials]
|
||||
cached = cached_partials[template_name]
|
||||
cache_key = "#{template_name}:#{parse_context.error_mode}"
|
||||
cached = cached_partials[cache_key]
|
||||
return cached if cached
|
||||
|
||||
file_system = context.registers[:file_system]
|
||||
@@ -15,10 +16,16 @@ module Liquid
|
||||
template_factory = context.registers[:template_factory]
|
||||
template = template_factory.for(template_name)
|
||||
|
||||
partial = template.parse(source, parse_context)
|
||||
begin
|
||||
partial = template.parse(source, parse_context)
|
||||
rescue Liquid::Error => e
|
||||
e.template_name = template&.name || template_name
|
||||
raise e
|
||||
end
|
||||
|
||||
partial.name ||= template_name
|
||||
|
||||
cached_partials[template_name] = partial
|
||||
cached_partials[cache_key] = partial
|
||||
ensure
|
||||
parse_context.partial = false
|
||||
end
|
||||
|
||||
@@ -69,7 +69,7 @@ module Liquid
|
||||
# @liquid_type filter
|
||||
# @liquid_category string
|
||||
# @liquid_summary
|
||||
# Capitalizes the first word in a string.
|
||||
# Capitalizes the first word in a string and downcases the remaining characters.
|
||||
# @liquid_syntax string | capitalize
|
||||
# @liquid_return [string]
|
||||
def capitalize(input)
|
||||
@@ -869,6 +869,34 @@ module Liquid
|
||||
false_check || (input.respond_to?(:empty?) && input.empty?) ? default_value : input
|
||||
end
|
||||
|
||||
# @liquid_public_docs
|
||||
# @liquid_type filter
|
||||
# @liquid_category array
|
||||
# @liquid_summary
|
||||
# Returns the sum of all elements in an array.
|
||||
# @liquid_syntax array | sum
|
||||
# @liquid_return [number]
|
||||
def sum(input, property = nil)
|
||||
ary = InputIterator.new(input, context)
|
||||
return 0 if ary.empty?
|
||||
|
||||
values_for_sum = ary.map do |item|
|
||||
if property.nil?
|
||||
item
|
||||
elsif item.respond_to?(:[])
|
||||
item[property]
|
||||
else
|
||||
0
|
||||
end
|
||||
rescue TypeError
|
||||
raise_property_error(property)
|
||||
end
|
||||
|
||||
InputIterator.new(values_for_sum, context).sum do |item|
|
||||
Utils.to_number(item)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :context
|
||||
|
||||
@@ -26,6 +26,7 @@ module Liquid
|
||||
@body = +''
|
||||
while (token = tokens.shift)
|
||||
if token =~ FullTokenPossiblyInvalid && block_delimiter == Regexp.last_match(2)
|
||||
parse_context.trim_whitespace = (token[-3] == WhitespaceControl)
|
||||
@body << Regexp.last_match(1) if Regexp.last_match(1) != ""
|
||||
return
|
||||
end
|
||||
|
||||
@@ -189,6 +189,8 @@ module Liquid
|
||||
@profiler = context.profiler = Liquid::Profiler.new
|
||||
end
|
||||
|
||||
context.template_name ||= name
|
||||
|
||||
begin
|
||||
# render the nodelist.
|
||||
@root.render_to_output_buffer(context, output || +'')
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class ArrayTest < Minitest::Test
|
||||
include Liquid
|
||||
|
||||
def test_array_size
|
||||
assert_template_result("0", "{{a.size}}", { 'a' => [] })
|
||||
assert_template_result("2", "{{a.size}}", { 'a' => [1, 2] })
|
||||
end
|
||||
|
||||
def test_array_first
|
||||
assert_template_result("", "{{a.first}}", { 'a' => [] })
|
||||
assert_template_result("1", "{{a.first}}", { 'a' => [1, 2] })
|
||||
end
|
||||
|
||||
def test_array_last
|
||||
assert_template_result("", "{{a.last}}", { 'a' => [] })
|
||||
assert_template_result("2", "{{a.last}}", { 'a' => [1, 2] })
|
||||
end
|
||||
|
||||
def test_array_index
|
||||
assert_template_result("", "{{a[1]}}", { 'a' => [] })
|
||||
assert_template_result("2", "{{a[1]}}", { 'a' => [1, 2] })
|
||||
end
|
||||
|
||||
def test_negative_array_index
|
||||
assert_template_result("3", "{{a[-1]}}", { 'a' => [1, 2, 3] })
|
||||
assert_template_result("1", "{{a[-3]}}", { 'a' => [1, 2, 3] })
|
||||
assert_template_result("", "{{a[-4]}}", { 'a' => [1, 2, 3] })
|
||||
end
|
||||
|
||||
def test_array_to_s
|
||||
arr = ["a", ["b", 1], "c"]
|
||||
assert_template_result("ab1c", "{{a}}", { 'a' => arr })
|
||||
end
|
||||
|
||||
def test_auto_methods
|
||||
a1 = []
|
||||
a2 = [1, 2, 3, "abc"]
|
||||
assert_template_result("0,nonblank", "{{a.size}},{%if a['size'] == blank%}blank{%else%}nonblank{%endif%}", { 'a' => a1 })
|
||||
assert_template_result("4,nonblank", "{{a.size}},{%if a['size'] == blank%}blank{%else%}nonblank{%endif%}", { 'a' => a2 })
|
||||
assert_template_result(",", "{{a.first}},{{a['first']}}", { 'a' => a1 })
|
||||
assert_template_result("1,", "{{a.first}},{{a['first']}}", { 'a' => a2 })
|
||||
assert_template_result(",", "{{a.last}},{{a['last']}}", { 'a' => a1 })
|
||||
assert_template_result("abc,", "{{a.last}},{{a['last']}}", { 'a' => a2 })
|
||||
end
|
||||
|
||||
def test_array_equality
|
||||
a0 = []
|
||||
a1 = [1]
|
||||
a2 = [1, 2]
|
||||
a3 = [1]
|
||||
|
||||
arrays = [a0, a1, a2, a3]
|
||||
arrays.each_with_index do |a, i|
|
||||
arrays.each_with_index do |b, j|
|
||||
prefix = "(#{i},#{j})"
|
||||
assert_template_result(
|
||||
prefix + (a == b ? "y" : "n"),
|
||||
"#{prefix}{% if a == b %}y{%else%}n{%endif%}",
|
||||
{ "a" => a, "b" => b },
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_contains
|
||||
a0 = [1, "a", ["b", "c"], true]
|
||||
a1 = ["b", "c"]
|
||||
a2 = ["B", "C"]
|
||||
|
||||
tpl = "{%if a contains b%}y{%else%}n{%endif%}"
|
||||
|
||||
assert_template_result("y", tpl, { "a" => a0, "b" => 1 })
|
||||
assert_template_result("n", tpl, { "a" => a0, "b" => 2 })
|
||||
assert_template_result("y", tpl, { "a" => a0, "b" => a1 })
|
||||
assert_template_result("n", tpl, { "a" => a0, "b" => a2 })
|
||||
end
|
||||
|
||||
def test_empty
|
||||
assert_template_result("y", "{%if a == empty%}y{%else%}n{%endif%}", { 'a' => [] })
|
||||
assert_template_result("n", "{%if a == empty%}y{%else%}n{%endif%}", { 'a' => [1, 2] })
|
||||
end
|
||||
|
||||
def test_blank
|
||||
assert_template_result("y", "{%if a == blank%}y{%else%}n{%endif%}", { 'a' => [] })
|
||||
assert_template_result("n", "{%if a == blank%}y{%else%}n{%endif%}", { 'a' => [1, 2] })
|
||||
end
|
||||
|
||||
def test_iter
|
||||
a0 = [1, "a"]
|
||||
assert_template_result("1a", "{%for i in a%}{{i}}{%endfor%}", { 'a' => a0 })
|
||||
end
|
||||
|
||||
def test_output
|
||||
a0 = []
|
||||
a1 = [1, "a", ["b", "c"], true]
|
||||
assert_template_result("", "{{a}}", { 'a' => a0 })
|
||||
assert_template_result("1abctrue", "{{a}}", { 'a' => a1 })
|
||||
end
|
||||
end
|
||||
@@ -263,4 +263,81 @@ class ErrorHandlingTest < Minitest::Test
|
||||
output = Liquid::Template.parse("{% assign x = 0 %}{% if 1 < '2' %}{% assign x = 3 %}{% endif %}{{ x }}").render
|
||||
assert_equal("0", output)
|
||||
end
|
||||
|
||||
def test_syntax_error_is_raised_with_template_name
|
||||
file_system = StubFileSystem.new("snippet" => "1\n2\n{{ 1")
|
||||
|
||||
context = Liquid::Context.build(
|
||||
registers: { file_system: file_system },
|
||||
)
|
||||
|
||||
template = Template.parse(
|
||||
'{% render "snippet" %}',
|
||||
line_numbers: true,
|
||||
)
|
||||
template.name = "template/index"
|
||||
|
||||
assert_equal(
|
||||
"Liquid syntax error (snippet line 3): Variable '{{' was not properly terminated with regexp: /\\}\\}/",
|
||||
template.render(context),
|
||||
)
|
||||
end
|
||||
|
||||
def test_syntax_error_is_raised_with_template_name_from_template_factory
|
||||
file_system = StubFileSystem.new("snippet" => "1\n2\n{{ 1")
|
||||
|
||||
context = Liquid::Context.build(
|
||||
registers: {
|
||||
file_system: file_system,
|
||||
template_factory: StubTemplateFactory.new,
|
||||
},
|
||||
)
|
||||
|
||||
template = Template.parse(
|
||||
'{% render "snippet" %}',
|
||||
line_numbers: true,
|
||||
)
|
||||
template.name = "template/index"
|
||||
|
||||
assert_equal(
|
||||
"Liquid syntax error (some/path/snippet line 3): Variable '{{' was not properly terminated with regexp: /\\}\\}/",
|
||||
template.render(context),
|
||||
)
|
||||
end
|
||||
|
||||
def test_error_is_raised_during_parse_with_template_name
|
||||
depth = Liquid::Block::MAX_DEPTH + 1
|
||||
code = "{% if true %}" * depth + "rendered" + "{% endif %}" * depth
|
||||
|
||||
template = Template.parse("{% render 'snippet' %}", line_numbers: true)
|
||||
|
||||
context = Liquid::Context.build(
|
||||
registers: {
|
||||
file_system: StubFileSystem.new("snippet" => code),
|
||||
template_factory: StubTemplateFactory.new,
|
||||
},
|
||||
)
|
||||
|
||||
assert_equal("Liquid error (some/path/snippet line 1): Nesting too deep", template.render(context))
|
||||
end
|
||||
|
||||
def test_internal_error_is_raised_with_template_name
|
||||
template = Template.new
|
||||
template.parse(
|
||||
"{% render 'snippet' %}",
|
||||
line_numbers: true,
|
||||
)
|
||||
template.name = "template/index"
|
||||
|
||||
context = Liquid::Context.build(
|
||||
registers: {
|
||||
file_system: StubFileSystem.new({}),
|
||||
},
|
||||
)
|
||||
|
||||
assert_equal(
|
||||
"Liquid error (template/index line 1): internal",
|
||||
template.render(context),
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "test_helper"
|
||||
require "json"
|
||||
|
||||
class HashTest < Minitest::Test
|
||||
include Liquid
|
||||
|
||||
class HashSubclass < Hash
|
||||
def []=(key, value)
|
||||
super(fmt_key(key), value)
|
||||
end
|
||||
|
||||
def [](key)
|
||||
super(fmt_key(key))
|
||||
end
|
||||
|
||||
def key?(key)
|
||||
super(fmt_key(key))
|
||||
end
|
||||
|
||||
def include?(key)
|
||||
super(fmt_key(key))
|
||||
end
|
||||
|
||||
def to_s
|
||||
super.upcase
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def fmt_key(key)
|
||||
return key unless key.is_a?(String)
|
||||
|
||||
key.upcase
|
||||
end
|
||||
end
|
||||
|
||||
def test_size
|
||||
assert_template_result("0", "{{h.size}}", { "h" => {} })
|
||||
assert_template_result("1", "{{h.size}}", { "h" => { "a" => 1 } })
|
||||
end
|
||||
|
||||
def test_first
|
||||
assert_template_result("", "{{h.first}}", { "h" => {} })
|
||||
assert_template_result("a1", "{{h.first}}", { "h" => { "a" => 1 } })
|
||||
end
|
||||
|
||||
def test_last
|
||||
assert_template_result("", "{{h.last}}", { "h" => { "a" => 1 } })
|
||||
end
|
||||
|
||||
def test_index
|
||||
assert_template_result("", "{{h['a']}}", { "h" => {} })
|
||||
assert_template_result("1", "{{h['a']}}", { "h" => { "a" => 1 } })
|
||||
end
|
||||
|
||||
def test_to_s
|
||||
h = { "a" => 1, "b" => [1, { "c" => 2 }] }
|
||||
assert_template_result("{\"a\"=>1, \"b\"=>[1, {\"c\"=>2}]}", "{{h}}", { "h" => h })
|
||||
end
|
||||
|
||||
def test_auto_methods
|
||||
h1 = { "a" => 1 }
|
||||
h2 = { "last" => 10, "first" => 11, "size" => 12 }
|
||||
assert_template_result("1,nonblank", "{{h.size}},{%if h['size'] == blank%}blank{%else%}nonblank{%endif%}", { "h" => h1 })
|
||||
assert_template_result("12,nonblank", "{{h.size}},{%if h['size'] == blank%}blank{%else%}nonblank{%endif%}", { "h" => h2 })
|
||||
assert_template_result("a1,", "{{h.first}},{{h['first']}}", { "h" => h1 })
|
||||
assert_template_result("11,11", "{{h.first}},{{h['first']}}", { "h" => h2 })
|
||||
assert_template_result(",", "{{h.last}},{{h['last']}}", { "h" => h1 })
|
||||
assert_template_result("10,10", "{{h.last}},{{h['last']}}", { "h" => h2 })
|
||||
end
|
||||
|
||||
def test_equality
|
||||
h0 = {}
|
||||
h1 = { "a" => 1 }
|
||||
h2 = { "a" => 1, "b" => 2 }
|
||||
h3 = { "a" => 1 }
|
||||
|
||||
hashes = [h0, h1, h2, h3]
|
||||
hashes.each_with_index do |a, i|
|
||||
hashes.each_with_index do |b, j|
|
||||
prefix = "(#{i},#{j})"
|
||||
assert_template_result(
|
||||
prefix + (a == b ? "y" : "n"),
|
||||
"#{prefix}{% if a == b %}y{%else%}n{%endif%}",
|
||||
{ "a" => a, "b" => b },
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_contains
|
||||
h = { "a" => 1, "b" => { "c" => "d" }, "3" => ["f", 2] }
|
||||
|
||||
tpl = "{%if h contains b%}y{%else%}n{%endif%}"
|
||||
|
||||
assert_template_result("y", tpl, { "h" => h, "b" => "a" })
|
||||
# assert_template_result("y", tpl, h, { "h" => { "b" => "a".html_safe } })
|
||||
assert_template_result("y", tpl, { "h" => h, "b" => "a".b })
|
||||
assert_template_result("n", tpl, { "h" => h, "b" => "A" })
|
||||
# assert_template_result("n", tpl, h, { "h" => { "b" => "A".html_safe } })
|
||||
assert_template_result("n", tpl, { "h" => h, "b" => "A".b })
|
||||
|
||||
assert_template_result("n", tpl, { "h" => h, "b" => 1 })
|
||||
assert_template_result("n", tpl, { "h" => h, "b" => "1" })
|
||||
|
||||
assert_template_result("n", tpl, { "h" => h, "b" => 3 })
|
||||
assert_template_result("y", tpl, { "h" => h, "b" => "3" })
|
||||
end
|
||||
|
||||
def test_empty
|
||||
assert_template_result("y", "{%if h == empty%}y{%else%}n{%endif%}", { "h" => {} })
|
||||
assert_template_result("n", "{%if h == empty%}y{%else%}n{%endif%}", { "h" => { "a" => 1 } })
|
||||
end
|
||||
|
||||
def test_blank
|
||||
assert_template_result("y", "{%if h == blank%}y{%else%}n{%endif%}", { "h" => {} })
|
||||
assert_template_result("n", "{%if h == blank%}y{%else%}n{%endif%}", { "h" => { "a" => 1 } })
|
||||
end
|
||||
|
||||
def test_iter
|
||||
h0 = { "a" => 1, "b" => { "c" => "d" }, "3" => ["f", 2] }
|
||||
assert_template_result("a1b{\"c\"=>\"d\"}3f2", "{%for i in h%}{{i}}{%endfor%}", { "h" => h0 })
|
||||
end
|
||||
|
||||
def test_output
|
||||
h0 = {}
|
||||
h1 = { "a" => 1, "b" => { "c" => "d" }, "3" => ["f", 2] }
|
||||
assert_template_result("{}", "{{h}}", { "h" => h0 })
|
||||
assert_template_result("{\"a\"=>1, \"b\"=>{\"c\"=>\"d\"}, \"3\"=>[\"f\", 2]}", "{{h}}", { "h" => h1 })
|
||||
end
|
||||
|
||||
def test_integer_key
|
||||
h = { 2 => "huh" }
|
||||
assert_template_result("huh", "{{h[2]}}", { "h" => h })
|
||||
assert_template_result("huh", "{{h[b]}}", { "h" => h, "b" => 2 })
|
||||
assert_template_result("", "{{h[b]}}", { "h" => h, "b" => "2" })
|
||||
end
|
||||
|
||||
def test_nil_key
|
||||
assert_hash_roundtrip(nil, nil)
|
||||
refute_hash_roundtrip(nil, "nil")
|
||||
refute_hash_roundtrip(nil, "null")
|
||||
end
|
||||
|
||||
def test_bool_key
|
||||
assert_hash_roundtrip(true, true)
|
||||
refute_hash_roundtrip(true, false)
|
||||
refute_hash_roundtrip(true, "true")
|
||||
refute_hash_roundtrip(true, nil)
|
||||
end
|
||||
|
||||
def test_int_key
|
||||
assert_hash_roundtrip(1, 1)
|
||||
refute_hash_roundtrip(1, 2)
|
||||
refute_hash_roundtrip(1, "1")
|
||||
refute_hash_roundtrip(1, 1.0000000000001)
|
||||
end
|
||||
|
||||
def test_string_key
|
||||
assert_hash_roundtrip("word", "word")
|
||||
# assert_hash_roundtrip("word", "word".html_safe)
|
||||
assert_hash_roundtrip("word", "word".b)
|
||||
refute_hash_roundtrip("word", "word ")
|
||||
refute_hash_roundtrip("word", nil)
|
||||
refute_hash_roundtrip("word", true)
|
||||
end
|
||||
|
||||
def test_weird_keys
|
||||
assert_hash_roundtrip([1, 2], [1, 2])
|
||||
refute_hash_roundtrip([1, 2], [1, 3])
|
||||
assert_hash_roundtrip({ "a" => 1 }, { "a" => 1 })
|
||||
refute_hash_roundtrip({ "a" => 1 }, { "a" => 2 })
|
||||
o = Struct.new(:to_liquid).new(Object.new)
|
||||
# failed_hash_roundtrip(o, o)
|
||||
refute_hash_roundtrip(o, 1)
|
||||
refute_hash_roundtrip(o, nil)
|
||||
assert_hash_roundtrip("\xff".b, "\xff".b)
|
||||
assert_hash_roundtrip(1..2, 1..2)
|
||||
refute_hash_roundtrip(1..2, 1...2)
|
||||
assert_hash_roundtrip(
|
||||
111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
|
||||
111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
|
||||
)
|
||||
# f64 hashing isn't perfect but we can make some basic assertions.
|
||||
assert_hash_roundtrip(1.1234, 1.1234)
|
||||
refute_hash_roundtrip(1.1234, 1.1)
|
||||
end
|
||||
|
||||
def test_hash_html_key
|
||||
# I mean this is probably not ideal but this is the way liquid works.
|
||||
assert_template_result("{\"a<script>b\"=>\"y\"}", "{{h}}", { "h" => { "a<script>b" => "y" } })
|
||||
end
|
||||
|
||||
def test_hash_subclass
|
||||
hs = HashSubclass.new
|
||||
hs["A"] = "b"
|
||||
assert_template_result("b", "{{h['a']}}", { "h" => hs })
|
||||
assert_template_result("b", "{{h['A']}}", { "h" => hs })
|
||||
assert_template_result("", "{{h['b']}}", { "h" => hs })
|
||||
assert_template_result("yes", "{% if h contains 'a' %}yes{%else%}no{%endif%}", { "h" => hs })
|
||||
assert_template_result('{"A"=>"B"}', "{{h}}", { "h" => hs })
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def failed_hash_roundtrip(key, test)
|
||||
assert_template_result(
|
||||
"Liquid error (templates/index line 1): internal",
|
||||
"{{h[b]}}",
|
||||
{ "h" => { key => "y" }, "b" => test },
|
||||
)
|
||||
end
|
||||
|
||||
def assert_hash_roundtrip(key, test)
|
||||
assert_template_result("y", "{{h[b]}}", { "h" => { key => "y" }, "b" => test })
|
||||
end
|
||||
|
||||
def refute_hash_roundtrip(key, test)
|
||||
assert_template_result("", "{{h[b]}}", { "h" => { key => "y" }, "b" => test })
|
||||
end
|
||||
end
|
||||
@@ -928,6 +928,72 @@ class StandardFiltersTest < Minitest::Test
|
||||
assert_equal([{ "foo" => true }, { "foo" => "for sure" }], @filters.where(input, "foo"))
|
||||
end
|
||||
|
||||
def test_sum_with_all_numbers
|
||||
input = [1, 2]
|
||||
|
||||
assert_equal(3, @filters.sum(input))
|
||||
assert_raises(Liquid::ArgumentError, "cannot select the property 'quantity'") do
|
||||
@filters.sum(input, "quantity")
|
||||
end
|
||||
end
|
||||
|
||||
def test_sum_with_numeric_strings
|
||||
input = [1, 2, "3", "4"]
|
||||
|
||||
assert_equal(10, @filters.sum(input))
|
||||
assert_raises(Liquid::ArgumentError, "cannot select the property 'quantity'") do
|
||||
@filters.sum(input, "quantity")
|
||||
end
|
||||
end
|
||||
|
||||
def test_sum_with_nested_arrays
|
||||
input = [1, [2, [3, 4]]]
|
||||
|
||||
assert_equal(10, @filters.sum(input))
|
||||
assert_raises(Liquid::ArgumentError, "cannot select the property 'quantity'") do
|
||||
@filters.sum(input, "quantity")
|
||||
end
|
||||
end
|
||||
|
||||
def test_sum_with_indexable_map_values
|
||||
input = [{ "quantity" => 1 }, { "quantity" => 2, "weight" => 3 }, { "weight" => 4 }]
|
||||
|
||||
assert_equal(0, @filters.sum(input))
|
||||
assert_equal(3, @filters.sum(input, "quantity"))
|
||||
assert_equal(7, @filters.sum(input, "weight"))
|
||||
assert_equal(0, @filters.sum(input, "subtotal"))
|
||||
end
|
||||
|
||||
def test_sum_with_indexable_non_map_values
|
||||
input = [1, [2], "foo", { "quantity" => 3 }]
|
||||
|
||||
assert_equal(3, @filters.sum(input))
|
||||
assert_raises(Liquid::ArgumentError, "cannot select the property 'quantity'") do
|
||||
@filters.sum(input, "quantity")
|
||||
end
|
||||
end
|
||||
|
||||
def test_sum_with_unindexable_values
|
||||
input = [1, true, nil, { "quantity" => 2 }]
|
||||
|
||||
assert_equal(1, @filters.sum(input))
|
||||
assert_raises(Liquid::ArgumentError, "cannot select the property 'quantity'") do
|
||||
@filters.sum(input, "quantity")
|
||||
end
|
||||
end
|
||||
|
||||
def test_sum_without_property_calls_to_liquid
|
||||
t = TestThing.new
|
||||
Liquid::Template.parse('{{ foo | sum }}').render("foo" => [t])
|
||||
assert(t.foo > 0)
|
||||
end
|
||||
|
||||
def test_sum_with_property_calls_to_liquid_on_property_values
|
||||
t = TestThing.new
|
||||
Liquid::Template.parse('{{ foo | sum: "quantity" }}').render("foo" => [{ "quantity" => t }])
|
||||
assert(t.foo > 0)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def with_timezone(tz)
|
||||
|
||||
@@ -113,4 +113,37 @@ class LiquidTagTest < Minitest::Test
|
||||
{% raw %}{% liquid echo 'test' %}{% endraw %}
|
||||
LIQUID
|
||||
end
|
||||
|
||||
def test_nested_liquid_tags
|
||||
assert_template_result('good', <<~LIQUID)
|
||||
{%- liquid
|
||||
liquid
|
||||
if true
|
||||
echo "good"
|
||||
endif
|
||||
-%}
|
||||
LIQUID
|
||||
end
|
||||
|
||||
def test_nested_liquid_tags_on_same_line
|
||||
assert_template_result('good', <<~LIQUID)
|
||||
{%- liquid liquid liquid echo "good" -%}
|
||||
LIQUID
|
||||
end
|
||||
|
||||
def test_nested_liquid_liquid_is_not_skipped_if_used_in_non_tag_position
|
||||
assert_template_result('liquid', <<~LIQUID, { 'liquid' => 'liquid' })
|
||||
{%- liquid liquid liquid echo liquid -%}
|
||||
LIQUID
|
||||
end
|
||||
|
||||
def test_next_liquid_with_unclosed_if_tag
|
||||
assert_match_syntax_error("Liquid syntax error (line 2): 'if' tag was never closed", <<~LIQUID)
|
||||
{%- liquid
|
||||
liquid if true
|
||||
echo "good"
|
||||
endif
|
||||
-%}
|
||||
LIQUID
|
||||
end
|
||||
end
|
||||
|
||||
@@ -13,8 +13,9 @@ class RawTagTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_output_in_raw
|
||||
# assert_template_result('{{ test }}', '{% raw %}{{ test }}{% endraw %}')
|
||||
assert_template_result('>{{ test }}<', '> {%- raw -%}{{ test }}{%- endraw -%} <')
|
||||
assert_template_result("> inner <", "> {%- raw -%} inner {%- endraw %} <")
|
||||
assert_template_result("> inner <", "> {%- raw -%} inner {%- endraw -%} <")
|
||||
end
|
||||
|
||||
def test_open_tag_in_raw
|
||||
|
||||
@@ -34,6 +34,7 @@ class VariableTest < Minitest::Test
|
||||
|
||||
assert_template_result('', '{% if foo %}true{% endif %}', { 'foo' => BooleanDrop.new(false) })
|
||||
assert_template_result('', '{% if foo == true %}True{% endif %}', { 'foo' => BooleanDrop.new(false) })
|
||||
assert_template_result('', '{% if foo and true %}SHOULD NOT HAPPEN{% endif %}', { 'foo' => BooleanDrop.new(false) })
|
||||
|
||||
assert_template_result('one', '{% if a contains x %}one{% endif %}', { 'a' => [1], 'x' => IntegerDrop.new(1) })
|
||||
end
|
||||
|
||||
@@ -174,4 +174,27 @@ class PartialCacheUnitTest < Minitest::Test
|
||||
|
||||
assert_equal('some/path/my_partial', partial.name)
|
||||
end
|
||||
|
||||
def test_includes_error_mode_into_template_cache
|
||||
template_factory = StubTemplateFactory.new
|
||||
context = Liquid::Context.build(
|
||||
registers: {
|
||||
file_system: StubFileSystem.new('my_partial' => 'my partial body'),
|
||||
template_factory: template_factory,
|
||||
},
|
||||
)
|
||||
|
||||
[:lax, :warn, :strict].each do |error_mode|
|
||||
Liquid::PartialCache.load(
|
||||
'my_partial',
|
||||
context: context,
|
||||
parse_context: Liquid::ParseContext.new(error_mode: error_mode),
|
||||
)
|
||||
end
|
||||
|
||||
assert_equal(
|
||||
["my_partial:lax", "my_partial:warn", "my_partial:strict"],
|
||||
context.registers[:cached_partials].keys,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user