liquid 5.7.1 → 5.8.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: 771dc6f35b1b6c881e98289584bbcf8f0b0a2c494ddf1b6bf14bda94aa6ef991
4
- data.tar.gz: 5de0ba09a9e281fd713480edafb79dc5580f3ef1233a6f7c27d45897649a2fa6
3
+ metadata.gz: d27b4c9417e479c5eec775f4ec1e5dd6165a2fb5fe76a8b769b31bc5acaff334
4
+ data.tar.gz: 77aef473425cf804dd4dfe1a17f00a424e70e30143952c9670c20d947fa038aa
5
5
  SHA512:
6
- metadata.gz: '09c9b6247637be3d1a7f9b9bdaff3351d72d3215d2cd7eaaf213d97eaca99ba5faef92bc89165c13f1b34fa3742a3de4e26fddba0d6390587e7a640d3af54ae3'
7
- data.tar.gz: 1afb6aca240c13c8ca09bb5079094a69e98bf48d83a1016ee261c34862dadd35e1bf8d64c906f12ecba3c616656f33f3576a3237eddc0d2ec4e97fd0e10bd43f
6
+ metadata.gz: 40f39337b8767db07e12beb2aa2a155a3bfb975752e02a09626b7fe3d53ef73c0c79252f29f2cb4a6edc63b8f0afff06d0df663d9a74d27a1f9507efd5db4a2c
7
+ data.tar.gz: 350ee158bdd4dabfb8781c937ca6426ee06cbaaedaffa469aa324fe717d7b7fbbf1f7a82aebd61a0bd9f96bc0ae4a94e887115385d1ca1f9c76aa2613ef88881
data/History.md CHANGED
@@ -1,18 +1,30 @@
1
1
  # Liquid Change Log
2
2
 
3
- ## 5.8.0 (unreleased)
3
+ ## 5.8.1 (unreleased)
4
4
 
5
- ## 5.7.1 2025-01-23
5
+ ## 5.8.0
6
6
 
7
- * Fix the `find` and `find_index`filters to return `nil` when filtering empty arrays
8
- * Fix the `has` filter to return `false` when filtering empty arrays
7
+ * Introduce the new `{% doc %}` tag [Guilherme Carreiro]
8
+
9
+ ## 5.7.3
10
+
11
+ * Raise Liquid::SyntaxError when parsing invalidly encoded strings [Chris AtLee]
12
+
13
+ ## 5.7.2 2025-01-31
14
+
15
+ * Fix array filters to not support nested properties [Guilherme Carreiro]
16
+
17
+ ## 5.7.1 2025-01-24
18
+
19
+ * Fix the `find` and `find_index`filters to return `nil` when filtering empty arrays [Guilherme Carreiro]
20
+ * Fix the `has` filter to return `false` when filtering empty arrays [Guilherme Carreiro]
9
21
 
10
22
  ## 5.7.0 2025-01-16
11
23
 
12
24
  ### Features
13
25
 
14
- * Add `find`, `find_index`, `has`, and `reject` filters to arrays
15
- * Compatibility with Ruby 3.4
26
+ * Add `find`, `find_index`, `has`, and `reject` filters to arrays [Guilherme Carreiro]
27
+ * Compatibility with Ruby 3.4 [Ian Ker-Seymer]
16
28
 
17
29
  ## 5.6.4 2025-01-14
18
30
 
data/lib/liquid/lexer.rb CHANGED
@@ -161,6 +161,12 @@ module Liquid
161
161
  end
162
162
  # rubocop:enable Metrics/BlockNesting
163
163
  output << EOS
164
+ rescue ::ArgumentError => e
165
+ if e.message == "invalid byte sequence in #{ss.string.encoding}"
166
+ raise SyntaxError, "Invalid byte sequence in #{ss.string.encoding}"
167
+ else
168
+ raise
169
+ end
164
170
  end
165
171
 
166
172
  def raise_syntax_error(start_pos, ss)
@@ -2,12 +2,14 @@
2
2
  errors:
3
3
  syntax:
4
4
  tag_unexpected_args: "Syntax Error in '%{tag}' - Valid syntax: %{tag}"
5
+ block_tag_unexpected_args: "Syntax Error in '%{tag}' - Valid syntax: {% %{tag} %}{% end%{tag} %}"
5
6
  assign: "Syntax Error in 'assign' - Valid syntax: assign [var] = [source]"
6
7
  capture: "Syntax Error in 'capture' - Valid syntax: capture [var]"
7
8
  case: "Syntax Error in 'case' - Valid syntax: case [condition]"
8
9
  case_invalid_when: "Syntax Error in tag 'case' - Valid when condition: {% when [condition] [or condition2...] %}"
9
10
  case_invalid_else: "Syntax Error in tag 'case' - Valid else condition: {% else %} (no parameters) "
10
11
  cycle: "Syntax Error in 'cycle' - Valid syntax: cycle [name :] var [, var2, var3 ...]"
12
+ doc_invalid_nested: "Syntax Error in 'doc' - Nested doc tags are not allowed"
11
13
  for: "Syntax Error in 'for loop' - Valid syntax: for [item] in [collection]"
12
14
  for_invalid_in: "For loops require an 'in' clause"
13
15
  for_invalid_attribute: "Invalid attribute in for loop. Valid attributes are limit and offset"
@@ -387,7 +387,7 @@ module Liquid
387
387
  end
388
388
  elsif ary.all? { |el| el.respond_to?(:[]) }
389
389
  begin
390
- ary.sort { |a, b| nil_safe_compare(fetch_property(a, property), fetch_property(b, property)) }
390
+ ary.sort { |a, b| nil_safe_compare(a[property], b[property]) }
391
391
  rescue TypeError
392
392
  raise_property_error(property)
393
393
  end
@@ -416,7 +416,7 @@ module Liquid
416
416
  end
417
417
  elsif ary.all? { |el| el.respond_to?(:[]) }
418
418
  begin
419
- ary.sort { |a, b| nil_safe_casecmp(fetch_property(a, property), fetch_property(b, property)) }
419
+ ary.sort { |a, b| nil_safe_casecmp(a[property], b[property]) }
420
420
  rescue TypeError
421
421
  raise_property_error(property)
422
422
  end
@@ -456,7 +456,7 @@ module Liquid
456
456
  # Tests if any item in an array has a specific property value.
457
457
  # @liquid_description
458
458
  # This requires you to provide both the property name and the associated value.
459
- # @liquid_syntax array | some: string, string
459
+ # @liquid_syntax array | has: string, string
460
460
  # @liquid_return [boolean]
461
461
  def has(input, property, target_value = nil)
462
462
  filter_array(input, property, target_value, false) { |ary, &block| ary.any?(&block) }
@@ -504,7 +504,7 @@ module Liquid
504
504
  []
505
505
  else
506
506
  ary.uniq do |item|
507
- fetch_property(item, property)
507
+ item[property]
508
508
  rescue TypeError
509
509
  raise_property_error(property)
510
510
  rescue NoMethodError
@@ -540,7 +540,7 @@ module Liquid
540
540
  if property == "to_liquid"
541
541
  e
542
542
  elsif e.respond_to?(:[])
543
- r = fetch_property(e, property)
543
+ r = e[property]
544
544
  r.is_a?(Proc) ? r.call : r
545
545
  end
546
546
  end
@@ -564,7 +564,7 @@ module Liquid
564
564
  []
565
565
  else
566
566
  ary.reject do |item|
567
- fetch_property(item, property).nil?
567
+ item[property].nil?
568
568
  rescue TypeError
569
569
  raise_property_error(property)
570
570
  rescue NoMethodError
@@ -950,7 +950,7 @@ module Liquid
950
950
  if property.nil?
951
951
  item
952
952
  elsif item.respond_to?(:[])
953
- fetch_property(item, property)
953
+ item[property]
954
954
  else
955
955
  0
956
956
  end
@@ -976,9 +976,9 @@ module Liquid
976
976
 
977
977
  block.call(ary) do |item|
978
978
  if target_value.nil?
979
- fetch_property(item, property)
979
+ item[property]
980
980
  else
981
- fetch_property(item, property) == target_value
981
+ item[property] == target_value
982
982
  end
983
983
  rescue TypeError
984
984
  raise_property_error(property)
@@ -988,31 +988,6 @@ module Liquid
988
988
  end
989
989
  end
990
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
-
1016
991
  def raise_property_error(property)
1017
992
  raise Liquid::ArgumentError, "cannot select the property '#{property}'"
1018
993
  end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Liquid
4
+ # @liquid_public_docs
5
+ # @liquid_type tag
6
+ # @liquid_category syntax
7
+ # @liquid_name doc
8
+ # @liquid_summary
9
+ # Documents template elements with annotations.
10
+ # @liquid_description
11
+ # The `doc` tag allows developers to include documentation within Liquid
12
+ # templates. Any content inside `doc` tags is not rendered or outputted.
13
+ # Liquid code inside will be parsed but not executed. This facilitates
14
+ # tooling support for features like code completion, linting, and inline
15
+ # documentation.
16
+ # @liquid_syntax
17
+ # {% doc %}
18
+ # Renders a message.
19
+ #
20
+ # @param {string} foo - A foo value.
21
+ # @param {string} [bar] - An optional bar value.
22
+ #
23
+ # @example
24
+ # {% render 'message', foo: 'Hello', bar: 'World' %}
25
+ # {% enddoc %}
26
+ # {{ foo }}, {{ bar }}!
27
+ class Doc < Block
28
+ NO_UNEXPECTED_ARGS = /\A\s*\z/
29
+
30
+ def initialize(tag_name, markup, parse_context)
31
+ super
32
+ ensure_valid_markup(tag_name, markup, parse_context)
33
+ end
34
+
35
+ def parse(tokens)
36
+ while (token = tokens.shift)
37
+ tag_name = token =~ BlockBody::FullTokenPossiblyInvalid && Regexp.last_match(2)
38
+
39
+ raise_nested_doc_error if tag_name == @tag_name
40
+
41
+ if tag_name == block_delimiter
42
+ parse_context.trim_whitespace = (token[-3] == WhitespaceControl)
43
+ return
44
+ end
45
+ end
46
+
47
+ raise_tag_never_closed(block_name)
48
+ end
49
+
50
+ def render_to_output_buffer(_context, output)
51
+ output
52
+ end
53
+
54
+ def blank?
55
+ true
56
+ end
57
+
58
+ private
59
+
60
+ def ensure_valid_markup(tag_name, markup, parse_context)
61
+ unless NO_UNEXPECTED_ARGS.match?(markup)
62
+ raise SyntaxError, parse_context.locale.t("errors.syntax.block_tag_unexpected_args", tag: tag_name)
63
+ end
64
+ end
65
+
66
+ def raise_nested_doc_error
67
+ raise SyntaxError, parse_context.locale.t("errors.syntax.doc_invalid_nested")
68
+ end
69
+ end
70
+ end
data/lib/liquid/tags.rb CHANGED
@@ -19,6 +19,7 @@ require_relative "tags/comment"
19
19
  require_relative "tags/raw"
20
20
  require_relative "tags/render"
21
21
  require_relative "tags/cycle"
22
+ require_relative "tags/doc"
22
23
 
23
24
  module Liquid
24
25
  module Tags
@@ -42,6 +43,7 @@ module Liquid
42
43
  'if' => If,
43
44
  'echo' => Echo,
44
45
  'tablerow' => TableRow,
46
+ 'doc' => Doc,
45
47
  }.freeze
46
48
  end
47
49
  end
@@ -103,6 +103,12 @@ module Liquid
103
103
 
104
104
  pos = @ss.pos -= 2
105
105
  @source.byteslice(start, pos - start)
106
+ rescue ::ArgumentError => e
107
+ if e.message == "invalid byte sequence in #{@ss.string.encoding}"
108
+ raise SyntaxError, "Invalid byte sequence in #{@ss.string.encoding}"
109
+ else
110
+ raise
111
+ end
106
112
  end
107
113
 
108
114
  def next_variable_token
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Liquid
5
- VERSION = "5.7.1"
5
+ VERSION = "5.8.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.7.1
4
+ version: 5.8.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-24 00:00:00.000000000 Z
10
+ date: 2025-02-25 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: strscan
@@ -120,6 +120,7 @@ files:
120
120
  - lib/liquid/tags/continue.rb
121
121
  - lib/liquid/tags/cycle.rb
122
122
  - lib/liquid/tags/decrement.rb
123
+ - lib/liquid/tags/doc.rb
123
124
  - lib/liquid/tags/echo.rb
124
125
  - lib/liquid/tags/for.rb
125
126
  - lib/liquid/tags/if.rb
@@ -158,7 +159,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
158
159
  - !ruby/object:Gem::Version
159
160
  version: 1.3.7
160
161
  requirements: []
161
- rubygems_version: 3.6.2
162
+ rubygems_version: 3.6.3
162
163
  specification_version: 4
163
164
  summary: A secure, non-evaling end user template engine with aesthetic markup.
164
165
  test_files: []