liquid 5.10.0 → 5.12.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: 1b12ad70375f0ed9e3a2bab4a0a53ea565f3082af8d5bc06f4dea13a0a4ac574
4
- data.tar.gz: 04c2f7494b1ab0f97a2a33ad9d39c9c573a33b42bd109df8d42bb5e26d245f01
3
+ metadata.gz: ede4a234547978b59f059b4a2939bfb3c91092952c45ef8616f7874c2f9902e3
4
+ data.tar.gz: 4f489720f234a848498a390a76ba89e97bfa6b5d8c708ac9b6de293748ce6dd3
5
5
  SHA512:
6
- metadata.gz: dc94bd83ae20e716eabf8ab2ce9f1fe9f8ff45dee6e0acac91f9095c32557c94d08e5a3c1b473c268c5a6a6d63a4d4ca10170406f062545545f63c1980a4d392
7
- data.tar.gz: 0f19c1d79039fda7a6e485cf794cde252817dc43bd292351b7bc53d1adabd12919e18cc0870fcf3db3c0018e22ef91a1507ded76b27719fb30b79414e04d3d2f
6
+ metadata.gz: c0417cca5aece94bb341d38ba6e20deac2437fa453e926c296b6725858a35e845f25c548d1ddb364c675223667284ee088cf1fe76c151ef905b70cda08b977d3
7
+ data.tar.gz: ec169349a4b973bc11d5f52a2f4f20e7467eaecae1521cdec014b9e42f2de2cc955b1789f44e0249db95f9d8fc5d7659fed6897c699753f8f4111ce16c742b36
data/History.md CHANGED
@@ -1,13 +1,11 @@
1
1
  # Liquid Change Log
2
2
 
3
+ ## 5.11.0
4
+ * Revert the Inline Snippets tag (#2001), treat its inclusion in the latest Liquid release as a bug, and allow for feedback on RFC#1916 to better support Liquid developers [Guilherme Carreiro]
5
+ * Rename the `:rigid` error mode to `:strict2` and display a warning when users attempt to use the `:rigid` mode [Guilherme Carreiro]
6
+
3
7
  ## 5.10.0
4
8
  * Introduce support for Inline Snippets [Julia Boutin]
5
- ```
6
- {%- snippet snowdevil -%}
7
- Snowdevil
8
- {%- endsnippet -%}
9
- {% render snowdevil %}
10
- ```
11
9
 
12
10
  ## 5.9.0
13
11
  * Introduce `:rigid` error mode for stricter, safer parsing of all tags [CP Clermont, Guilherme Carreiro]
data/README.md CHANGED
@@ -103,10 +103,10 @@ Liquid also comes with different parsers that can be used when editing templates
103
103
  when templates are invalid. You can enable this new parser like this:
104
104
 
105
105
  ```ruby
106
- Liquid::Environment.default.error_mode = :rigid # Raises a SyntaxError when invalid syntax is used in all tags
107
- Liquid::Environment.default.error_mode = :strict # Raises a SyntaxError when invalid syntax is used in some tags
108
- Liquid::Environment.default.error_mode = :warn # Adds strict errors to template.errors but continues as normal
109
- Liquid::Environment.default.error_mode = :lax # The default mode, accepts almost anything.
106
+ Liquid::Environment.default.error_mode = :strict2 # Raises a SyntaxError when invalid syntax is used in all tags
107
+ Liquid::Environment.default.error_mode = :strict # Raises a SyntaxError when invalid syntax is used in some tags
108
+ Liquid::Environment.default.error_mode = :warn # Adds strict errors to template.errors but continues as normal
109
+ Liquid::Environment.default.error_mode = :lax # The default mode, accepts almost anything.
110
110
  ```
111
111
 
112
112
  If you want to set the error mode only on specific templates you can pass `:error_mode` as an option to `parse`:
@@ -113,22 +113,65 @@ module Liquid
113
113
 
114
114
  def equal_variables(left, right)
115
115
  if left.is_a?(MethodLiteral)
116
- if right.respond_to?(left.method_name)
117
- return right.send(left.method_name)
118
- else
119
- return nil
120
- end
116
+ return call_method_literal(left, right)
121
117
  end
122
118
 
123
119
  if right.is_a?(MethodLiteral)
124
- if left.respond_to?(right.method_name)
125
- return left.send(right.method_name)
120
+ return call_method_literal(right, left)
121
+ end
122
+
123
+ left == right
124
+ end
125
+
126
+ def call_method_literal(literal, value)
127
+ method_name = literal.method_name
128
+
129
+ # If the object responds to the method (e.g., ActiveSupport is loaded), use it
130
+ if value.respond_to?(method_name)
131
+ value.send(method_name)
132
+ else
133
+ # Emulate ActiveSupport's blank?/empty? to make Liquid invariant
134
+ # to whether ActiveSupport is loaded or not
135
+ case method_name
136
+ when :blank?
137
+ liquid_blank?(value)
138
+ when :empty?
139
+ liquid_empty?(value)
126
140
  else
127
- return nil
141
+ false
128
142
  end
129
143
  end
144
+ end
130
145
 
131
- left == right
146
+ # Implement blank? semantics matching ActiveSupport
147
+ # blank? returns true for nil, false, empty strings, whitespace-only strings,
148
+ # empty arrays, and empty hashes
149
+ def liquid_blank?(value)
150
+ case value
151
+ when NilClass, FalseClass
152
+ true
153
+ when TrueClass, Numeric
154
+ false
155
+ when String
156
+ # Blank if empty or whitespace only (matches ActiveSupport)
157
+ value.empty? || value.match?(/\A\s*\z/)
158
+ when Array, Hash
159
+ value.empty?
160
+ else
161
+ # Fall back to empty? if available, otherwise false
162
+ value.respond_to?(:empty?) ? value.empty? : false
163
+ end
164
+ end
165
+
166
+ # Implement empty? semantics
167
+ # Note: nil is NOT empty. empty? checks if a collection has zero elements.
168
+ def liquid_empty?(value)
169
+ case value
170
+ when String, Array, Hash
171
+ value.empty?
172
+ else
173
+ value.respond_to?(:empty?) ? value.empty? : false
174
+ end
132
175
  end
133
176
 
134
177
  def interpret_condition(left, right, op, context)
@@ -154,8 +197,8 @@ module Liquid
154
197
  end
155
198
 
156
199
  def deprecated_default_context
157
- warn("DEPRECATION WARNING: Condition#evaluate without a context argument is deprecated" \
158
- " and will be removed from Liquid 6.0.0.")
200
+ warn("DEPRECATION WARNING: Condition#evaluate without a context argument is deprecated " \
201
+ "and will be removed from Liquid 6.0.0.")
159
202
  Context.new
160
203
  end
161
204
 
@@ -184,7 +184,7 @@ module Liquid
184
184
  end
185
185
 
186
186
  def key?(key)
187
- self[key] != nil
187
+ find_variable(key, raise_on_not_found: false) != nil
188
188
  end
189
189
 
190
190
  def evaluate(object)
data/lib/liquid/drop.rb CHANGED
@@ -31,7 +31,7 @@ module Liquid
31
31
 
32
32
  # Catch all for the method
33
33
  def liquid_method_missing(method)
34
- return nil unless @context&.strict_variables
34
+ return unless @context&.strict_variables
35
35
  raise Liquid::UndefinedDropMethod, "undefined method #{method}"
36
36
  end
37
37
 
@@ -34,7 +34,7 @@ module Liquid
34
34
  # @param file_system The default file system that is used
35
35
  # to load templates from.
36
36
  # @param error_mode [Symbol] The default error mode for all templates
37
- # (either :rigid, :strict, :warn, or :lax).
37
+ # (either :strict2, :strict, :warn, or :lax).
38
38
  # @param exception_renderer [Proc] The exception renderer that is used to
39
39
  # render exceptions.
40
40
  # @yieldparam environment [Environment] The environment instance that is being built.
@@ -55,7 +55,7 @@ module Liquid
55
55
  end
56
56
 
57
57
  def inner_parse(markup, ss, cache)
58
- if (markup.start_with?("(") && markup.end_with?(")")) && markup =~ RANGES_REGEX
58
+ if markup.start_with?("(") && markup.end_with?(")") && markup =~ RANGES_REGEX
59
59
  return RangeLookup.parse(
60
60
  Regexp.last_match(1),
61
61
  Regexp.last_match(2),
data/lib/liquid/i18n.rb CHANGED
@@ -28,7 +28,7 @@ module Liquid
28
28
  def interpolate(name, vars)
29
29
  name.gsub(/%\{(\w+)\}/) do
30
30
  # raise TranslationError, "Undefined key #{$1} for interpolation in translation #{name}" unless vars[$1.to_sym]
31
- (vars[Regexp.last_match(1).to_sym]).to_s
31
+ vars[Regexp.last_match(1).to_sym].to_s
32
32
  end
33
33
  end
34
34
 
@@ -5,7 +5,6 @@
5
5
  block_tag_unexpected_args: "Syntax Error in '%{tag}' - Valid syntax: {% %{tag} %}{% end%{tag} %}"
6
6
  assign: "Syntax Error in 'assign' - Valid syntax: assign [var] = [source]"
7
7
  capture: "Syntax Error in 'capture' - Valid syntax: capture [var]"
8
- snippet: "Syntax Error in 'snippet' - Valid syntax: snippet [var]"
9
8
  case: "Syntax Error in 'case' - Valid syntax: case [condition]"
10
9
  case_invalid_when: "Syntax Error in tag 'case' - Valid when condition: {% when [condition] [or condition2...] %}"
11
10
  case_invalid_else: "Syntax Error in tag 'case' - Valid else condition: {% else %} (no parameters) "
@@ -20,7 +19,6 @@
20
19
  invalid_delimiter: "'%{tag}' is not a valid delimiter for %{block_name} tags. use %{block_delimiter}"
21
20
  invalid_template_encoding: "Invalid template encoding"
22
21
  render: "Syntax error in tag 'render' - Template name must be a quoted string"
23
- render_invalid_template_name: "Syntax error in tag 'render' - Expected a string or identifier, found %{found}"
24
22
  table_row: "Syntax Error in 'table_row loop' - Valid syntax: table_row [item] in [collection] cols=3"
25
23
  table_row_invalid_attribute: "Invalid attribute '%{attribute}' in tablerow loop. Valid attributes are cols, limit, offset, and range"
26
24
  tag_never_closed: "'%{block_name}' tag was never closed"
@@ -31,6 +29,5 @@
31
29
  variable_termination: "Variable '%{token}' was not properly terminated with regexp: %{tag_end}"
32
30
  argument:
33
31
  include: "Argument error in tag 'include' - Illegal template name"
34
- render: "Argument error in tag 'render' - Dynamically chosen templates are not allowed"
35
32
  disabled:
36
33
  tag: "usage is not allowed in this context"
@@ -55,15 +55,15 @@ module Liquid
55
55
  end
56
56
 
57
57
  def parse_expression(markup, safe: false)
58
- if !safe && @error_mode == :rigid
58
+ if !safe && @error_mode == :strict2
59
59
  # parse_expression is a widely used API. To maintain backward
60
- # compatibility while raising awareness about rigid parser standards,
60
+ # compatibility while raising awareness about strict2 parser standards,
61
61
  # the safe flag supports API users make a deliberate decision.
62
62
  #
63
- # In rigid mode, markup MUST come from a string returned by the parser
63
+ # In strict2 mode, markup MUST come from a string returned by the parser
64
64
  # (e.g., parser.expression). We're not calling the parser here to
65
65
  # prevent redundant parser overhead.
66
- raise Liquid::InternalError, "unsafe parse_expression cannot be used in rigid mode"
66
+ raise Liquid::InternalError, "unsafe parse_expression cannot be used in strict2 mode"
67
67
  end
68
68
 
69
69
  Expression.parse(markup, @string_scanner, @expression_cache)
@@ -7,16 +7,19 @@ module Liquid
7
7
  # It's basically doing the same thing the {#parse_with_selected_parser},
8
8
  # except this will try the strict parser regardless of the error mode,
9
9
  # and fall back to the lax parser if the error mode is lax or warn,
10
- # except when in rigid mode where it uses the rigid parser.
10
+ # except when in strict2 mode where it uses the strict2 parser.
11
11
  #
12
12
  # @deprecated Use {#parse_with_selected_parser} instead.
13
13
  def strict_parse_with_error_mode_fallback(markup)
14
- return rigid_parse_with_error_context(markup) if rigid_mode?
14
+ return strict2_parse_with_error_context(markup) if strict2_mode?
15
15
 
16
16
  strict_parse_with_error_context(markup)
17
17
  rescue SyntaxError => e
18
18
  case parse_context.error_mode
19
19
  when :rigid
20
+ rigid_warn
21
+ raise
22
+ when :strict2
20
23
  raise
21
24
  when :strict
22
25
  raise
@@ -28,12 +31,13 @@ module Liquid
28
31
 
29
32
  def parse_with_selected_parser(markup)
30
33
  case parse_context.error_mode
31
- when :rigid then rigid_parse_with_error_context(markup)
32
- when :strict then strict_parse_with_error_context(markup)
33
- when :lax then lax_parse(markup)
34
+ when :rigid then rigid_warn && strict2_parse_with_error_context(markup)
35
+ when :strict2 then strict2_parse_with_error_context(markup)
36
+ when :strict then strict_parse_with_error_context(markup)
37
+ when :lax then lax_parse(markup)
34
38
  when :warn
35
39
  begin
36
- rigid_parse_with_error_context(markup)
40
+ strict2_parse_with_error_context(markup)
37
41
  rescue SyntaxError => e
38
42
  parse_context.warnings << e
39
43
  lax_parse(markup)
@@ -41,14 +45,18 @@ module Liquid
41
45
  end
42
46
  end
43
47
 
44
- def rigid_mode?
45
- parse_context.error_mode == :rigid
48
+ def strict2_mode?
49
+ parse_context.error_mode == :strict2 || parse_context.error_mode == :rigid
46
50
  end
47
51
 
48
52
  private
49
53
 
50
- def rigid_parse_with_error_context(markup)
51
- rigid_parse(markup)
54
+ def rigid_warn
55
+ Deprecations.warn(':rigid', ':strict2')
56
+ end
57
+
58
+ def strict2_parse_with_error_context(markup)
59
+ strict2_parse(markup)
52
60
  rescue SyntaxError => e
53
61
  e.line_number = line_number
54
62
  e.markup_context = markup_context(markup)
@@ -2,24 +2,39 @@
2
2
 
3
3
  module Liquid
4
4
  class ResourceLimits
5
- attr_accessor :render_length_limit, :render_score_limit, :assign_score_limit
6
- attr_reader :render_score, :assign_score
5
+ attr_accessor :render_length_limit,
6
+ :render_score_limit,
7
+ :assign_score_limit,
8
+ :cumulative_render_score_limit,
9
+ :cumulative_assign_score_limit
10
+ attr_reader :render_score,
11
+ :assign_score,
12
+ :cumulative_render_score,
13
+ :cumulative_assign_score
7
14
 
8
15
  def initialize(limits)
9
- @render_length_limit = limits[:render_length_limit]
10
- @render_score_limit = limits[:render_score_limit]
11
- @assign_score_limit = limits[:assign_score_limit]
16
+ @render_length_limit = limits[:render_length_limit]
17
+ @render_score_limit = limits[:render_score_limit]
18
+ @assign_score_limit = limits[:assign_score_limit]
19
+ @cumulative_render_score_limit = limits[:cumulative_render_score_limit]
20
+ @cumulative_assign_score_limit = limits[:cumulative_assign_score_limit]
21
+ @cumulative_render_score = 0
22
+ @cumulative_assign_score = 0
12
23
  reset
13
24
  end
14
25
 
15
26
  def increment_render_score(amount)
16
27
  @render_score += amount
28
+ @cumulative_render_score += amount
17
29
  raise_limits_reached if @render_score_limit && @render_score > @render_score_limit
30
+ raise_limits_reached if @cumulative_render_score_limit && @cumulative_render_score > @cumulative_render_score_limit
18
31
  end
19
32
 
20
33
  def increment_assign_score(amount)
21
34
  @assign_score += amount
35
+ @cumulative_assign_score += amount
22
36
  raise_limits_reached if @assign_score_limit && @assign_score > @assign_score_limit
37
+ raise_limits_reached if @cumulative_assign_score_limit && @cumulative_assign_score > @cumulative_assign_score_limit
23
38
  end
24
39
 
25
40
  # update either render_length or assign_score based on whether or not the writes are captured
@@ -47,6 +62,8 @@ module Liquid
47
62
  @reached_limit = false
48
63
  @last_capture_length = nil
49
64
  @render_score = @assign_score = 0
65
+ raise_limits_reached if @cumulative_render_score_limit && @cumulative_render_score > @cumulative_render_score_limit
66
+ raise_limits_reached if @cumulative_assign_score_limit && @cumulative_assign_score > @cumulative_assign_score_limit
50
67
  end
51
68
 
52
69
  def with_capture
@@ -293,6 +293,19 @@ module Liquid
293
293
  input.split(pattern)
294
294
  end
295
295
 
296
+ # @liquid_public_docs
297
+ # @liquid_type filter
298
+ # @liquid_category string
299
+ # @liquid_summary
300
+ # Removes leading and trailing whitespace and collapses consecutive whitespace to a single space.
301
+ # @liquid_syntax string | squish
302
+ # @liquid_return [string]
303
+ def squish(input)
304
+ return if input.nil?
305
+
306
+ Utils.to_s(input).strip.gsub(/\s+/, ' ')
307
+ end
308
+
296
309
  # @liquid_public_docs
297
310
  # @liquid_type filter
298
311
  # @liquid_category string
@@ -768,6 +781,8 @@ module Liquid
768
781
  # @liquid_syntax array | first
769
782
  # @liquid_return [untyped]
770
783
  def first(array)
784
+ # ActiveSupport returns "" for empty strings, not nil
785
+ return array[0] || "" if array.is_a?(String)
771
786
  array.first if array.respond_to?(:first)
772
787
  end
773
788
 
@@ -779,6 +794,8 @@ module Liquid
779
794
  # @liquid_syntax array | last
780
795
  # @liquid_return [untyped]
781
796
  def last(array)
797
+ # ActiveSupport returns "" for empty strings, not nil
798
+ return array[-1] || "" if array.is_a?(String)
782
799
  array.last if array.respond_to?(:last)
783
800
  end
784
801
 
@@ -86,7 +86,7 @@ module Liquid
86
86
 
87
87
  private
88
88
 
89
- def rigid_parse(markup)
89
+ def strict2_parse(markup)
90
90
  parser = @parse_context.new_parser(markup)
91
91
  @left = safe_parse_expression(parser)
92
92
  parser.consume(:end_of_string)
@@ -107,18 +107,18 @@ module Liquid
107
107
  def record_when_condition(markup)
108
108
  body = new_body
109
109
 
110
- if rigid_mode?
111
- parse_rigid_when(markup, body)
110
+ if strict2_mode?
111
+ parse_strict2_when(markup, body)
112
112
  else
113
113
  parse_lax_when(markup, body)
114
114
  end
115
115
  end
116
116
 
117
- def parse_rigid_when(markup, body)
117
+ def parse_strict2_when(markup, body)
118
118
  parser = @parse_context.new_parser(markup)
119
119
 
120
120
  loop do
121
- expr = safe_parse_expression(parser)
121
+ expr = Condition.parse_expression(parse_context, parser.expression, safe: true)
122
122
  block = Condition.new(@left, '==', expr)
123
123
  block.attach(body)
124
124
  @blocks << block
@@ -56,7 +56,7 @@ module Liquid
56
56
  private
57
57
 
58
58
  # cycle [name:] expression(, expression)*
59
- def rigid_parse(markup)
59
+ def strict2_parse(markup)
60
60
  p = @parse_context.new_parser(markup)
61
61
 
62
62
  @variables = []
@@ -111,7 +111,7 @@ module Liquid
111
111
 
112
112
  private
113
113
 
114
- def rigid_parse(markup)
114
+ def strict2_parse(markup)
115
115
  strict_parse(markup)
116
116
  end
117
117
 
@@ -66,7 +66,7 @@ module Liquid
66
66
 
67
67
  private
68
68
 
69
- def rigid_parse(markup)
69
+ def strict2_parse(markup)
70
70
  strict_parse(markup)
71
71
  end
72
72
 
@@ -84,7 +84,7 @@ module Liquid
84
84
  alias_method :parse_context, :options
85
85
  private :parse_context
86
86
 
87
- def rigid_parse(markup)
87
+ def strict2_parse(markup)
88
88
  p = @parse_context.new_parser(markup)
89
89
 
90
90
  @template_name_expr = safe_parse_expression(p)
@@ -27,7 +27,7 @@ module Liquid
27
27
  # @liquid_syntax_keyword filename The name of the snippet to render, without the `.liquid` extension.
28
28
  class Render < Tag
29
29
  FOR = 'for'
30
- SYNTAX = /(#{QuotedString}+|#{VariableSegment}+)(\s+(with|#{FOR})\s+(#{QuotedFragment}+))?(\s+(?:as)\s+(#{VariableSegment}+))?/o
30
+ SYNTAX = /(#{QuotedString}+)(\s+(with|#{FOR})\s+(#{QuotedFragment}+))?(\s+(?:as)\s+(#{VariableSegment}+))?/o
31
31
 
32
32
  disable_tags "include"
33
33
 
@@ -47,23 +47,21 @@ module Liquid
47
47
  end
48
48
 
49
49
  def render_tag(context, output)
50
- template = context.evaluate(@template_name_expr)
51
-
52
- if template.respond_to?(:to_partial)
53
- partial = template.to_partial
54
- template_name = template.filename
55
- context_variable_name = @alias_name || template.name
56
- elsif @template_name_expr.is_a?(String)
57
- partial = PartialCache.load(template, context: context, parse_context: parse_context)
58
- template_name = partial.name
59
- context_variable_name = @alias_name || template_name.split('/').last
60
- else
61
- raise ::ArgumentError
62
- end
50
+ # The expression should be a String literal, which parses to a String object
51
+ template_name = @template_name_expr
52
+ raise ::ArgumentError unless template_name.is_a?(String)
53
+
54
+ partial = PartialCache.load(
55
+ template_name,
56
+ context: context,
57
+ parse_context: parse_context,
58
+ )
59
+
60
+ context_variable_name = @alias_name || template_name.split('/').last
63
61
 
64
62
  render_partial_func = ->(var, forloop) {
65
63
  inner_context = context.new_isolated_subcontext
66
- inner_context.template_name = template_name
64
+ inner_context.template_name = partial.name
67
65
  inner_context.partial = true
68
66
  inner_context['forloop'] = forloop if forloop
69
67
 
@@ -87,10 +85,10 @@ module Liquid
87
85
  end
88
86
 
89
87
  # render (string) (with|for expression)? (as id)? (key: value)*
90
- def rigid_parse(markup)
88
+ def strict2_parse(markup)
91
89
  p = @parse_context.new_parser(markup)
92
90
 
93
- @template_name_expr = parse_expression(rigid_template_name(p), safe: true)
91
+ @template_name_expr = parse_expression(strict2_template_name(p), safe: true)
94
92
  with_or_for = p.id?("for") || p.id?("with")
95
93
  @variable_name_expr = safe_parse_expression(p) if with_or_for
96
94
  @alias_name = p.consume(:id) if p.id?("as")
@@ -103,18 +101,14 @@ module Liquid
103
101
  key = p.consume
104
102
  p.consume(:colon)
105
103
  @attributes[key] = safe_parse_expression(p)
106
- p.consume?(:comma) # optional comma
104
+ p.consume?(:comma)
107
105
  end
108
106
 
109
107
  p.consume(:end_of_string)
110
108
  end
111
109
 
112
- def rigid_template_name(p)
113
- return p.consume(:string) if p.look(:string)
114
- return p.consume(:id) if p.look(:id)
115
-
116
- found = p.consume || "nothing"
117
- raise SyntaxError, options[:locale].t("errors.syntax.render_invalid_template_name", found: found)
110
+ def strict2_template_name(p)
111
+ p.consume(:string)
118
112
  end
119
113
 
120
114
  def strict_parse(markup)
@@ -34,7 +34,7 @@ module Liquid
34
34
  parse_with_selected_parser(markup)
35
35
  end
36
36
 
37
- def rigid_parse(markup)
37
+ def strict2_parse(markup)
38
38
  p = @parse_context.new_parser(markup)
39
39
 
40
40
  @variable_name = p.consume(:id)
data/lib/liquid/tags.rb CHANGED
@@ -20,7 +20,6 @@ require_relative "tags/raw"
20
20
  require_relative "tags/render"
21
21
  require_relative "tags/cycle"
22
22
  require_relative "tags/doc"
23
- require_relative "tags/snippet"
24
23
 
25
24
  module Liquid
26
25
  module Tags
@@ -45,7 +44,6 @@ module Liquid
45
44
  'echo' => Echo,
46
45
  'tablerow' => TableRow,
47
46
  'doc' => Doc,
48
- 'snippet' => Snippet,
49
47
  }.freeze
50
48
  end
51
49
  end
@@ -25,7 +25,7 @@ module Liquid
25
25
  # :lax acts like liquid 2.5 and silently ignores malformed tags in most cases.
26
26
  # :warn is the default and will give deprecation warnings when invalid syntax is used.
27
27
  # :strict enforces correct syntax for most tags
28
- # :rigid enforces correct syntax for all tags
28
+ # :strict2 enforces correct syntax for all tags
29
29
  def error_mode=(mode)
30
30
  Deprecations.warn("Template.error_mode=", "Environment#error_mode=")
31
31
  Environment.default.error_mode = mode
@@ -117,7 +117,7 @@ module Liquid
117
117
  byte_a = byte_b = @ss.scan_byte
118
118
 
119
119
  while byte_b
120
- byte_a = @ss.scan_byte while byte_a && (byte_a != CLOSE_CURLEY && byte_a != OPEN_CURLEY)
120
+ byte_a = @ss.scan_byte while byte_a && byte_a != CLOSE_CURLEY && byte_a != OPEN_CURLEY
121
121
 
122
122
  break unless byte_a
123
123
 
data/lib/liquid/utils.rb CHANGED
@@ -69,7 +69,7 @@ module Liquid
69
69
  return obj if obj.respond_to?(:strftime)
70
70
 
71
71
  if obj.is_a?(String)
72
- return nil if obj.empty?
72
+ return if obj.empty?
73
73
  obj = obj.downcase
74
74
  end
75
75
 
@@ -95,6 +95,8 @@ module Liquid
95
95
 
96
96
  def self.to_s(obj, seen = {})
97
97
  case obj
98
+ when BigDecimal
99
+ obj.to_s("F")
98
100
  when Hash
99
101
  # If the custom hash implementation overrides `#to_s`, use their
100
102
  # custom implementation. Otherwise we use Liquid's default
@@ -74,14 +74,14 @@ module Liquid
74
74
  p.consume(:end_of_string)
75
75
  end
76
76
 
77
- def rigid_parse(markup)
77
+ def strict2_parse(markup)
78
78
  @filters = []
79
79
  p = @parse_context.new_parser(markup)
80
80
 
81
81
  return if p.look(:end_of_string)
82
82
 
83
83
  @name = parse_context.safe_parse_expression(p)
84
- @filters << rigid_parse_filter_expressions(p) while p.consume?(:pipe)
84
+ @filters << strict2_parse_filter_expressions(p) while p.consume?(:pipe)
85
85
  p.consume(:end_of_string)
86
86
  end
87
87
 
@@ -156,7 +156,7 @@ module Liquid
156
156
  # argument = (positional_argument | keyword_argument)
157
157
  # positional_argument = expression
158
158
  # keyword_argument = id ":" expression
159
- def rigid_parse_filter_expressions(p)
159
+ def strict2_parse_filter_expressions(p)
160
160
  filtername = p.consume(:id)
161
161
  filter_args = []
162
162
  keyword_args = {}
@@ -70,6 +70,11 @@ module Liquid
70
70
  elsif lookup_command?(i) && object.respond_to?(key)
71
71
  object = object.send(key).to_liquid
72
72
 
73
+ # Handle string first/last like ActiveSupport does (returns first/last character)
74
+ # ActiveSupport returns "" for empty strings, not nil
75
+ elsif lookup_command?(i) && object.is_a?(String) && (key == "first" || key == "last")
76
+ object = key == "first" ? (object[0] || "") : (object[-1] || "")
77
+
73
78
  # No key was present with the desired value and it wasn't one of the directly supported
74
79
  # keywords either. The only thing we got left is to return nil or
75
80
  # raise an exception if `strict_variables` option is set to true
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Liquid
5
- VERSION = "5.10.0"
5
+ VERSION = "5.12.0"
6
6
  end
data/lib/liquid.rb CHANGED
@@ -67,7 +67,6 @@ require 'liquid/i18n'
67
67
  require 'liquid/drop'
68
68
  require 'liquid/tablerowloop_drop'
69
69
  require 'liquid/forloop_drop'
70
- require 'liquid/snippet_drop'
71
70
  require 'liquid/extensions'
72
71
  require 'liquid/errors'
73
72
  require 'liquid/interrupts'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: liquid
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.10.0
4
+ version: 5.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tobias Lütke
@@ -105,7 +105,6 @@ files:
105
105
  - lib/liquid/range_lookup.rb
106
106
  - lib/liquid/registers.rb
107
107
  - lib/liquid/resource_limits.rb
108
- - lib/liquid/snippet_drop.rb
109
108
  - lib/liquid/standardfilters.rb
110
109
  - lib/liquid/strainer_template.rb
111
110
  - lib/liquid/tablerowloop_drop.rb
@@ -131,7 +130,6 @@ files:
131
130
  - lib/liquid/tags/inline_comment.rb
132
131
  - lib/liquid/tags/raw.rb
133
132
  - lib/liquid/tags/render.rb
134
- - lib/liquid/tags/snippet.rb
135
133
  - lib/liquid/tags/table_row.rb
136
134
  - lib/liquid/tags/unless.rb
137
135
  - lib/liquid/template.rb
@@ -161,7 +159,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
161
159
  - !ruby/object:Gem::Version
162
160
  version: 1.3.7
163
161
  requirements: []
164
- rubygems_version: 3.7.2
162
+ rubygems_version: 4.0.8
165
163
  specification_version: 4
166
164
  summary: A secure, non-evaling end user template engine with aesthetic markup.
167
165
  test_files: []
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Liquid
4
- class SnippetDrop < Drop
5
- attr_reader :body, :name, :filename
6
-
7
- def initialize(body, name, filename)
8
- super()
9
- @body = body
10
- @name = name
11
- @filename = filename
12
- end
13
-
14
- def to_partial
15
- @body
16
- end
17
-
18
- def to_s
19
- 'SnippetDrop'
20
- end
21
- end
22
- end
@@ -1,45 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Liquid
4
- # @liquid_public_docs
5
- # @liquid_type tag
6
- # @liquid_category variable
7
- # @liquid_name snippet
8
- # @liquid_summary
9
- # Creates a new inline snippet.
10
- # @liquid_description
11
- # You can create inline snippets to make your Liquid code more modular.
12
- # @liquid_syntax
13
- # {% snippet snippet_name %}
14
- # value
15
- # {% endsnippet %}
16
- class Snippet < Block
17
- def initialize(tag_name, markup, options)
18
- super
19
- p = @parse_context.new_parser(markup)
20
- if p.look(:id)
21
- @to = p.consume(:id)
22
- p.consume(:end_of_string)
23
- else
24
- raise SyntaxError, options[:locale].t("errors.syntax.snippet")
25
- end
26
- end
27
-
28
- def render_to_output_buffer(context, output)
29
- snippet_drop = SnippetDrop.new(@body, @to, context.template_name)
30
- context.scopes.last[@to] = snippet_drop
31
- context.resource_limits.increment_assign_score(assign_score_of(snippet_drop))
32
- output
33
- end
34
-
35
- def blank?
36
- true
37
- end
38
-
39
- private
40
-
41
- def assign_score_of(snippet_drop)
42
- snippet_drop.body.nodelist.sum { |node| node.to_s.bytesize }
43
- end
44
- end
45
- end