Compare commits

...
Author SHA1 Message Date
Ian Ker-Seymer f5964b7511 Refactor limit tracking using render_score flag
Remove reached_limit field; use render_score == -1 to indicate limits reached. Update related methods accordingly for clarity and consistency.
2025-05-16 15:15:03 -04:00
Ian Ker-Seymer 8511d1b1c9 Refactor ResourceLimits to use a struct
Switch ResourceLimits to a Struct for more compact
data representation and cleaner initialization logic.
2025-05-16 15:12:07 -04:00
James MengandGitHub c6f05eaf83 Merge pull request #1955 from Shopify/jm/trim_doc_tag_desc
Remove {{ foo }} and {{ bar }} from `doc` tag description
2025-05-06 15:29:29 -07:00
James Meng eabbd5cb6f Remove {{ foo }} and {{ bar }} from doc tag description 2025-05-06 15:20:35 -07:00
Marco Concetto RudilossoandGitHub ea864f1177 Merge pull request #1951 from Shopify/actual-revert
Fully revert calling to_s on filter array
2025-04-14 17:49:47 +02:00
Marco Concetto Rudilosso c34dd812c5 Fully revert calling to_s on filter array 2025-04-14 17:36:45 +02:00
Marco Concetto RudilossoandGitHub cc04892e54 Merge pull request #1943 from Shopify/revert-to-s
Revert `Utils.to_s` on all array filters
2025-04-14 17:22:10 +02:00
Marco Concetto Rudilosso f676e699a4 rollback changes to tests 2025-04-14 11:24:01 +02:00
Marco Concetto Rudilosso fc0f7f44c1 use Utils.to_s on property error 2025-04-14 11:17:59 +02:00
Marco Concetto Rudilosso b459eb656f remove test 2025-04-14 11:17:46 +02:00
Marco Concetto Rudilosso c6dcf3e714 add test 2025-04-14 11:17:46 +02:00
Marco Concetto Rudilosso 4dae678c63 Revert "Stringify properties before filtering (#1929)"
This reverts commit f5d6a36574.
2025-04-14 11:17:46 +02:00
Marco Concetto Rudilosso 4c07ff920b Revert "Always stringify properties in all array filters (#1936)"
This reverts commit aa1640035f.
2025-04-14 11:17:46 +02:00
Ian Ker-SeymerandGitHub dbe709c3bf Use to_liquid_value in uniq filter (#1948)
* Use to_liquid_value in uniq filter

* Bump version to 5.8.4
2025-04-09 15:00:01 -04:00
7 changed files with 78 additions and 85 deletions
+1 -1
View File
@@ -1 +1 @@
3.4.1
3.4.3
+17 -26
View File
@@ -1,61 +1,52 @@
# frozen_string_literal: true
module Liquid
class ResourceLimits
attr_accessor :render_length_limit, :render_score_limit, :assign_score_limit
attr_reader :render_score, :assign_score
def initialize(limits)
@render_length_limit = limits[:render_length_limit]
@render_score_limit = limits[:render_score_limit]
@assign_score_limit = limits[:assign_score_limit]
reset
end
class ResourceLimits < Struct.new(:render_length_limit, :render_score_limit, :assign_score_limit, :render_score, :assign_score, :last_capture_length, keyword_init: true)
def increment_render_score(amount)
@render_score += amount
raise_limits_reached if @render_score_limit && @render_score > @render_score_limit
self.render_score ||= 0
self.render_score += amount
raise_limits_reached if render_score_limit && render_score > render_score_limit
end
def increment_assign_score(amount)
@assign_score += amount
raise_limits_reached if @assign_score_limit && @assign_score > @assign_score_limit
self.assign_score ||= 0
self.assign_score += amount
raise_limits_reached if assign_score_limit && assign_score > assign_score_limit
end
# update either render_length or assign_score based on whether or not the writes are captured
def increment_write_score(output)
if (last_captured = @last_capture_length)
if (last_captured = last_capture_length)
captured = output.bytesize
increment = captured - last_captured
@last_capture_length = captured
self.last_capture_length = captured
increment_assign_score(increment)
elsif @render_length_limit && output.bytesize > @render_length_limit
elsif render_length_limit && output.bytesize > render_length_limit
raise_limits_reached
end
end
def raise_limits_reached
@reached_limit = true
self.render_score = -1
raise MemoryError, "Memory limits exceeded"
end
def reached?
@reached_limit
self.render_score == -1
end
def reset
@reached_limit = false
@last_capture_length = nil
@render_score = @assign_score = 0
self.last_capture_length = nil
self.render_score = self.assign_score = 0
end
def with_capture
old_capture_length = @last_capture_length
old_capture_length = last_capture_length
begin
@last_capture_length = 0
self.last_capture_length = 0
yield
ensure
@last_capture_length = old_capture_length
self.last_capture_length = old_capture_length
end
end
end
+6 -15
View File
@@ -386,7 +386,6 @@ module Liquid
end
elsif ary.all? { |el| el.respond_to?(:[]) }
begin
property = Utils.to_s(property)
ary.sort { |a, b| nil_safe_compare(a[property], b[property]) }
rescue TypeError
raise_property_error(property)
@@ -416,7 +415,6 @@ module Liquid
end
elsif ary.all? { |el| el.respond_to?(:[]) }
begin
property = Utils.to_s(property)
ary.sort { |a, b| nil_safe_casecmp(a[property], b[property]) }
rescue TypeError
raise_property_error(property)
@@ -504,7 +502,6 @@ module Liquid
elsif ary.empty? # The next two cases assume a non-empty array.
[]
else
property = Utils.to_s(property)
ary.uniq do |item|
item[property]
rescue TypeError
@@ -536,11 +533,6 @@ module Liquid
# @liquid_syntax array | map: string
# @liquid_return [array[untyped]]
def map(input, property)
property = Utils.to_s(property)
# Return the input array if property is empty (no-op)
return InputIterator.new(input, context).to_a if property.empty?
InputIterator.new(input, context).map do |e|
e = e.call if e.is_a?(Proc)
@@ -570,7 +562,6 @@ module Liquid
elsif ary.empty? # The next two cases assume a non-empty array.
[]
else
property = Liquid::Utils.to_s(property)
ary.reject do |item|
item[property].nil?
rescue TypeError
@@ -960,8 +951,6 @@ module Liquid
# @liquid_syntax array | sum
# @liquid_return [number]
def sum(input, property = nil)
property = property.nil? ? nil : Utils.to_s(property)
ary = InputIterator.new(input, context)
return 0 if ary.empty?
@@ -990,9 +979,8 @@ module Liquid
def filter_array(input, property, target_value, default_value = [], &block)
ary = InputIterator.new(input, context)
return default_value if ary.empty?
property = Utils.to_s(property)
return default_value if ary.empty?
block.call(ary) do |item|
if target_value.nil?
@@ -1009,7 +997,7 @@ module Liquid
end
def raise_property_error(property)
raise Liquid::ArgumentError, "cannot select the property '#{property}'"
raise Liquid::ArgumentError, "cannot select the property '#{Utils.to_s(property)}'"
end
def apply_operation(input, operand, operation)
@@ -1081,7 +1069,10 @@ module Liquid
end
def uniq(&block)
to_a.uniq(&block)
to_a.uniq do |item|
item = Utils.to_liquid_value(item)
block ? yield(item) : item
end
end
def compact
-1
View File
@@ -27,7 +27,6 @@ module Liquid
# @example
# {% render 'message', foo: 'Hello', bar: 'World' %}
# {% enddoc %}
# {{ foo }}, {{ bar }}!
class Doc < Block
NO_UNEXPECTED_ARGS = /\A\s*\z/
+1 -1
View File
@@ -2,5 +2,5 @@
# frozen_string_literal: true
module Liquid
VERSION = "5.8.3"
VERSION = "5.8.6"
end
+24 -41
View File
@@ -560,26 +560,6 @@ class StandardFiltersTest < Minitest::Test
end
end
def test_map_with_nil_property
array = [
{ "handle" => "alpha", "value" => "A" },
{ "handle" => "beta", "value" => "B" },
{ "handle" => "gamma", "value" => "C" }
]
assert_template_result("alpha beta gamma", "{{ array | map: nil | map: 'handle' | join: ' ' }}", { "array" => array })
end
def test_map_with_empty_string_property
array = [
{ "handle" => "alpha", "value" => "A" },
{ "handle" => "beta", "value" => "B" },
{ "handle" => "gamma", "value" => "C" }
]
assert_template_result("alpha beta gamma", "{{ array | map: '' | map: 'handle' | join: ' ' }}", { "array" => array })
end
def test_map_with_value_property
array = [
{ "handle" => "alpha", "value" => "A" },
@@ -591,16 +571,15 @@ class StandardFiltersTest < Minitest::Test
end
def test_map_returns_input_with_no_property
input = [
foo = [
[1],
[2],
[3],
]
result = @filters.map(input, nil)
assert_equal(input.flatten, result)
result = @filters.map(input, '')
assert_equal(input.flatten, result)
assert_raises(Liquid::ArgumentError) do
@filters.map(foo, nil)
end
end
def test_sort_works_on_enumerables
@@ -1075,10 +1054,11 @@ class StandardFiltersTest < Minitest::Test
def test_where_with_nil_is_a_no_op
environment = { "array" => ["alpha", "beta", "gamma"] }
expected_output = "alpha beta gamma"
template = "{{ array | where: nil | join: ' ' }}"
assert_template_result(expected_output, template, environment)
assert_raises(Liquid::ArgumentError) do
assert_template_result("alpha beta gamma", template, environment)
end
end
def test_where_with_value
@@ -1109,19 +1089,6 @@ class StandardFiltersTest < Minitest::Test
assert_template_result(expected_output, template, { "array" => array })
end
def test_where_with_non_string_property
array = [
{ "handle" => "alpha", "{}" => true },
{ "handle" => "beta", "{}" => false },
{ "handle" => "gamma", "{}" => false },
{ "handle" => "delta", "{}" => true },
]
template = "{{ array | where: some_property, true | map: 'handle' | join: ' ' }}"
expected_output = "alpha delta"
assert_template_result(expected_output, template, { "array" => array, "some_property" => {} })
end
def test_where_string_keys
input = [
"alpha", "beta", "gamma", "delta"
@@ -1330,7 +1297,7 @@ class StandardFiltersTest < Minitest::Test
end
def test_sum_with_non_string_property
input = [{ "true" => 1 }, { "1.0" => 0.2, "1" => -0.3 }, { "1..5" => 0.4 }]
input = [{ true => 1 }, { 1.0 => 0.2, 1 => -0.3 }, { 1..5 => 0.4 }]
assert_equal(1, @filters.sum(input, true))
assert_equal(0.2, @filters.sum(input, 1.0))
@@ -1340,6 +1307,22 @@ class StandardFiltersTest < Minitest::Test
assert_equal(0, @filters.sum(input, ""))
end
def test_uniq_with_to_liquid_value
input = [StringDrop.new("foo"), StringDrop.new("bar"), "foo"]
expected = [StringDrop.new("foo"), StringDrop.new("bar")]
result = @filters.uniq(input)
assert_equal(expected, result)
end
def test_uniq_with_to_liquid_value_pick_correct_classes
input = ["foo", StringDrop.new("foo"), StringDrop.new("bar")]
expected = [String, StringDrop]
result = @filters.uniq(input).map(&:class)
assert_equal(expected, result)
end
private
def with_timezone(tz)
+29
View File
@@ -146,6 +146,35 @@ class BooleanDrop < Liquid::Drop
end
end
class StringDrop < Liquid::Drop
include Comparable
def initialize(value)
super()
@value = value
end
def to_liquid_value
@value
end
def to_s
@value
end
def to_str
@value
end
def inspect
"#<StringDrop @value=#{@value.inspect}>"
end
def <=>(other)
to_liquid_value <=> Liquid::Utils.to_liquid_value(other)
end
end
class ErrorDrop < Liquid::Drop
def standard_error
raise Liquid::StandardError, 'standard error'