mirror of
https://github.com/Shopify/liquid.git
synced 2026-09-13 07:50:43 -07:00
Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f9454d8cf3 | ||
|
|
8dd9279265 | ||
|
|
bf1419b8ac | ||
|
|
5718c4cee2 | ||
|
|
b0dbc62696 | ||
|
|
03aafa974c | ||
|
|
6372289ba3 | ||
|
|
0ec52a40b5 | ||
|
|
74af735f0e |
+1
-1
@@ -1 +1 @@
|
||||
3.3.6
|
||||
3.4.1
|
||||
|
||||
+3
-1
@@ -29,6 +29,8 @@
|
||||
|
||||
## Releasing
|
||||
|
||||
* Bump the version in `lib/liquid/version.rb` and merge it on `main`
|
||||
* Bump the version in `lib/liquid/version.rb`
|
||||
* Update the `History.md` file
|
||||
* Open a PR like [this one](https://github.com/Shopify/liquid/pull/1894) and merge it to `main`
|
||||
* Create a new release using the [GitHub UI](https://github.com/Shopify/liquid/releases/new)
|
||||
|
||||
|
||||
@@ -20,6 +20,10 @@ group :benchmark, :test do
|
||||
end
|
||||
end
|
||||
|
||||
group :development do
|
||||
gem "webrick"
|
||||
end
|
||||
|
||||
group :test do
|
||||
gem 'rubocop', '~> 1.61.0'
|
||||
gem 'rubocop-shopify', '~> 2.12.0', require: false
|
||||
|
||||
+54
-2
@@ -1,12 +1,64 @@
|
||||
# Liquid Change Log
|
||||
|
||||
## 5.6.0 (unreleased)
|
||||
## 5.8.0 (unreleased)
|
||||
|
||||
## 5.7.2 2025-01-31
|
||||
|
||||
* Fix array filters to not support nested properties
|
||||
|
||||
## 5.7.1 2025-01-24
|
||||
|
||||
* Fix the `find` and `find_index`filters to return `nil` when filtering empty arrays
|
||||
* Fix the `has` filter to return `false` when filtering empty arrays
|
||||
|
||||
## 5.7.0 2025-01-16
|
||||
|
||||
### Features
|
||||
|
||||
* Add `find`, `find_index`, `has`, and `reject` filters to arrays
|
||||
* Compatibility with Ruby 3.4
|
||||
|
||||
## 5.6.4 2025-01-14
|
||||
|
||||
### Fixes
|
||||
* Add a default `string_scanner` to avoid errors with `Liquid::VariableLookup.parse("foo.bar")` [Ian Ker-Seymer]
|
||||
|
||||
## 5.6.3 2025-01-13
|
||||
* Remove `lru_redux` dependency [Michael Go]
|
||||
|
||||
## 5.6.2 2025-01-13
|
||||
|
||||
### Fixes
|
||||
* Preserve the old behavior of requiring floats to start with a digit [Michael Go]
|
||||
|
||||
## 5.6.1 2025-01-13
|
||||
|
||||
### Performance improvements
|
||||
* Faster Expression parser / Tokenizer with StringScanner [Michael Go]
|
||||
|
||||
## 5.6.0 2024-12-19
|
||||
|
||||
### Architectural changes
|
||||
* Added new `Environment` class to manage configuration and state that was previously stored in `Template` [Ian Ker-Seymer]
|
||||
* Moved tag registration from `Template` to `Environment` [Ian Ker-Seymer]
|
||||
* Removed `StrainerFactory` in favor of `Environment`-based strainer creation [Ian Ker-Seymer]
|
||||
* Consolidated standard tags into a new `Tags` module with `STANDARD_TAGS` constant [Ian Ker-Seymer]
|
||||
|
||||
### Performance improvements
|
||||
* Optimized `Lexer` with a new `Lexer2` implementation using jump tables for faster tokenization, requires Ruby 3.4 [Ian Ker-Seymer]
|
||||
* Improved variable rendering with specialized handling for different types [Michael Go]
|
||||
* Reduced array allocations by using frozen empty constants [Michael Go]
|
||||
|
||||
### API changes
|
||||
* Deprecated several `Template` class methods in favor of `Environment` methods [Ian Ker-Seymer]
|
||||
* Added deprecation warnings system [Ian Ker-Seymer]
|
||||
* Changed how filters and tags are registered to use Environment [Ian Ker-Seymer]
|
||||
|
||||
### Fixes
|
||||
* Fixed table row handling of break interrupts [Alex Coco]
|
||||
* Improved variable output handling for arrays [Ian Ker-Seymer]
|
||||
* Fix Tokenizer to handle null source value (#1873) [Bahar Pourazar]
|
||||
|
||||
|
||||
## 5.5.0 2024-03-21
|
||||
|
||||
Please reference the GitHub release for more information.
|
||||
|
||||
@@ -387,7 +387,7 @@ module Liquid
|
||||
end
|
||||
elsif ary.all? { |el| el.respond_to?(:[]) }
|
||||
begin
|
||||
ary.sort { |a, b| nil_safe_compare(fetch_property(a, property), fetch_property(b, property)) }
|
||||
ary.sort { |a, b| nil_safe_compare(a[property], b[property]) }
|
||||
rescue TypeError
|
||||
raise_property_error(property)
|
||||
end
|
||||
@@ -416,7 +416,7 @@ module Liquid
|
||||
end
|
||||
elsif ary.all? { |el| el.respond_to?(:[]) }
|
||||
begin
|
||||
ary.sort { |a, b| nil_safe_casecmp(fetch_property(a, property), fetch_property(b, property)) }
|
||||
ary.sort { |a, b| nil_safe_casecmp(a[property], b[property]) }
|
||||
rescue TypeError
|
||||
raise_property_error(property)
|
||||
end
|
||||
@@ -459,7 +459,7 @@ module Liquid
|
||||
# @liquid_syntax array | some: string, string
|
||||
# @liquid_return [boolean]
|
||||
def has(input, property, target_value = nil)
|
||||
filter_array(input, property, target_value) { |ary, &block| ary.any?(&block) }
|
||||
filter_array(input, property, target_value, false) { |ary, &block| ary.any?(&block) }
|
||||
end
|
||||
|
||||
# @liquid_public_docs
|
||||
@@ -472,7 +472,7 @@ module Liquid
|
||||
# @liquid_syntax array | find: string, string
|
||||
# @liquid_return [untyped]
|
||||
def find(input, property, target_value = nil)
|
||||
filter_array(input, property, target_value) { |ary, &block| ary.find(&block) }
|
||||
filter_array(input, property, target_value, nil) { |ary, &block| ary.find(&block) }
|
||||
end
|
||||
|
||||
# @liquid_public_docs
|
||||
@@ -485,7 +485,7 @@ module Liquid
|
||||
# @liquid_syntax array | find_index: string, string
|
||||
# @liquid_return [number]
|
||||
def find_index(input, property, target_value = nil)
|
||||
filter_array(input, property, target_value) { |ary, &block| ary.find_index(&block) }
|
||||
filter_array(input, property, target_value, nil) { |ary, &block| ary.find_index(&block) }
|
||||
end
|
||||
|
||||
# @liquid_public_docs
|
||||
@@ -504,7 +504,7 @@ module Liquid
|
||||
[]
|
||||
else
|
||||
ary.uniq do |item|
|
||||
fetch_property(item, property)
|
||||
item[property]
|
||||
rescue TypeError
|
||||
raise_property_error(property)
|
||||
rescue NoMethodError
|
||||
@@ -540,7 +540,7 @@ module Liquid
|
||||
if property == "to_liquid"
|
||||
e
|
||||
elsif e.respond_to?(:[])
|
||||
r = fetch_property(e, property)
|
||||
r = e[property]
|
||||
r.is_a?(Proc) ? r.call : r
|
||||
end
|
||||
end
|
||||
@@ -564,7 +564,7 @@ module Liquid
|
||||
[]
|
||||
else
|
||||
ary.reject do |item|
|
||||
fetch_property(item, property).nil?
|
||||
item[property].nil?
|
||||
rescue TypeError
|
||||
raise_property_error(property)
|
||||
rescue NoMethodError
|
||||
@@ -950,7 +950,7 @@ module Liquid
|
||||
if property.nil?
|
||||
item
|
||||
elsif item.respond_to?(:[])
|
||||
fetch_property(item, property)
|
||||
item[property]
|
||||
else
|
||||
0
|
||||
end
|
||||
@@ -969,16 +969,16 @@ module Liquid
|
||||
|
||||
attr_reader :context
|
||||
|
||||
def filter_array(input, property, target_value, &block)
|
||||
def filter_array(input, property, target_value, default_value = [], &block)
|
||||
ary = InputIterator.new(input, context)
|
||||
|
||||
return [] if ary.empty?
|
||||
return default_value if ary.empty?
|
||||
|
||||
block.call(ary) do |item|
|
||||
if target_value.nil?
|
||||
fetch_property(item, property)
|
||||
item[property]
|
||||
else
|
||||
fetch_property(item, property) == target_value
|
||||
item[property] == target_value
|
||||
end
|
||||
rescue TypeError
|
||||
raise_property_error(property)
|
||||
@@ -988,31 +988,6 @@ module Liquid
|
||||
end
|
||||
end
|
||||
|
||||
def fetch_property(drop, property_or_keys)
|
||||
##
|
||||
# This keeps backward compatibility by supporting properties containing
|
||||
# dots. This is valid in Liquid syntax and used in some runtimes, such as
|
||||
# Shopify with metafields.
|
||||
#
|
||||
# Using this approach, properties like 'price.value' can be accessed in
|
||||
# both of the following examples:
|
||||
#
|
||||
# ```
|
||||
# [
|
||||
# { 'name' => 'Item 1', 'price.price' => 40000 },
|
||||
# { 'name' => 'Item 2', 'price' => { 'value' => 39900 } }
|
||||
# ]
|
||||
# ```
|
||||
value = drop[property_or_keys]
|
||||
|
||||
return value if !value.nil? || !property_or_keys.is_a?(String)
|
||||
|
||||
keys = property_or_keys.split('.')
|
||||
keys.reduce(drop) do |drop, key|
|
||||
drop.respond_to?(:[]) ? drop[key] : drop
|
||||
end
|
||||
end
|
||||
|
||||
def raise_property_error(property)
|
||||
raise Liquid::ArgumentError, "cannot select the property '#{property}'"
|
||||
end
|
||||
@@ -1063,7 +1038,18 @@ module Liquid
|
||||
end
|
||||
|
||||
def join(glue)
|
||||
to_a.join(glue.to_s)
|
||||
first = true
|
||||
output = +""
|
||||
each do |item|
|
||||
if first
|
||||
first = false
|
||||
else
|
||||
output << glue
|
||||
end
|
||||
|
||||
output << Liquid::Utils.to_s(item)
|
||||
end
|
||||
output
|
||||
end
|
||||
|
||||
def concat(args)
|
||||
|
||||
+28
-18
@@ -90,34 +90,38 @@ module Liquid
|
||||
obj
|
||||
end
|
||||
|
||||
if RUBY_VERSION >= '3.4'
|
||||
def self.to_s(obj, seen = {})
|
||||
case obj
|
||||
when Hash
|
||||
def self.to_s(obj, seen = {})
|
||||
case obj
|
||||
when Hash
|
||||
# If the custom hash implementation overrides `#to_s`, use their
|
||||
# custom implementation. Otherwise we use Liquid's default
|
||||
# implementation.
|
||||
if obj.class.instance_method(:to_s) == HASH_TO_S_METHOD
|
||||
hash_inspect(obj, seen)
|
||||
when Array
|
||||
array_inspect(obj, seen)
|
||||
else
|
||||
obj.to_s
|
||||
end
|
||||
when Array
|
||||
array_inspect(obj, seen)
|
||||
else
|
||||
obj.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def self.inspect(obj, seen = {})
|
||||
case obj
|
||||
when Hash
|
||||
def self.inspect(obj, seen = {})
|
||||
case obj
|
||||
when Hash
|
||||
# If the custom hash implementation overrides `#inspect`, use their
|
||||
# custom implementation. Otherwise we use Liquid's default
|
||||
# implementation.
|
||||
if obj.class.instance_method(:inspect) == HASH_INSPECT_METHOD
|
||||
hash_inspect(obj, seen)
|
||||
when Array
|
||||
array_inspect(obj, seen)
|
||||
else
|
||||
obj.inspect
|
||||
end
|
||||
end
|
||||
else
|
||||
def self.to_s(obj, seen = nil)
|
||||
obj.to_s
|
||||
end
|
||||
|
||||
def self.inspect(obj, seen = nil)
|
||||
when Array
|
||||
array_inspect(obj, seen)
|
||||
else
|
||||
obj.inspect
|
||||
end
|
||||
end
|
||||
@@ -175,5 +179,11 @@ module Liquid
|
||||
ensure
|
||||
seen.delete(hash.object_id)
|
||||
end
|
||||
|
||||
HASH_TO_S_METHOD = Hash.instance_method(:to_s)
|
||||
private_constant :HASH_TO_S_METHOD
|
||||
|
||||
HASH_INSPECT_METHOD = Hash.instance_method(:inspect)
|
||||
private_constant :HASH_INSPECT_METHOD
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
VERSION = "5.6.5"
|
||||
VERSION = "5.7.2"
|
||||
end
|
||||
|
||||
@@ -77,7 +77,30 @@ class HashRenderingTest < Minitest::Test
|
||||
assert_template_result("{\"numbers\"=>[{:foo=>42}]}", "{{ my_hash }}", { "my_hash" => { "numbers" => [{ foo: 42 }] } })
|
||||
end
|
||||
|
||||
def test_join_filter_with_hash
|
||||
array = [{ "key1" => "value1" }, { "key2" => "value2" }]
|
||||
glue = { "lol" => "wut" }
|
||||
assert_template_result("{\"key1\"=>\"value1\"}{\"lol\"=>\"wut\"}{\"key2\"=>\"value2\"}", "{{ my_array | join: glue }}", { "my_array" => array, "glue" => glue })
|
||||
end
|
||||
|
||||
def test_render_hash_with_hash_key
|
||||
assert_template_result("{{\"foo\"=>\"bar\"}=>42}", "{{ my_hash }}", { "my_hash" => { Hash["foo" => "bar"] => 42 } })
|
||||
end
|
||||
|
||||
def test_rendering_hash_with_custom_to_s_method_uses_custom_to_s
|
||||
my_hash = Class.new(Hash) do
|
||||
def to_s
|
||||
"kewl"
|
||||
end
|
||||
end.new
|
||||
|
||||
assert_template_result("kewl", "{{ my_hash }}", { "my_hash" => my_hash })
|
||||
end
|
||||
|
||||
def test_rendering_hash_without_custom_to_s_uses_default_inspect
|
||||
my_hash = Class.new(Hash).new
|
||||
my_hash[:foo] = :bar
|
||||
|
||||
assert_template_result("{:foo=>:bar}", "{{ my_hash }}", { "my_hash" => my_hash })
|
||||
end
|
||||
end
|
||||
|
||||
@@ -54,30 +54,6 @@ class TestEnumerable < Liquid::Drop
|
||||
end
|
||||
end
|
||||
|
||||
class TestDeepEnumerable < Liquid::Drop
|
||||
include Enumerable
|
||||
|
||||
class Product < Liquid::Drop
|
||||
attr_reader :title, :price, :premium
|
||||
|
||||
def initialize(title:, price:, premium: nil)
|
||||
@title = { "content" => title, "language" => "en" }
|
||||
@price = { "value" => price, "unit" => "USD" }
|
||||
@premium = { "category" => premium } if premium
|
||||
end
|
||||
end
|
||||
|
||||
def each(&block)
|
||||
[
|
||||
Product.new(title: "Pro goggles", price: 1299),
|
||||
Product.new(title: "Thermal gloves", price: 1299),
|
||||
Product.new(title: "Alpine jacket", price: 3999, premium: 'Basic'),
|
||||
Product.new(title: "Mountain boots", price: 3899, premium: 'Pro'),
|
||||
Product.new(title: "Safety helmet", price: 1999)
|
||||
].each(&block)
|
||||
end
|
||||
end
|
||||
|
||||
class NumberLikeThing < Liquid::Drop
|
||||
def initialize(amount)
|
||||
@amount = amount
|
||||
@@ -157,6 +133,18 @@ class StandardFiltersTest < Minitest::Test
|
||||
assert_equal([], @filters.slice(input, -(1 << 63), 6))
|
||||
end
|
||||
|
||||
def test_find_on_empty_array
|
||||
assert_nil(@filters.find([], 'foo', 'bar'))
|
||||
end
|
||||
|
||||
def test_find_index_on_empty_array
|
||||
assert_nil(@filters.find_index([], 'foo', 'bar'))
|
||||
end
|
||||
|
||||
def test_has_on_empty_array
|
||||
refute(@filters.has([], 'foo', 'bar'))
|
||||
end
|
||||
|
||||
def test_truncate
|
||||
assert_equal('1234...', @filters.truncate('1234567890', 7))
|
||||
assert_equal('1234567890', @filters.truncate('1234567890', 20))
|
||||
@@ -305,6 +293,16 @@ class StandardFiltersTest < Minitest::Test
|
||||
assert_equal('1121314', @filters.join([1, 2, 3, 4], 1))
|
||||
end
|
||||
|
||||
def test_join_calls_to_liquid_on_each_element
|
||||
drop = Class.new(Liquid::Drop) do
|
||||
def to_liquid
|
||||
'i did it'
|
||||
end
|
||||
end
|
||||
|
||||
assert_equal('i did it, i did it', @filters.join([drop.new, drop.new], ", "))
|
||||
end
|
||||
|
||||
def test_sort
|
||||
assert_equal([1, 2, 3, 4], @filters.sort([4, 3, 2, 1]))
|
||||
assert_equal([{ "a" => 1 }, { "a" => 2 }, { "a" => 3 }, { "a" => 4 }], @filters.sort([{ "a" => 4 }, { "a" => 3 }, { "a" => 1 }, { "a" => 2 }], "a"))
|
||||
@@ -416,15 +414,6 @@ class StandardFiltersTest < Minitest::Test
|
||||
end
|
||||
end
|
||||
|
||||
def test_sort_natural_with_deep_enumerables
|
||||
template = <<~LIQUID
|
||||
{{- products | sort_natural: 'title.content' | map: 'title.content' | join: ', ' -}}
|
||||
LIQUID
|
||||
expected_output = "Alpine jacket, Mountain boots, Pro goggles, Safety helmet, Thermal gloves"
|
||||
|
||||
assert_template_result(expected_output, template, { "products" => TestDeepEnumerable.new })
|
||||
end
|
||||
|
||||
def test_legacy_sort_hash
|
||||
assert_equal([{ a: 1, b: 2 }], @filters.sort(a: 1, b: 2))
|
||||
end
|
||||
@@ -461,15 +450,6 @@ class StandardFiltersTest < Minitest::Test
|
||||
end
|
||||
end
|
||||
|
||||
def test_uniq_with_deep_enumerables
|
||||
template = <<~LIQUID
|
||||
{{- products | uniq: 'price.value' | map: "title.content" | join: ', ' -}}
|
||||
LIQUID
|
||||
expected_output = "Pro goggles, Alpine jacket, Mountain boots, Safety helmet"
|
||||
|
||||
assert_template_result(expected_output, template, { "products" => TestDeepEnumerable.new })
|
||||
end
|
||||
|
||||
def test_compact_empty_array
|
||||
assert_equal([], @filters.compact([], "a"))
|
||||
end
|
||||
@@ -486,15 +466,6 @@ class StandardFiltersTest < Minitest::Test
|
||||
end
|
||||
end
|
||||
|
||||
def test_compact_with_deep_enumerables
|
||||
template = <<~LIQUID
|
||||
{{- products | compact: 'premium.category' | map: 'title.content' | join: ', ' -}}
|
||||
LIQUID
|
||||
expected_output = "Alpine jacket, Mountain boots"
|
||||
|
||||
assert_template_result(expected_output, template, { "products" => TestDeepEnumerable.new })
|
||||
end
|
||||
|
||||
def test_reverse
|
||||
assert_equal([4, 3, 2, 1], @filters.reverse([1, 2, 3, 4]))
|
||||
end
|
||||
@@ -604,15 +575,6 @@ class StandardFiltersTest < Minitest::Test
|
||||
assert_template_result("213", '{{ foo | sort: "bar" | map: "foo" }}', { "foo" => TestEnumerable.new })
|
||||
end
|
||||
|
||||
def test_sort_with_deep_enumerables
|
||||
template = <<~LIQUID
|
||||
{{- products | sort: 'price.value' | map: 'title.content' | join: ', ' -}}
|
||||
LIQUID
|
||||
expected_output = "Pro goggles, Thermal gloves, Safety helmet, Mountain boots, Alpine jacket"
|
||||
|
||||
assert_template_result(expected_output, template, { "products" => TestDeepEnumerable.new })
|
||||
end
|
||||
|
||||
def test_first_and_last_call_to_liquid
|
||||
assert_template_result('foobar', '{{ foo | first }}', { 'foo' => [ThingWithToLiquid.new] })
|
||||
assert_template_result('foobar', '{{ foo | last }}', { 'foo' => [ThingWithToLiquid.new] })
|
||||
@@ -929,15 +891,6 @@ class StandardFiltersTest < Minitest::Test
|
||||
assert_template_result(expected_output, template, { "array" => array })
|
||||
end
|
||||
|
||||
def test_reject_with_deep_enumerables
|
||||
template = <<~LIQUID
|
||||
{{- products | reject: 'title.content', 'Pro goggles' | map: 'price.value' | join: ', ' -}}
|
||||
LIQUID
|
||||
expected_output = "1299, 3999, 3899, 1999"
|
||||
|
||||
assert_template_result(expected_output, template, { "products" => TestDeepEnumerable.new })
|
||||
end
|
||||
|
||||
def test_has
|
||||
array = [
|
||||
{ "handle" => "alpha", "ok" => true },
|
||||
@@ -966,6 +919,18 @@ class StandardFiltersTest < Minitest::Test
|
||||
assert_template_result(expected_output, "{{ array | has: 'ok', true }}", { "array" => array })
|
||||
end
|
||||
|
||||
def test_has_with_empty_arrays
|
||||
template = <<~LIQUID
|
||||
{%- assign has_product = products | has: 'title.content', 'Not found' -%}
|
||||
{%- unless has_product -%}
|
||||
Product not found.
|
||||
{%- endunless -%}
|
||||
LIQUID
|
||||
expected_output = "Product not found."
|
||||
|
||||
assert_template_result(expected_output, template, { "products" => [] })
|
||||
end
|
||||
|
||||
def test_has_with_false_value
|
||||
array = [
|
||||
{ "handle" => "alpha", "ok" => true },
|
||||
@@ -994,16 +959,6 @@ class StandardFiltersTest < Minitest::Test
|
||||
assert_template_result(expected_output, template, { "array" => array })
|
||||
end
|
||||
|
||||
def test_has_with_deep_enumerables
|
||||
template = <<~LIQUID
|
||||
{{- products | has: 'title.content', 'Pro goggles' -}},
|
||||
{{- products | has: 'title.content', 'foo' -}}
|
||||
LIQUID
|
||||
expected_output = "true,false"
|
||||
|
||||
assert_template_result(expected_output, template, { "products" => TestDeepEnumerable.new })
|
||||
end
|
||||
|
||||
def test_find_with_value
|
||||
products = [
|
||||
{ "title" => "Pro goggles", "price" => 1299 },
|
||||
@@ -1022,14 +977,16 @@ class StandardFiltersTest < Minitest::Test
|
||||
assert_template_result(expected_output, template, { "products" => products })
|
||||
end
|
||||
|
||||
def test_find_with_deep_enumerables
|
||||
def test_find_with_empty_arrays
|
||||
template = <<~LIQUID
|
||||
{%- assign product = products | find: 'title.content', 'Pro goggles' -%}
|
||||
{{- product.title.content -}}
|
||||
{%- assign product = products | find: 'title.content', 'Not found' -%}
|
||||
{%- unless product -%}
|
||||
Product not found.
|
||||
{%- endunless -%}
|
||||
LIQUID
|
||||
expected_output = "Pro goggles"
|
||||
expected_output = "Product not found."
|
||||
|
||||
assert_template_result(expected_output, template, { "products" => TestDeepEnumerable.new })
|
||||
assert_template_result(expected_output, template, { "products" => [] })
|
||||
end
|
||||
|
||||
def test_find_index_with_value
|
||||
@@ -1050,14 +1007,16 @@ class StandardFiltersTest < Minitest::Test
|
||||
assert_template_result(expected_output, template, { "products" => products })
|
||||
end
|
||||
|
||||
def test_find_index_with_deep_enumerables
|
||||
def test_find_index_with_empty_arrays
|
||||
template = <<~LIQUID
|
||||
{%- assign index = products | find_index: 'title.content', 'Alpine jacket' -%}
|
||||
{{- index -}}
|
||||
{%- assign index = products | find_index: 'title.content', 'Not found' -%}
|
||||
{%- unless index -%}
|
||||
Index not found.
|
||||
{%- endunless -%}
|
||||
LIQUID
|
||||
expected_output = "2"
|
||||
expected_output = "Index not found."
|
||||
|
||||
assert_template_result(expected_output, template, { "products" => TestDeepEnumerable.new })
|
||||
assert_template_result(expected_output, template, { "products" => [] })
|
||||
end
|
||||
|
||||
def test_where
|
||||
@@ -1158,15 +1117,6 @@ class StandardFiltersTest < Minitest::Test
|
||||
assert_nil(@filters.where([nil], "ok"))
|
||||
end
|
||||
|
||||
def test_where_with_deep_enumerables
|
||||
template = <<~LIQUID
|
||||
{{- products | where: 'title.content', 'Pro goggles' | map: 'price.value' -}}
|
||||
LIQUID
|
||||
expected_output = "1299"
|
||||
|
||||
assert_template_result(expected_output, template, { "products" => TestDeepEnumerable.new })
|
||||
end
|
||||
|
||||
def test_all_filters_never_raise_non_liquid_exception
|
||||
test_drop = TestDrop.new(value: "test")
|
||||
test_drop.context = Context.new
|
||||
@@ -1318,15 +1268,6 @@ class StandardFiltersTest < Minitest::Test
|
||||
assert_template_result("0", "{{ input | sum: 'subtotal' }}", { "input" => input })
|
||||
end
|
||||
|
||||
def test_sum_with_deep_enumerables
|
||||
template = <<~LIQUID
|
||||
{{- products | sum: 'price.value' -}}
|
||||
LIQUID
|
||||
expected_output = "12495"
|
||||
|
||||
assert_template_result(expected_output, template, { "products" => TestDeepEnumerable.new })
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def with_timezone(tz)
|
||||
|
||||
Reference in New Issue
Block a user