liquid 5.4.0 → 5.6.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +11 -0
  3. data/README.md +48 -6
  4. data/lib/liquid/block.rb +8 -4
  5. data/lib/liquid/block_body.rb +28 -10
  6. data/lib/liquid/condition.rb +9 -4
  7. data/lib/liquid/const.rb +8 -0
  8. data/lib/liquid/context.rb +24 -14
  9. data/lib/liquid/deprecations.rb +22 -0
  10. data/lib/liquid/drop.rb +4 -0
  11. data/lib/liquid/environment.rb +159 -0
  12. data/lib/liquid/errors.rb +16 -15
  13. data/lib/liquid/expression.rb +101 -22
  14. data/lib/liquid/forloop_drop.rb +2 -5
  15. data/lib/liquid/lexer.rb +155 -44
  16. data/lib/liquid/locales/en.yml +1 -0
  17. data/lib/liquid/parse_context.rb +29 -6
  18. data/lib/liquid/parse_tree_visitor.rb +1 -1
  19. data/lib/liquid/parser.rb +3 -3
  20. data/lib/liquid/partial_cache.rb +12 -3
  21. data/lib/liquid/range_lookup.rb +14 -4
  22. data/lib/liquid/standardfilters.rb +82 -21
  23. data/lib/liquid/tablerowloop_drop.rb +1 -1
  24. data/lib/liquid/tag/disabler.rb +0 -8
  25. data/lib/liquid/tag.rb +13 -3
  26. data/lib/liquid/tags/assign.rb +1 -3
  27. data/lib/liquid/tags/break.rb +1 -3
  28. data/lib/liquid/tags/capture.rb +0 -2
  29. data/lib/liquid/tags/case.rb +1 -3
  30. data/lib/liquid/tags/comment.rb +60 -3
  31. data/lib/liquid/tags/continue.rb +1 -3
  32. data/lib/liquid/tags/cycle.rb +14 -4
  33. data/lib/liquid/tags/decrement.rb +8 -7
  34. data/lib/liquid/tags/echo.rb +2 -4
  35. data/lib/liquid/tags/for.rb +6 -8
  36. data/lib/liquid/tags/if.rb +3 -5
  37. data/lib/liquid/tags/ifchanged.rb +0 -2
  38. data/lib/liquid/tags/include.rb +8 -8
  39. data/lib/liquid/tags/increment.rb +8 -7
  40. data/lib/liquid/tags/inline_comment.rb +0 -15
  41. data/lib/liquid/tags/raw.rb +2 -4
  42. data/lib/liquid/tags/render.rb +14 -12
  43. data/lib/liquid/tags/table_row.rb +18 -6
  44. data/lib/liquid/tags/unless.rb +3 -5
  45. data/lib/liquid/tags.rb +47 -0
  46. data/lib/liquid/template.rb +60 -57
  47. data/lib/liquid/tokenizer.rb +127 -11
  48. data/lib/liquid/variable.rb +14 -8
  49. data/lib/liquid/variable_lookup.rb +13 -5
  50. data/lib/liquid/version.rb +1 -1
  51. data/lib/liquid.rb +15 -16
  52. metadata +37 -10
  53. data/lib/liquid/strainer_factory.rb +0 -41
@@ -6,7 +6,14 @@ require 'bigdecimal'
6
6
 
7
7
  module Liquid
8
8
  module StandardFilters
9
- MAX_INT = (1 << 31) - 1
9
+ MAX_I32 = (1 << 31) - 1
10
+ private_constant :MAX_I32
11
+
12
+ MIN_I64 = -(1 << 63)
13
+ MAX_I64 = (1 << 63) - 1
14
+ I64_RANGE = MIN_I64..MAX_I64
15
+ private_constant :MIN_I64, :MAX_I64, :I64_RANGE
16
+
10
17
  HTML_ESCAPE = {
11
18
  '&' => '&amp;',
12
19
  '>' => '&gt;',
@@ -18,10 +25,23 @@ module Liquid
18
25
  STRIP_HTML_BLOCKS = Regexp.union(
19
26
  %r{<script.*?</script>}m,
20
27
  /<!--.*?-->/m,
21
- %r{<style.*?</style>}m
28
+ %r{<style.*?</style>}m,
22
29
  )
23
30
  STRIP_HTML_TAGS = /<.*?>/m
24
31
 
32
+ class << self
33
+ def try_coerce_encoding(input, encoding:)
34
+ original_encoding = input.encoding
35
+ if input.encoding != encoding
36
+ input.force_encoding(encoding)
37
+ unless input.valid_encoding?
38
+ input.force_encoding(original_encoding)
39
+ end
40
+ end
41
+ input
42
+ end
43
+ end
44
+
25
45
  # @liquid_public_docs
26
46
  # @liquid_type filter
27
47
  # @liquid_category array
@@ -62,7 +82,7 @@ module Liquid
62
82
  # @liquid_type filter
63
83
  # @liquid_category string
64
84
  # @liquid_summary
65
- # Capitalizes the first word in a string.
85
+ # Capitalizes the first word in a string and downcases the remaining characters.
66
86
  # @liquid_syntax string | capitalize
67
87
  # @liquid_return [string]
68
88
  def capitalize(input)
@@ -73,7 +93,7 @@ module Liquid
73
93
  # @liquid_type filter
74
94
  # @liquid_category string
75
95
  # @liquid_summary
76
- # Escapes a string.
96
+ # Escapes special characters in HTML, such as `<>`, `'`, and `&`, and converts characters into escape sequences. The filter doesn't effect characters within the string that don’t have a corresponding escape sequence.".
77
97
  # @liquid_syntax string | escape
78
98
  # @liquid_return [string]
79
99
  def escape(input)
@@ -143,7 +163,8 @@ module Liquid
143
163
  # @liquid_syntax string | base64_decode
144
164
  # @liquid_return [string]
145
165
  def base64_decode(input)
146
- Base64.strict_decode64(input.to_s)
166
+ input = input.to_s
167
+ StandardFilters.try_coerce_encoding(Base64.strict_decode64(input), encoding: input.encoding)
147
168
  rescue ::ArgumentError
148
169
  raise Liquid::ArgumentError, "invalid base64 provided to base64_decode"
149
170
  end
@@ -167,7 +188,8 @@ module Liquid
167
188
  # @liquid_syntax string | base64_url_safe_decode
168
189
  # @liquid_return [string]
169
190
  def base64_url_safe_decode(input)
170
- Base64.urlsafe_decode64(input.to_s)
191
+ input = input.to_s
192
+ StandardFilters.try_coerce_encoding(Base64.urlsafe_decode64(input), encoding: input.encoding)
171
193
  rescue ::ArgumentError
172
194
  raise Liquid::ArgumentError, "invalid base64 provided to base64_url_safe_decode"
173
195
  end
@@ -186,10 +208,19 @@ module Liquid
186
208
  offset = Utils.to_integer(offset)
187
209
  length = length ? Utils.to_integer(length) : 1
188
210
 
189
- if input.is_a?(Array)
190
- input.slice(offset, length) || []
191
- else
192
- input.to_s.slice(offset, length) || ''
211
+ begin
212
+ if input.is_a?(Array)
213
+ input.slice(offset, length) || []
214
+ else
215
+ input.to_s.slice(offset, length) || ''
216
+ end
217
+ rescue RangeError
218
+ if I64_RANGE.cover?(length) && I64_RANGE.cover?(offset)
219
+ raise # unexpected error
220
+ end
221
+ offset = offset.clamp(I64_RANGE)
222
+ length = length.clamp(I64_RANGE)
223
+ retry
193
224
  end
194
225
  end
195
226
 
@@ -239,9 +270,9 @@ module Liquid
239
270
  wordlist = begin
240
271
  input.split(" ", words + 1)
241
272
  rescue RangeError
242
- raise if words + 1 < MAX_INT
243
- # e.g. integer #{words} too big to convert to `int'
244
- raise Liquid::ArgumentError, "integer #{words} too big for truncatewords"
273
+ # integer too big for String#split, but we can semantically assume no truncation is needed
274
+ return input if words + 1 > MAX_I32
275
+ raise # unexpected error
245
276
  end
246
277
  return input if wordlist.length <= words
247
278
 
@@ -599,7 +630,7 @@ module Liquid
599
630
  # @liquid_description
600
631
  # > Note:
601
632
  # > The `concat` filter won't filter out duplicates. If you want to remove duplicates, then you need to use the
602
- # > [`uniq` filter](/api/liquid/filters#uniq).
633
+ # > [`uniq` filter](/docs/api/liquid/filters/uniq).
603
634
  # @liquid_syntax array | concat: array
604
635
  # @liquid_return [array[untyped]]
605
636
  def concat(input, array)
@@ -741,7 +772,7 @@ module Liquid
741
772
  # @liquid_type filter
742
773
  # @liquid_category math
743
774
  # @liquid_summary
744
- # Divides a number by a given number.
775
+ # Divides a number by a given number. The `divided_by` filter produces a result of the same type as the divisor. This means if you divide by an integer, the result will be an integer, and if you divide by a float, the result will be a float.
745
776
  # @liquid_syntax number | divided_by: number
746
777
  # @liquid_return [number]
747
778
  def divided_by(input, operand)
@@ -841,18 +872,48 @@ module Liquid
841
872
  # @liquid_summary
842
873
  # Sets a default value for any variable whose value is one of the following:
843
874
  #
844
- # - [`empty`](/api/liquid/basics#empty)
845
- # - [`false`](/api/liquid/basics#truthy-and-falsy)
846
- # - [`nil`](/api/liquid/basics#nil)
875
+ # - [`empty`](/docs/api/liquid/basics#empty)
876
+ # - [`false`](/docs/api/liquid/basics#truthy-and-falsy)
877
+ # - [`nil`](/docs/api/liquid/basics#nil)
847
878
  # @liquid_syntax variable | default: variable
848
879
  # @liquid_return [untyped]
849
- # @liquid_optional_param allow_false [boolean] Whether to use false values instead of the default.
880
+ # @liquid_optional_param allow_false: [boolean] Whether to use false values instead of the default.
850
881
  def default(input, default_value = '', options = {})
851
882
  options = {} unless options.is_a?(Hash)
852
883
  false_check = options['allow_false'] ? input.nil? : !Liquid::Utils.to_liquid_value(input)
853
884
  false_check || (input.respond_to?(:empty?) && input.empty?) ? default_value : input
854
885
  end
855
886
 
887
+ # @liquid_public_docs
888
+ # @liquid_type filter
889
+ # @liquid_category array
890
+ # @liquid_summary
891
+ # Returns the sum of all elements in an array.
892
+ # @liquid_syntax array | sum
893
+ # @liquid_return [number]
894
+ def sum(input, property = nil)
895
+ ary = InputIterator.new(input, context)
896
+ return 0 if ary.empty?
897
+
898
+ values_for_sum = ary.map do |item|
899
+ if property.nil?
900
+ item
901
+ elsif item.respond_to?(:[])
902
+ item[property]
903
+ else
904
+ 0
905
+ end
906
+ rescue TypeError
907
+ raise_property_error(property)
908
+ end
909
+
910
+ result = InputIterator.new(values_for_sum, context).sum do |item|
911
+ Utils.to_number(item)
912
+ end
913
+
914
+ result.is_a?(BigDecimal) ? result.to_f : result
915
+ end
916
+
856
917
  private
857
918
 
858
919
  attr_reader :context
@@ -883,6 +944,8 @@ module Liquid
883
944
  def nil_safe_casecmp(a, b)
884
945
  if !a.nil? && !b.nil?
885
946
  a.to_s.casecmp(b.to_s)
947
+ elsif a.nil? && b.nil?
948
+ 0
886
949
  else
887
950
  a.nil? ? 1 : -1
888
951
  end
@@ -938,6 +1001,4 @@ module Liquid
938
1001
  end
939
1002
  end
940
1003
  end
941
-
942
- Template.register_filter(StandardFilters)
943
1004
  end
@@ -5,7 +5,7 @@ module Liquid
5
5
  # @liquid_type object
6
6
  # @liquid_name tablerowloop
7
7
  # @liquid_summary
8
- # Information about a parent [`tablerow` loop](/api/liquid/tags#tablerow).
8
+ # Information about a parent [`tablerow` loop](/docs/api/liquid/tags/tablerow).
9
9
  class TablerowloopDrop < Drop
10
10
  def initialize(length, cols)
11
11
  @length = length
@@ -3,14 +3,6 @@
3
3
  module Liquid
4
4
  class Tag
5
5
  module Disabler
6
- module ClassMethods
7
- attr_reader :disabled_tags
8
- end
9
-
10
- def self.prepended(base)
11
- base.extend(ClassMethods)
12
- end
13
-
14
6
  def render_to_output_buffer(context, output)
15
7
  context.with_disabled_tags(self.class.disabled_tags) do
16
8
  super
data/lib/liquid/tag.rb CHANGED
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'liquid/tag/disabler'
4
+ require 'liquid/tag/disableable'
5
+
3
6
  module Liquid
4
7
  class Tag
5
8
  attr_reader :nodelist, :tag_name, :line_number, :parse_context
@@ -14,12 +17,18 @@ module Liquid
14
17
  end
15
18
 
16
19
  def disable_tags(*tag_names)
17
- @disabled_tags ||= []
18
- @disabled_tags.concat(tag_names)
20
+ tag_names += disabled_tags
21
+ define_singleton_method(:disabled_tags) { tag_names }
19
22
  prepend(Disabler)
20
23
  end
21
24
 
22
25
  private :new
26
+
27
+ protected
28
+
29
+ def disabled_tags
30
+ []
31
+ end
23
32
  end
24
33
 
25
34
  def initialize(tag_name, markup, parse_context)
@@ -48,7 +57,8 @@ module Liquid
48
57
  # of the `render_to_output_buffer` method will become the default and the `render`
49
58
  # method will be removed.
50
59
  def render_to_output_buffer(context, output)
51
- output << render(context)
60
+ render_result = render(context)
61
+ output << render_result if render_result
52
62
  output
53
63
  end
54
64
 
@@ -8,7 +8,7 @@ module Liquid
8
8
  # @liquid_summary
9
9
  # Creates a new variable.
10
10
  # @liquid_description
11
- # You can create variables of any [basic type](/api/liquid/basics#types), [object](/api/liquid/objects), or object property.
11
+ # You can create variables of any [basic type](/docs/api/liquid/basics#types), [object](/docs/api/liquid/objects), or object property.
12
12
  # @liquid_syntax
13
13
  # {% assign variable_name = value %}
14
14
  # @liquid_syntax_keyword variable_name The name of the variable being created.
@@ -72,6 +72,4 @@ module Liquid
72
72
  end
73
73
  end
74
74
  end
75
-
76
- Template.register_tag('assign', Assign)
77
75
  end
@@ -15,7 +15,7 @@ module Liquid
15
15
  # @liquid_category iteration
16
16
  # @liquid_name break
17
17
  # @liquid_summary
18
- # Stops a [`for` loop](/api/liquid/tags#for) from iterating.
18
+ # Stops a [`for` loop](/docs/api/liquid/tags/for) from iterating.
19
19
  # @liquid_syntax
20
20
  # {% break %}
21
21
  class Break < Tag
@@ -26,6 +26,4 @@ module Liquid
26
26
  output
27
27
  end
28
28
  end
29
-
30
- Template.register_tag('break', Break)
31
29
  end
@@ -39,6 +39,4 @@ module Liquid
39
39
  true
40
40
  end
41
41
  end
42
-
43
- Template.register_tag('capture', Capture)
44
42
  end
@@ -77,7 +77,7 @@ module Liquid
77
77
  end
78
78
 
79
79
  result = Liquid::Utils.to_liquid_value(
80
- block.evaluate(context)
80
+ block.evaluate(context),
81
81
  )
82
82
 
83
83
  if result
@@ -123,6 +123,4 @@ module Liquid
123
123
  end
124
124
  end
125
125
  end
126
-
127
- Template.register_tag('case', Case)
128
126
  end
@@ -8,7 +8,7 @@ module Liquid
8
8
  # @liquid_summary
9
9
  # Prevents an expression from being rendered or output.
10
10
  # @liquid_description
11
- # Any text inside `comment` tags won't be output, and any Liquid code won't be rendered.
11
+ # Any text inside `comment` tags won't be output, and any Liquid code will be parsed, but not executed.
12
12
  # @liquid_syntax
13
13
  # {% comment %}
14
14
  # content
@@ -25,7 +25,64 @@ module Liquid
25
25
  def blank?
26
26
  true
27
27
  end
28
- end
29
28
 
30
- Template.register_tag('comment', Comment)
29
+ private
30
+
31
+ def parse_body(body, tokenizer)
32
+ if parse_context.depth >= MAX_DEPTH
33
+ raise StackLevelError, "Nesting too deep"
34
+ end
35
+
36
+ parse_context.depth += 1
37
+ comment_tag_depth = 1
38
+
39
+ begin
40
+ # Consume tokens without creating child nodes.
41
+ # The children tag doesn't require to be a valid Liquid except the comment and raw tag.
42
+ # The child comment and raw tag must be closed.
43
+ while (token = tokenizer.send(:shift))
44
+ tag_name = if tokenizer.for_liquid_tag
45
+ next if token.empty? || token.match?(BlockBody::WhitespaceOrNothing)
46
+
47
+ tag_name_match = BlockBody::LiquidTagToken.match(token)
48
+
49
+ next if tag_name_match.nil?
50
+
51
+ tag_name_match[1]
52
+ else
53
+ token =~ BlockBody::FullToken
54
+ Regexp.last_match(2)
55
+ end
56
+
57
+ case tag_name
58
+ when "raw"
59
+ parse_raw_tag_body(tokenizer)
60
+ when "comment"
61
+ comment_tag_depth += 1
62
+ when "endcomment"
63
+ comment_tag_depth -= 1
64
+ end
65
+
66
+ if comment_tag_depth.zero?
67
+ parse_context.trim_whitespace = (token[-3] == WhitespaceControl) unless tokenizer.for_liquid_tag
68
+ return false
69
+ end
70
+ end
71
+
72
+ raise_tag_never_closed(block_name)
73
+ ensure
74
+ parse_context.depth -= 1
75
+ end
76
+
77
+ false
78
+ end
79
+
80
+ def parse_raw_tag_body(tokenizer)
81
+ while (token = tokenizer.send(:shift))
82
+ return if token =~ BlockBody::FullTokenPossiblyInvalid && "endraw" == Regexp.last_match(2)
83
+ end
84
+
85
+ raise_tag_never_closed("raw")
86
+ end
87
+ end
31
88
  end
@@ -6,7 +6,7 @@ module Liquid
6
6
  # @liquid_category iteration
7
7
  # @liquid_name continue
8
8
  # @liquid_summary
9
- # Causes a [`for` loop](/api/liquid/tags#for) to skip to the next iteration.
9
+ # Causes a [`for` loop](/docs/api/liquid/tags/for) to skip to the next iteration.
10
10
  # @liquid_syntax
11
11
  # {% continue %}
12
12
  class Continue < Tag
@@ -17,6 +17,4 @@ module Liquid
17
17
  output
18
18
  end
19
19
  end
20
-
21
- Template.register_tag('continue', Continue)
22
20
  end
@@ -6,7 +6,7 @@ module Liquid
6
6
  # @liquid_category iteration
7
7
  # @liquid_name cycle
8
8
  # @liquid_summary
9
- # Loops through a group of strings and outputs them one at a time for each iteration of a [`for` loop](/api/liquid/tags#for).
9
+ # Loops through a group of strings and outputs them one at a time for each iteration of a [`for` loop](/docs/api/liquid/tags/for).
10
10
  # @liquid_description
11
11
  # The `cycle` tag must be used inside a `for` loop.
12
12
  #
@@ -26,14 +26,20 @@ module Liquid
26
26
  when NamedSyntax
27
27
  @variables = variables_from_string(Regexp.last_match(2))
28
28
  @name = parse_expression(Regexp.last_match(1))
29
+ @is_named = true
29
30
  when SimpleSyntax
30
31
  @variables = variables_from_string(markup)
31
32
  @name = @variables.to_s
33
+ @is_named = !@name.match?(/\w+:0x\h{8}/)
32
34
  else
33
35
  raise SyntaxError, options[:locale].t("errors.syntax.cycle")
34
36
  end
35
37
  end
36
38
 
39
+ def named?
40
+ @is_named
41
+ end
42
+
37
43
  def render_to_output_buffer(context, output)
38
44
  context.registers[:cycle] ||= {}
39
45
 
@@ -62,7 +68,13 @@ module Liquid
62
68
  def variables_from_string(markup)
63
69
  markup.split(',').collect do |var|
64
70
  var =~ /\s*(#{QuotedFragment})\s*/o
65
- Regexp.last_match(1) ? parse_expression(Regexp.last_match(1)) : nil
71
+ next unless Regexp.last_match(1)
72
+
73
+ # Expression Parser returns cached objects, and we need to dup them to
74
+ # start the cycle over for each new cycle call.
75
+ # Liquid-C does not have a cache, so we don't need to dup the object.
76
+ var = parse_expression(Regexp.last_match(1))
77
+ var.is_a?(VariableLookup) ? var.dup : var
66
78
  end.compact
67
79
  end
68
80
 
@@ -72,6 +84,4 @@ module Liquid
72
84
  end
73
85
  end
74
86
  end
75
-
76
- Template.register_tag('cycle', Cycle)
77
87
  end
@@ -12,26 +12,27 @@ module Liquid
12
12
  # or [section](/themes/architecture/sections) file that they're created in. However, the variable is shared across
13
13
  # [snippets](/themes/architecture#snippets) included in the file.
14
14
  #
15
- # Similarly, variables that are created with `decrement` are independent from those created with [`assign`](/api/liquid/tags#assign)
16
- # and [`capture`](/api/liquid/tags#capture). However, `decrement` and [`increment`](/api/liquid/tags#increment) share
15
+ # Similarly, variables that are created with `decrement` are independent from those created with [`assign`](/docs/api/liquid/tags/assign)
16
+ # and [`capture`](/docs/api/liquid/tags/capture). However, `decrement` and [`increment`](/docs/api/liquid/tags/increment) share
17
17
  # variables.
18
18
  # @liquid_syntax
19
19
  # {% decrement variable_name %}
20
20
  # @liquid_syntax_keyword variable_name The name of the variable being decremented.
21
21
  class Decrement < Tag
22
+ attr_reader :variable_name
23
+
22
24
  def initialize(tag_name, markup, options)
23
25
  super
24
- @variable = markup.strip
26
+ @variable_name = markup.strip
25
27
  end
26
28
 
27
29
  def render_to_output_buffer(context, output)
28
- value = context.environments.first[@variable] ||= 0
30
+ counter_environment = context.environments.first
31
+ value = counter_environment[@variable_name] || 0
29
32
  value -= 1
30
- context.environments.first[@variable] = value
33
+ counter_environment[@variable_name] = value
31
34
  output << value.to_s
32
35
  output
33
36
  end
34
37
  end
35
-
36
- Template.register_tag('decrement', Decrement)
37
38
  end
@@ -9,10 +9,10 @@ module Liquid
9
9
  # Outputs an expression.
10
10
  # @liquid_description
11
11
  # Using the `echo` tag is the same as wrapping an expression in curly brackets (`{{` and `}}`). However, unlike the curly
12
- # bracket method, you can use the `echo` tag inside [`liquid` tags](/api/liquid/tags#liquid).
12
+ # bracket method, you can use the `echo` tag inside [`liquid` tags](/docs/api/liquid/tags/liquid).
13
13
  #
14
14
  # > Tip:
15
- # > You can use [filters](/api/liquid/filters) on expressions inside `echo` tags.
15
+ # > You can use [filters](/docs/api/liquid/filters) on expressions inside `echo` tags.
16
16
  # @liquid_syntax
17
17
  # {% liquid
18
18
  # echo expression
@@ -36,6 +36,4 @@ module Liquid
36
36
  end
37
37
  end
38
38
  end
39
-
40
- Template.register_tag('echo', Echo)
41
39
  end
@@ -9,10 +9,10 @@ module Liquid
9
9
  # Renders an expression for every item in an array.
10
10
  # @liquid_description
11
11
  # You can do a maximum of 50 iterations with a `for` loop. If you need to iterate over more than 50 items, then use the
12
- # [`paginate` tag](/api/liquid/tags#paginate) to split the items over multiple pages.
12
+ # [`paginate` tag](/docs/api/liquid/tags/paginate) to split the items over multiple pages.
13
13
  #
14
14
  # > Tip:
15
- # > Every `for` loop has an associated [`forloop` object](/api/liquid/objects#forloop) with information about the loop.
15
+ # > Every `for` loop has an associated [`forloop` object](/docs/api/liquid/objects/forloop) with information about the loop.
16
16
  # @liquid_syntax
17
17
  # {% for variable in array %}
18
18
  # expression
@@ -88,7 +88,7 @@ module Liquid
88
88
  end
89
89
 
90
90
  def strict_parse(markup)
91
- p = Parser.new(markup)
91
+ p = @parse_context.new_parser(markup)
92
92
  @variable_name = p.consume(:id)
93
93
  raise SyntaxError, options[:locale].t("errors.syntax.for_invalid_in") unless p.id?('in')
94
94
 
@@ -98,11 +98,12 @@ module Liquid
98
98
  @name = "#{@variable_name}-#{collection_name}"
99
99
  @reversed = p.id?('reversed')
100
100
 
101
- while p.look(:id) && p.look(:colon, 1)
101
+ while p.look(:comma) || p.look(:id)
102
+ p.consume?(:comma)
102
103
  unless (attribute = p.id?('limit') || p.id?('offset'))
103
104
  raise SyntaxError, options[:locale].t("errors.syntax.for_invalid_attribute")
104
105
  end
105
- p.consume
106
+ p.consume(:colon)
106
107
  set_attribute(attribute, p.expression)
107
108
  end
108
109
  p.consume(:end_of_string)
@@ -177,7 +178,6 @@ module Liquid
177
178
  case key
178
179
  when 'offset'
179
180
  @from = if expr == 'continue'
180
- Usage.increment('for_offset_continue')
181
181
  :continue
182
182
  else
183
183
  parse_expression(expr)
@@ -201,6 +201,4 @@ module Liquid
201
201
  end
202
202
  end
203
203
  end
204
-
205
- Template.register_tag('for', For)
206
204
  end
@@ -53,7 +53,7 @@ module Liquid
53
53
  def render_to_output_buffer(context, output)
54
54
  @blocks.each do |block|
55
55
  result = Liquid::Utils.to_liquid_value(
56
- block.evaluate(context)
56
+ block.evaluate(context),
57
57
  )
58
58
 
59
59
  if result
@@ -102,7 +102,7 @@ module Liquid
102
102
  end
103
103
 
104
104
  def strict_parse(markup)
105
- p = Parser.new(markup)
105
+ p = @parse_context.new_parser(markup)
106
106
  condition = parse_binary_comparisons(p)
107
107
  p.consume(:end_of_string)
108
108
  condition
@@ -111,7 +111,7 @@ module Liquid
111
111
  def parse_binary_comparisons(p)
112
112
  condition = parse_comparison(p)
113
113
  first_condition = condition
114
- while (op = (p.id?('and') || p.id?('or')))
114
+ while (op = p.id?('and') || p.id?('or'))
115
115
  child_condition = parse_comparison(p)
116
116
  condition.send(op, child_condition)
117
117
  condition = child_condition
@@ -135,6 +135,4 @@ module Liquid
135
135
  end
136
136
  end
137
137
  end
138
-
139
- Template.register_tag('if', If)
140
138
  end
@@ -14,6 +14,4 @@ module Liquid
14
14
  output
15
15
  end
16
16
  end
17
-
18
- Template.register_tag('ifchanged', Ifchanged)
19
17
  end
@@ -8,7 +8,7 @@ module Liquid
8
8
  # @liquid_summary
9
9
  # Renders a [snippet](/themes/architecture#snippets).
10
10
  # @liquid_description
11
- # Inside the snippet, you can access and alter variables that are [created](/api/liquid/tags#variable-tags) outside of the
11
+ # Inside the snippet, you can access and alter variables that are [created](/docs/api/liquid/tags/variable-tags) outside of the
12
12
  # snippet.
13
13
  # @liquid_syntax
14
14
  # {% include 'filename' %}
@@ -16,7 +16,7 @@ module Liquid
16
16
  # @liquid_deprecated
17
17
  # Deprecated because the way that variables are handled reduces performance and makes code harder to both read and maintain.
18
18
  #
19
- # The `include` tag has been replaced by [`render`](/api/liquid/tags#render).
19
+ # The `include` tag has been replaced by [`render`](/docs/api/liquid/tags/render).
20
20
  class Include < Tag
21
21
  prepend Tag::Disableable
22
22
 
@@ -52,12 +52,12 @@ module Liquid
52
52
 
53
53
  def render_to_output_buffer(context, output)
54
54
  template_name = context.evaluate(@template_name_expr)
55
- raise ArgumentError, options[:locale].t("errors.argument.include") unless template_name
55
+ raise ArgumentError, options[:locale].t("errors.argument.include") unless template_name.is_a?(String)
56
56
 
57
57
  partial = PartialCache.load(
58
58
  template_name,
59
59
  context: context,
60
- parse_context: parse_context
60
+ parse_context: parse_context,
61
61
  )
62
62
 
63
63
  context_variable_name = @alias_name || template_name.split('/').last
@@ -70,9 +70,11 @@ module Liquid
70
70
 
71
71
  old_template_name = context.template_name
72
72
  old_partial = context.partial
73
+
73
74
  begin
74
- context.template_name = template_name
75
- context.partial = true
75
+ context.template_name = partial.name
76
+ context.partial = true
77
+
76
78
  context.stack do
77
79
  @attributes.each do |key, value|
78
80
  context[key] = context.evaluate(value)
@@ -108,6 +110,4 @@ module Liquid
108
110
  end
109
111
  end
110
112
  end
111
-
112
- Template.register_tag('include', Include)
113
113
  end