liquid 5.6.4 → 5.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 33f5ba5a5f6175dc7ddf6f0f618581fac11cf81b33ed508f174590f2a0911b4a
4
- data.tar.gz: 07e512179364e4c9be0649ada3495a5314e1c57d97e91b697cac098c175bd1c7
3
+ metadata.gz: 72a0f18697a90c81db846fc86027cbec36198105fb458d40989f88b1acd55687
4
+ data.tar.gz: ba8f6ecc9612f737f954006109b91759ea7cea41074de2ab38e73221d4e18be2
5
5
  SHA512:
6
- metadata.gz: 7f54ca814d38f03c384b147776cb645686c61a5962bf9e736220d60bd2b6c0a9e5e853643429349170a1920ce5b1373b3d0c6ff47118afc0568368823e6dc920
7
- data.tar.gz: 46ecbe3e271d8a7083d8d58f360d9476594d434561cbafec829cd1294ceb44c2a623096e084206ef220808974f7a804a92945b0a60290d414ca9651ceb74f20c
6
+ metadata.gz: ad608314f023c78123d3cf57a3c9b54229ec7dca712f1b3897218c27880e5f78d7188cdce775d15759960803a31c5e7d1adc81c9282ab784a6f6fa50efeb051e
7
+ data.tar.gz: 6988a403789ca9a3a3d3d48e5cba0639f81226b3cd8af477dcce1d8eeb9db3693e90b373ea391f5d458a723ec40d7c0c6d28f7e445d3d0562b219e9cbcfff1ee
data/History.md CHANGED
@@ -1,11 +1,53 @@
1
1
  # Liquid Change Log
2
2
 
3
- ## 5.6.0 (unreleased)
3
+ ## 5.8.0 (unreleased)
4
+
5
+ ## 5.7.0 2025-01-16
6
+
7
+ ### Features
8
+ * Add `find`, `find_index`, `has`, and `reject` filters to arrays
9
+ * Compatibility with Ruby 3.4
10
+
11
+ ## 5.6.4 2025-01-14
4
12
 
5
13
  ### Fixes
14
+ * Add a default `string_scanner` to avoid errors with `Liquid::VariableLookup.parse("foo.bar")` [Ian Ker-Seymer]
6
15
 
7
- * Fix Tokenizer to handle null source value (#1873) [Bahar Pourazar]
16
+ ## 5.6.3 2025-01-13
17
+ * Remove `lru_redux` dependency [Michael Go]
18
+
19
+ ## 5.6.2 2025-01-13
20
+
21
+ ### Fixes
22
+ * Preserve the old behavior of requiring floats to start with a digit [Michael Go]
8
23
 
24
+ ## 5.6.1 2025-01-13
25
+
26
+ ### Performance improvements
27
+ * Faster Expression parser / Tokenizer with StringScanner [Michael Go]
28
+
29
+ ## 5.6.0 2024-12-19
30
+
31
+ ### Architectural changes
32
+ * Added new `Environment` class to manage configuration and state that was previously stored in `Template` [Ian Ker-Seymer]
33
+ * Moved tag registration from `Template` to `Environment` [Ian Ker-Seymer]
34
+ * Removed `StrainerFactory` in favor of `Environment`-based strainer creation [Ian Ker-Seymer]
35
+ * Consolidated standard tags into a new `Tags` module with `STANDARD_TAGS` constant [Ian Ker-Seymer]
36
+
37
+ ### Performance improvements
38
+ * Optimized `Lexer` with a new `Lexer2` implementation using jump tables for faster tokenization, requires Ruby 3.4 [Ian Ker-Seymer]
39
+ * Improved variable rendering with specialized handling for different types [Michael Go]
40
+ * Reduced array allocations by using frozen empty constants [Michael Go]
41
+
42
+ ### API changes
43
+ * Deprecated several `Template` class methods in favor of `Environment` methods [Ian Ker-Seymer]
44
+ * Added deprecation warnings system [Ian Ker-Seymer]
45
+ * Changed how filters and tags are registered to use Environment [Ian Ker-Seymer]
46
+
47
+ ### Fixes
48
+ * Fixed table row handling of break interrupts [Alex Coco]
49
+ * Improved variable output handling for arrays [Ian Ker-Seymer]
50
+ * Fix Tokenizer to handle null source value (#1873) [Bahar Pourazar]
9
51
 
10
52
  ## 5.5.0 2024-03-21
11
53
 
@@ -64,7 +64,7 @@ module Liquid
64
64
  # @liquid_syntax string | downcase
65
65
  # @liquid_return [string]
66
66
  def downcase(input)
67
- input.to_s.downcase
67
+ Utils.to_s(input).downcase
68
68
  end
69
69
 
70
70
  # @liquid_public_docs
@@ -75,7 +75,7 @@ module Liquid
75
75
  # @liquid_syntax string | upcase
76
76
  # @liquid_return [string]
77
77
  def upcase(input)
78
- input.to_s.upcase
78
+ Utils.to_s(input).upcase
79
79
  end
80
80
 
81
81
  # @liquid_public_docs
@@ -86,7 +86,7 @@ module Liquid
86
86
  # @liquid_syntax string | capitalize
87
87
  # @liquid_return [string]
88
88
  def capitalize(input)
89
- input.to_s.capitalize
89
+ Utils.to_s(input).capitalize
90
90
  end
91
91
 
92
92
  # @liquid_public_docs
@@ -97,7 +97,7 @@ module Liquid
97
97
  # @liquid_syntax string | escape
98
98
  # @liquid_return [string]
99
99
  def escape(input)
100
- CGI.escapeHTML(input.to_s) unless input.nil?
100
+ CGI.escapeHTML(Utils.to_s(input)) unless input.nil?
101
101
  end
102
102
  alias_method :h, :escape
103
103
 
@@ -109,7 +109,7 @@ module Liquid
109
109
  # @liquid_syntax string | escape_once
110
110
  # @liquid_return [string]
111
111
  def escape_once(input)
112
- input.to_s.gsub(HTML_ESCAPE_ONCE_REGEXP, HTML_ESCAPE)
112
+ Utils.to_s(input).gsub(HTML_ESCAPE_ONCE_REGEXP, HTML_ESCAPE)
113
113
  end
114
114
 
115
115
  # @liquid_public_docs
@@ -124,7 +124,7 @@ module Liquid
124
124
  # @liquid_syntax string | url_encode
125
125
  # @liquid_return [string]
126
126
  def url_encode(input)
127
- CGI.escape(input.to_s) unless input.nil?
127
+ CGI.escape(Utils.to_s(input)) unless input.nil?
128
128
  end
129
129
 
130
130
  # @liquid_public_docs
@@ -138,7 +138,7 @@ module Liquid
138
138
  def url_decode(input)
139
139
  return if input.nil?
140
140
 
141
- result = CGI.unescape(input.to_s)
141
+ result = CGI.unescape(Utils.to_s(input))
142
142
  raise Liquid::ArgumentError, "invalid byte sequence in #{result.encoding}" unless result.valid_encoding?
143
143
 
144
144
  result
@@ -152,7 +152,7 @@ module Liquid
152
152
  # @liquid_syntax string | base64_encode
153
153
  # @liquid_return [string]
154
154
  def base64_encode(input)
155
- Base64.strict_encode64(input.to_s)
155
+ Base64.strict_encode64(Utils.to_s(input))
156
156
  end
157
157
 
158
158
  # @liquid_public_docs
@@ -163,7 +163,7 @@ module Liquid
163
163
  # @liquid_syntax string | base64_decode
164
164
  # @liquid_return [string]
165
165
  def base64_decode(input)
166
- input = input.to_s
166
+ input = Utils.to_s(input)
167
167
  StandardFilters.try_coerce_encoding(Base64.strict_decode64(input), encoding: input.encoding)
168
168
  rescue ::ArgumentError
169
169
  raise Liquid::ArgumentError, "invalid base64 provided to base64_decode"
@@ -177,7 +177,7 @@ module Liquid
177
177
  # @liquid_syntax string | base64_url_safe_encode
178
178
  # @liquid_return [string]
179
179
  def base64_url_safe_encode(input)
180
- Base64.urlsafe_encode64(input.to_s)
180
+ Base64.urlsafe_encode64(Utils.to_s(input))
181
181
  end
182
182
 
183
183
  # @liquid_public_docs
@@ -188,7 +188,7 @@ module Liquid
188
188
  # @liquid_syntax string | base64_url_safe_decode
189
189
  # @liquid_return [string]
190
190
  def base64_url_safe_decode(input)
191
- input = input.to_s
191
+ input = Utils.to_s(input)
192
192
  StandardFilters.try_coerce_encoding(Base64.urlsafe_decode64(input), encoding: input.encoding)
193
193
  rescue ::ArgumentError
194
194
  raise Liquid::ArgumentError, "invalid base64 provided to base64_url_safe_decode"
@@ -212,7 +212,7 @@ module Liquid
212
212
  if input.is_a?(Array)
213
213
  input.slice(offset, length) || []
214
214
  else
215
- input.to_s.slice(offset, length) || ''
215
+ Utils.to_s(input).slice(offset, length) || ''
216
216
  end
217
217
  rescue RangeError
218
218
  if I64_RANGE.cover?(length) && I64_RANGE.cover?(offset)
@@ -236,10 +236,10 @@ module Liquid
236
236
  # @liquid_return [string]
237
237
  def truncate(input, length = 50, truncate_string = "...")
238
238
  return if input.nil?
239
- input_str = input.to_s
239
+ input_str = Utils.to_s(input)
240
240
  length = Utils.to_integer(length)
241
241
 
242
- truncate_string_str = truncate_string.to_s
242
+ truncate_string_str = Utils.to_s(truncate_string)
243
243
 
244
244
  l = length - truncate_string_str.length
245
245
  l = 0 if l < 0
@@ -263,7 +263,7 @@ module Liquid
263
263
  # @liquid_return [string]
264
264
  def truncatewords(input, words = 15, truncate_string = "...")
265
265
  return if input.nil?
266
- input = input.to_s
266
+ input = Utils.to_s(input)
267
267
  words = Utils.to_integer(words)
268
268
  words = 1 if words <= 0
269
269
 
@@ -277,7 +277,8 @@ module Liquid
277
277
  return input if wordlist.length <= words
278
278
 
279
279
  wordlist.pop
280
- wordlist.join(" ").concat(truncate_string.to_s)
280
+ truncate_string = Utils.to_s(truncate_string)
281
+ wordlist.join(" ").concat(truncate_string)
281
282
  end
282
283
 
283
284
  # @liquid_public_docs
@@ -288,7 +289,9 @@ module Liquid
288
289
  # @liquid_syntax string | split: string
289
290
  # @liquid_return [array[string]]
290
291
  def split(input, pattern)
291
- input.to_s.split(pattern.to_s)
292
+ pattern = Utils.to_s(pattern)
293
+ input = Utils.to_s(input)
294
+ input.split(pattern)
292
295
  end
293
296
 
294
297
  # @liquid_public_docs
@@ -299,7 +302,8 @@ module Liquid
299
302
  # @liquid_syntax string | strip
300
303
  # @liquid_return [string]
301
304
  def strip(input)
302
- input.to_s.strip
305
+ input = Utils.to_s(input)
306
+ input.strip
303
307
  end
304
308
 
305
309
  # @liquid_public_docs
@@ -310,7 +314,8 @@ module Liquid
310
314
  # @liquid_syntax string | lstrip
311
315
  # @liquid_return [string]
312
316
  def lstrip(input)
313
- input.to_s.lstrip
317
+ input = Utils.to_s(input)
318
+ input.lstrip
314
319
  end
315
320
 
316
321
  # @liquid_public_docs
@@ -321,7 +326,8 @@ module Liquid
321
326
  # @liquid_syntax string | rstrip
322
327
  # @liquid_return [string]
323
328
  def rstrip(input)
324
- input.to_s.rstrip
329
+ input = Utils.to_s(input)
330
+ input.rstrip
325
331
  end
326
332
 
327
333
  # @liquid_public_docs
@@ -332,8 +338,9 @@ module Liquid
332
338
  # @liquid_syntax string | strip_html
333
339
  # @liquid_return [string]
334
340
  def strip_html(input)
341
+ input = Utils.to_s(input)
335
342
  empty = ''
336
- result = input.to_s.gsub(STRIP_HTML_BLOCKS, empty)
343
+ result = input.gsub(STRIP_HTML_BLOCKS, empty)
337
344
  result.gsub!(STRIP_HTML_TAGS, empty)
338
345
  result
339
346
  end
@@ -346,7 +353,8 @@ module Liquid
346
353
  # @liquid_syntax string | strip_newlines
347
354
  # @liquid_return [string]
348
355
  def strip_newlines(input)
349
- input.to_s.gsub(/\r?\n/, '')
356
+ input = Utils.to_s(input)
357
+ input.gsub(/\r?\n/, '')
350
358
  end
351
359
 
352
360
  # @liquid_public_docs
@@ -357,6 +365,7 @@ module Liquid
357
365
  # @liquid_syntax array | join
358
366
  # @liquid_return [string]
359
367
  def join(input, glue = ' ')
368
+ glue = Utils.to_s(glue)
360
369
  InputIterator.new(input, context).join(glue)
361
370
  end
362
371
 
@@ -378,7 +387,7 @@ module Liquid
378
387
  end
379
388
  elsif ary.all? { |el| el.respond_to?(:[]) }
380
389
  begin
381
- ary.sort { |a, b| nil_safe_compare(a[property], b[property]) }
390
+ ary.sort { |a, b| nil_safe_compare(fetch_property(a, property), fetch_property(b, property)) }
382
391
  rescue TypeError
383
392
  raise_property_error(property)
384
393
  end
@@ -407,7 +416,7 @@ module Liquid
407
416
  end
408
417
  elsif ary.all? { |el| el.respond_to?(:[]) }
409
418
  begin
410
- ary.sort { |a, b| nil_safe_casecmp(a[property], b[property]) }
419
+ ary.sort { |a, b| nil_safe_casecmp(fetch_property(a, property), fetch_property(b, property)) }
411
420
  rescue TypeError
412
421
  raise_property_error(property)
413
422
  end
@@ -424,29 +433,59 @@ module Liquid
424
433
  # @liquid_syntax array | where: string, string
425
434
  # @liquid_return [array[untyped]]
426
435
  def where(input, property, target_value = nil)
427
- ary = InputIterator.new(input, context)
436
+ filter_array(input, property, target_value) { |ary, &block| ary.select(&block) }
437
+ end
428
438
 
429
- if ary.empty?
430
- []
431
- elsif target_value.nil?
432
- ary.select do |item|
433
- item[property]
434
- rescue TypeError
435
- raise_property_error(property)
436
- rescue NoMethodError
437
- return nil unless item.respond_to?(:[])
438
- raise
439
- end
440
- else
441
- ary.select do |item|
442
- item[property] == target_value
443
- rescue TypeError
444
- raise_property_error(property)
445
- rescue NoMethodError
446
- return nil unless item.respond_to?(:[])
447
- raise
448
- end
449
- end
439
+ # @liquid_public_docs
440
+ # @liquid_type filter
441
+ # @liquid_category array
442
+ # @liquid_summary
443
+ # Filters an array to exclude items with a specific property value.
444
+ # @liquid_description
445
+ # This requires you to provide both the property name and the associated value.
446
+ # @liquid_syntax array | reject: string, string
447
+ # @liquid_return [array[untyped]]
448
+ def reject(input, property, target_value = nil)
449
+ filter_array(input, property, target_value) { |ary, &block| ary.reject(&block) }
450
+ end
451
+
452
+ # @liquid_public_docs
453
+ # @liquid_type filter
454
+ # @liquid_category array
455
+ # @liquid_summary
456
+ # Tests if any item in an array has a specific property value.
457
+ # @liquid_description
458
+ # This requires you to provide both the property name and the associated value.
459
+ # @liquid_syntax array | some: string, string
460
+ # @liquid_return [boolean]
461
+ def has(input, property, target_value = nil)
462
+ filter_array(input, property, target_value) { |ary, &block| ary.any?(&block) }
463
+ end
464
+
465
+ # @liquid_public_docs
466
+ # @liquid_type filter
467
+ # @liquid_category array
468
+ # @liquid_summary
469
+ # Returns the first item in an array with a specific property value.
470
+ # @liquid_description
471
+ # This requires you to provide both the property name and the associated value.
472
+ # @liquid_syntax array | find: string, string
473
+ # @liquid_return [untyped]
474
+ def find(input, property, target_value = nil)
475
+ filter_array(input, property, target_value) { |ary, &block| ary.find(&block) }
476
+ end
477
+
478
+ # @liquid_public_docs
479
+ # @liquid_type filter
480
+ # @liquid_category array
481
+ # @liquid_summary
482
+ # Returns the index of the first item in an array with a specific property value.
483
+ # @liquid_description
484
+ # This requires you to provide both the property name and the associated value.
485
+ # @liquid_syntax array | find_index: string, string
486
+ # @liquid_return [number]
487
+ def find_index(input, property, target_value = nil)
488
+ filter_array(input, property, target_value) { |ary, &block| ary.find_index(&block) }
450
489
  end
451
490
 
452
491
  # @liquid_public_docs
@@ -465,7 +504,7 @@ module Liquid
465
504
  []
466
505
  else
467
506
  ary.uniq do |item|
468
- item[property]
507
+ fetch_property(item, property)
469
508
  rescue TypeError
470
509
  raise_property_error(property)
471
510
  rescue NoMethodError
@@ -501,7 +540,7 @@ module Liquid
501
540
  if property == "to_liquid"
502
541
  e
503
542
  elsif e.respond_to?(:[])
504
- r = e[property]
543
+ r = fetch_property(e, property)
505
544
  r.is_a?(Proc) ? r.call : r
506
545
  end
507
546
  end
@@ -525,7 +564,7 @@ module Liquid
525
564
  []
526
565
  else
527
566
  ary.reject do |item|
528
- item[property].nil?
567
+ fetch_property(item, property).nil?
529
568
  rescue TypeError
530
569
  raise_property_error(property)
531
570
  rescue NoMethodError
@@ -543,7 +582,10 @@ module Liquid
543
582
  # @liquid_syntax string | replace: string, string
544
583
  # @liquid_return [string]
545
584
  def replace(input, string, replacement = '')
546
- input.to_s.gsub(string.to_s, replacement.to_s)
585
+ string = Utils.to_s(string)
586
+ replacement = Utils.to_s(replacement)
587
+ input = Utils.to_s(input)
588
+ input.gsub(string, replacement)
547
589
  end
548
590
 
549
591
  # @liquid_public_docs
@@ -554,7 +596,10 @@ module Liquid
554
596
  # @liquid_syntax string | replace_first: string, string
555
597
  # @liquid_return [string]
556
598
  def replace_first(input, string, replacement = '')
557
- input.to_s.sub(string.to_s, replacement.to_s)
599
+ string = Utils.to_s(string)
600
+ replacement = Utils.to_s(replacement)
601
+ input = Utils.to_s(input)
602
+ input.sub(string, replacement)
558
603
  end
559
604
 
560
605
  # @liquid_public_docs
@@ -565,9 +610,9 @@ module Liquid
565
610
  # @liquid_syntax string | replace_last: string, string
566
611
  # @liquid_return [string]
567
612
  def replace_last(input, string, replacement)
568
- input = input.to_s
569
- string = string.to_s
570
- replacement = replacement.to_s
613
+ input = Utils.to_s(input)
614
+ string = Utils.to_s(string)
615
+ replacement = Utils.to_s(replacement)
571
616
 
572
617
  start_index = input.rindex(string)
573
618
 
@@ -619,7 +664,9 @@ module Liquid
619
664
  # @liquid_syntax string | append: string
620
665
  # @liquid_return [string]
621
666
  def append(input, string)
622
- input.to_s + string.to_s
667
+ input = Utils.to_s(input)
668
+ string = Utils.to_s(string)
669
+ input + string
623
670
  end
624
671
 
625
672
  # @liquid_public_docs
@@ -648,7 +695,9 @@ module Liquid
648
695
  # @liquid_syntax string | prepend: string
649
696
  # @liquid_return [string]
650
697
  def prepend(input, string)
651
- string.to_s + input.to_s
698
+ input = Utils.to_s(input)
699
+ string = Utils.to_s(string)
700
+ string + input
652
701
  end
653
702
 
654
703
  # @liquid_public_docs
@@ -659,7 +708,8 @@ module Liquid
659
708
  # @liquid_syntax string | newline_to_br
660
709
  # @liquid_return [string]
661
710
  def newline_to_br(input)
662
- input.to_s.gsub(/\r?\n/, "<br />\n")
711
+ input = Utils.to_s(input)
712
+ input.gsub(/\r?\n/, "<br />\n")
663
713
  end
664
714
 
665
715
  # Reformat a date using Ruby's core Time#strftime( string ) -> string
@@ -694,11 +744,12 @@ module Liquid
694
744
  #
695
745
  # See also: http://www.ruby-doc.org/core/Time.html#method-i-strftime
696
746
  def date(input, format)
697
- return input if format.to_s.empty?
747
+ str_format = Utils.to_s(format)
748
+ return input if str_format.empty?
698
749
 
699
750
  return input unless (date = Utils.to_date(input))
700
751
 
701
- date.strftime(format.to_s)
752
+ date.strftime(str_format)
702
753
  end
703
754
 
704
755
  # @liquid_public_docs
@@ -899,7 +950,7 @@ module Liquid
899
950
  if property.nil?
900
951
  item
901
952
  elsif item.respond_to?(:[])
902
- item[property]
953
+ fetch_property(item, property)
903
954
  else
904
955
  0
905
956
  end
@@ -918,6 +969,50 @@ module Liquid
918
969
 
919
970
  attr_reader :context
920
971
 
972
+ def filter_array(input, property, target_value, &block)
973
+ ary = InputIterator.new(input, context)
974
+
975
+ return [] if ary.empty?
976
+
977
+ block.call(ary) do |item|
978
+ if target_value.nil?
979
+ fetch_property(item, property)
980
+ else
981
+ fetch_property(item, property) == target_value
982
+ end
983
+ rescue TypeError
984
+ raise_property_error(property)
985
+ rescue NoMethodError
986
+ return nil unless item.respond_to?(:[])
987
+ raise
988
+ end
989
+ end
990
+
991
+ def fetch_property(drop, property_or_keys)
992
+ ##
993
+ # This keeps backward compatibility by supporting properties containing
994
+ # dots. This is valid in Liquid syntax and used in some runtimes, such as
995
+ # Shopify with metafields.
996
+ #
997
+ # Using this approach, properties like 'price.value' can be accessed in
998
+ # both of the following examples:
999
+ #
1000
+ # ```
1001
+ # [
1002
+ # { 'name' => 'Item 1', 'price.price' => 40000 },
1003
+ # { 'name' => 'Item 2', 'price' => { 'value' => 39900 } }
1004
+ # ]
1005
+ # ```
1006
+ value = drop[property_or_keys]
1007
+
1008
+ return value if !value.nil? || !property_or_keys.is_a?(String)
1009
+
1010
+ keys = property_or_keys.split('.')
1011
+ keys.reduce(drop) do |drop, key|
1012
+ drop.respond_to?(:[]) ? drop[key] : drop
1013
+ end
1014
+ end
1015
+
921
1016
  def raise_property_error(property)
922
1017
  raise Liquid::ArgumentError, "cannot select the property '#{property}'"
923
1018
  end
@@ -968,7 +1063,18 @@ module Liquid
968
1063
  end
969
1064
 
970
1065
  def join(glue)
971
- to_a.join(glue.to_s)
1066
+ first = true
1067
+ output = +""
1068
+ each do |item|
1069
+ if first
1070
+ first = false
1071
+ else
1072
+ output << glue
1073
+ end
1074
+
1075
+ output << Liquid::Utils.to_s(item)
1076
+ end
1077
+ output
972
1078
  end
973
1079
 
974
1080
  def concat(args)
data/lib/liquid/utils.rb CHANGED
@@ -89,5 +89,101 @@ module Liquid
89
89
  # Otherwise return the object itself
90
90
  obj
91
91
  end
92
+
93
+ def self.to_s(obj, seen = {})
94
+ case obj
95
+ when Hash
96
+ # If the custom hash implementation overrides `#to_s`, use their
97
+ # custom implementation. Otherwise we use Liquid's default
98
+ # implementation.
99
+ if obj.class.instance_method(:to_s) == HASH_TO_S_METHOD
100
+ hash_inspect(obj, seen)
101
+ else
102
+ obj.to_s
103
+ end
104
+ when Array
105
+ array_inspect(obj, seen)
106
+ else
107
+ obj.to_s
108
+ end
109
+ end
110
+
111
+ def self.inspect(obj, seen = {})
112
+ case obj
113
+ when Hash
114
+ # If the custom hash implementation overrides `#inspect`, use their
115
+ # custom implementation. Otherwise we use Liquid's default
116
+ # implementation.
117
+ if obj.class.instance_method(:inspect) == HASH_INSPECT_METHOD
118
+ hash_inspect(obj, seen)
119
+ else
120
+ obj.inspect
121
+ end
122
+ when Array
123
+ array_inspect(obj, seen)
124
+ else
125
+ obj.inspect
126
+ end
127
+ end
128
+
129
+ def self.array_inspect(arr, seen = {})
130
+ if seen[arr.object_id]
131
+ return "[...]"
132
+ end
133
+
134
+ seen[arr.object_id] = true
135
+ str = +"["
136
+ cursor = 0
137
+ len = arr.length
138
+
139
+ while cursor < len
140
+ if cursor > 0
141
+ str << ", "
142
+ end
143
+
144
+ item_str = inspect(arr[cursor], seen)
145
+ str << item_str
146
+ cursor += 1
147
+ end
148
+
149
+ str << "]"
150
+ str
151
+ ensure
152
+ seen.delete(arr.object_id)
153
+ end
154
+
155
+ def self.hash_inspect(hash, seen = {})
156
+ if seen[hash.object_id]
157
+ return "{...}"
158
+ end
159
+ seen[hash.object_id] = true
160
+
161
+ str = +"{"
162
+ first = true
163
+ hash.each do |key, value|
164
+ if first
165
+ first = false
166
+ else
167
+ str << ", "
168
+ end
169
+
170
+ key_str = inspect(key, seen)
171
+ str << key_str
172
+ str << "=>"
173
+
174
+ value_str = inspect(value, seen)
175
+ str << value_str
176
+ end
177
+ str << "}"
178
+ str
179
+ ensure
180
+ seen.delete(hash.object_id)
181
+ end
182
+
183
+ HASH_TO_S_METHOD = Hash.instance_method(:to_s)
184
+ private_constant :HASH_TO_S_METHOD
185
+
186
+ HASH_INSPECT_METHOD = Hash.instance_method(:inspect)
187
+ private_constant :HASH_INSPECT_METHOD
92
188
  end
93
189
  end
@@ -107,8 +107,8 @@ module Liquid
107
107
  obj.each do |o|
108
108
  render_obj_to_output(o, output)
109
109
  end
110
- when
111
- output << obj.to_s
110
+ else
111
+ output << Liquid::Utils.to_s(obj)
112
112
  end
113
113
  end
114
114
 
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Liquid
5
- VERSION = "5.6.4"
5
+ VERSION = "5.7.0"
6
6
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: liquid
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.6.4
4
+ version: 5.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tobias Lütke
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-01-14 00:00:00.000000000 Z
10
+ date: 2025-01-17 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: strscan