liquid 4.0.0 → 5.10.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.
Files changed (117) hide show
  1. checksums.yaml +5 -5
  2. data/History.md +235 -2
  3. data/README.md +58 -8
  4. data/lib/liquid/block.rb +51 -20
  5. data/lib/liquid/block_body.rb +216 -82
  6. data/lib/liquid/condition.rb +83 -32
  7. data/lib/liquid/const.rb +8 -0
  8. data/lib/liquid/context.rb +130 -59
  9. data/lib/liquid/deprecations.rb +22 -0
  10. data/lib/liquid/document.rb +47 -9
  11. data/lib/liquid/drop.rb +8 -2
  12. data/lib/liquid/environment.rb +159 -0
  13. data/lib/liquid/errors.rb +23 -20
  14. data/lib/liquid/expression.rb +114 -31
  15. data/lib/liquid/extensions.rb +8 -0
  16. data/lib/liquid/file_system.rb +6 -4
  17. data/lib/liquid/forloop_drop.rb +51 -4
  18. data/lib/liquid/i18n.rb +5 -3
  19. data/lib/liquid/interrupts.rb +3 -1
  20. data/lib/liquid/lexer.rb +165 -39
  21. data/lib/liquid/locales/en.yml +16 -6
  22. data/lib/liquid/parse_context.rb +62 -7
  23. data/lib/liquid/parse_tree_visitor.rb +42 -0
  24. data/lib/liquid/parser.rb +31 -19
  25. data/lib/liquid/parser_switching.rb +42 -3
  26. data/lib/liquid/partial_cache.rb +33 -0
  27. data/lib/liquid/profiler/hooks.rb +26 -14
  28. data/lib/liquid/profiler.rb +67 -86
  29. data/lib/liquid/range_lookup.rb +26 -6
  30. data/lib/liquid/registers.rb +51 -0
  31. data/lib/liquid/resource_limits.rb +47 -8
  32. data/lib/liquid/snippet_drop.rb +22 -0
  33. data/lib/liquid/standardfilters.rb +813 -137
  34. data/lib/liquid/strainer_template.rb +62 -0
  35. data/lib/liquid/tablerowloop_drop.rb +64 -5
  36. data/lib/liquid/tag/disableable.rb +22 -0
  37. data/lib/liquid/tag/disabler.rb +13 -0
  38. data/lib/liquid/tag.rb +42 -6
  39. data/lib/liquid/tags/assign.rb +46 -18
  40. data/lib/liquid/tags/break.rb +15 -4
  41. data/lib/liquid/tags/capture.rb +26 -18
  42. data/lib/liquid/tags/case.rb +108 -32
  43. data/lib/liquid/tags/comment.rb +76 -4
  44. data/lib/liquid/tags/continue.rb +15 -13
  45. data/lib/liquid/tags/cycle.rb +117 -34
  46. data/lib/liquid/tags/decrement.rb +30 -23
  47. data/lib/liquid/tags/doc.rb +81 -0
  48. data/lib/liquid/tags/echo.rb +39 -0
  49. data/lib/liquid/tags/for.rb +109 -96
  50. data/lib/liquid/tags/if.rb +72 -41
  51. data/lib/liquid/tags/ifchanged.rb +10 -11
  52. data/lib/liquid/tags/include.rb +89 -63
  53. data/lib/liquid/tags/increment.rb +31 -20
  54. data/lib/liquid/tags/inline_comment.rb +28 -0
  55. data/lib/liquid/tags/raw.rb +25 -13
  56. data/lib/liquid/tags/render.rb +151 -0
  57. data/lib/liquid/tags/snippet.rb +45 -0
  58. data/lib/liquid/tags/table_row.rb +104 -21
  59. data/lib/liquid/tags/unless.rb +37 -20
  60. data/lib/liquid/tags.rb +51 -0
  61. data/lib/liquid/template.rb +90 -106
  62. data/lib/liquid/template_factory.rb +9 -0
  63. data/lib/liquid/tokenizer.rb +143 -13
  64. data/lib/liquid/usage.rb +8 -0
  65. data/lib/liquid/utils.rb +114 -5
  66. data/lib/liquid/variable.rb +119 -45
  67. data/lib/liquid/variable_lookup.rb +35 -13
  68. data/lib/liquid/version.rb +3 -1
  69. data/lib/liquid.rb +31 -18
  70. metadata +56 -107
  71. data/lib/liquid/strainer.rb +0 -66
  72. data/test/fixtures/en_locale.yml +0 -9
  73. data/test/integration/assign_test.rb +0 -48
  74. data/test/integration/blank_test.rb +0 -106
  75. data/test/integration/capture_test.rb +0 -50
  76. data/test/integration/context_test.rb +0 -32
  77. data/test/integration/document_test.rb +0 -19
  78. data/test/integration/drop_test.rb +0 -273
  79. data/test/integration/error_handling_test.rb +0 -260
  80. data/test/integration/filter_test.rb +0 -178
  81. data/test/integration/hash_ordering_test.rb +0 -23
  82. data/test/integration/output_test.rb +0 -123
  83. data/test/integration/parsing_quirks_test.rb +0 -118
  84. data/test/integration/render_profiling_test.rb +0 -154
  85. data/test/integration/security_test.rb +0 -66
  86. data/test/integration/standard_filter_test.rb +0 -535
  87. data/test/integration/tags/break_tag_test.rb +0 -15
  88. data/test/integration/tags/continue_tag_test.rb +0 -15
  89. data/test/integration/tags/for_tag_test.rb +0 -410
  90. data/test/integration/tags/if_else_tag_test.rb +0 -188
  91. data/test/integration/tags/include_tag_test.rb +0 -238
  92. data/test/integration/tags/increment_tag_test.rb +0 -23
  93. data/test/integration/tags/raw_tag_test.rb +0 -31
  94. data/test/integration/tags/standard_tag_test.rb +0 -296
  95. data/test/integration/tags/statements_test.rb +0 -111
  96. data/test/integration/tags/table_row_test.rb +0 -64
  97. data/test/integration/tags/unless_else_tag_test.rb +0 -26
  98. data/test/integration/template_test.rb +0 -323
  99. data/test/integration/trim_mode_test.rb +0 -525
  100. data/test/integration/variable_test.rb +0 -92
  101. data/test/test_helper.rb +0 -117
  102. data/test/unit/block_unit_test.rb +0 -58
  103. data/test/unit/condition_unit_test.rb +0 -158
  104. data/test/unit/context_unit_test.rb +0 -483
  105. data/test/unit/file_system_unit_test.rb +0 -35
  106. data/test/unit/i18n_unit_test.rb +0 -37
  107. data/test/unit/lexer_unit_test.rb +0 -51
  108. data/test/unit/parser_unit_test.rb +0 -82
  109. data/test/unit/regexp_unit_test.rb +0 -44
  110. data/test/unit/strainer_unit_test.rb +0 -148
  111. data/test/unit/tag_unit_test.rb +0 -21
  112. data/test/unit/tags/case_tag_unit_test.rb +0 -10
  113. data/test/unit/tags/for_tag_unit_test.rb +0 -13
  114. data/test/unit/tags/if_tag_unit_test.rb +0 -8
  115. data/test/unit/template_unit_test.rb +0 -78
  116. data/test/unit/tokenizer_unit_test.rb +0 -55
  117. data/test/unit/variable_unit_test.rb +0 -162
@@ -1,7 +1,22 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Liquid
4
+ # @liquid_public_docs
5
+ # @liquid_type tag
6
+ # @liquid_category syntax
7
+ # @liquid_name comment
8
+ # @liquid_summary
9
+ # Prevents an expression from being rendered or output.
10
+ # @liquid_description
11
+ # Any text inside `comment` tags won't be output, and any Liquid code will be parsed, but not executed.
12
+ # @liquid_syntax
13
+ # {% comment %}
14
+ # content
15
+ # {% endcomment %}
16
+ # @liquid_syntax_keyword content The content of the comment.
2
17
  class Comment < Block
3
- def render(_context)
4
- ''.freeze
18
+ def render_to_output_buffer(_context, output)
19
+ output
5
20
  end
6
21
 
7
22
  def unknown_tag(_tag, _markup, _tokens)
@@ -10,7 +25,64 @@ module Liquid
10
25
  def blank?
11
26
  true
12
27
  end
13
- end
14
28
 
15
- Template.register_tag('comment'.freeze, 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
16
88
  end
@@ -1,18 +1,20 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Liquid
2
- # Continue tag to be used to break out of a for loop.
3
- #
4
- # == Basic Usage:
5
- # {% for item in collection %}
6
- # {% if item.condition %}
7
- # {% continue %}
8
- # {% endif %}
9
- # {% endfor %}
10
- #
4
+ # @liquid_public_docs
5
+ # @liquid_type tag
6
+ # @liquid_category iteration
7
+ # @liquid_name continue
8
+ # @liquid_summary
9
+ # Causes a [`for` loop](/docs/api/liquid/tags/for) to skip to the next iteration.
10
+ # @liquid_syntax
11
+ # {% continue %}
11
12
  class Continue < Tag
12
- def interrupt
13
- ContinueInterrupt.new
13
+ INTERRUPT = ContinueInterrupt.new.freeze
14
+
15
+ def render_to_output_buffer(context, output)
16
+ context.push_interrupt(INTERRUPT)
17
+ output
14
18
  end
15
19
  end
16
-
17
- Template.register_tag('continue'.freeze, Continue)
18
20
  end
@@ -1,57 +1,140 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Liquid
2
- # Cycle is usually used within a loop to alternate between values, like colors or DOM classes.
3
- #
4
- # {% for item in items %}
5
- # <div class="{% cycle 'red', 'green', 'blue' %}"> {{ item }} </div>
6
- # {% end %}
7
- #
8
- # <div class="red"> Item one </div>
9
- # <div class="green"> Item two </div>
10
- # <div class="blue"> Item three </div>
11
- # <div class="red"> Item four </div>
12
- # <div class="green"> Item five</div>
4
+ # @liquid_public_docs
5
+ # @liquid_type tag
6
+ # @liquid_category iteration
7
+ # @liquid_name cycle
8
+ # @liquid_summary
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
+ # @liquid_description
11
+ # The `cycle` tag must be used inside a `for` loop.
13
12
  #
13
+ # > Tip:
14
+ # > Use the `cycle` tag to output text in a predictable pattern. For example, to apply odd/even classes to rows in a table.
15
+ # @liquid_syntax
16
+ # {% cycle string, string, ... %}
14
17
  class Cycle < Tag
15
18
  SimpleSyntax = /\A#{QuotedFragment}+/o
16
19
  NamedSyntax = /\A(#{QuotedFragment})\s*\:\s*(.*)/om
20
+ UNNAMED_CYCLE_PATTERN = /\w+:0x\h{8}/
21
+
22
+ attr_reader :variables
17
23
 
18
24
  def initialize(tag_name, markup, options)
19
25
  super
20
- case markup
21
- when NamedSyntax
22
- @variables = variables_from_string($2)
23
- @name = Expression.parse($1)
24
- when SimpleSyntax
25
- @variables = variables_from_string(markup)
26
- @name = @variables.to_s
27
- else
28
- raise SyntaxError.new(options[:locale].t("errors.syntax.cycle".freeze))
29
- end
26
+ parse_with_selected_parser(markup)
27
+ end
28
+
29
+ def named?
30
+ @is_named
30
31
  end
31
32
 
32
- def render(context)
33
- context.registers[:cycle] ||= Hash.new(0)
33
+ def render_to_output_buffer(context, output)
34
+ context.registers[:cycle] ||= {}
34
35
 
35
- context.stack do
36
- key = context.evaluate(@name)
37
- iteration = context.registers[:cycle][key]
38
- result = context.evaluate(@variables[iteration])
39
- iteration += 1
40
- iteration = 0 if iteration >= @variables.size
41
- context.registers[:cycle][key] = iteration
42
- result
36
+ key = context.evaluate(@name)
37
+ iteration = context.registers[:cycle][key].to_i
38
+
39
+ val = context.evaluate(@variables[iteration])
40
+
41
+ if val.is_a?(Array)
42
+ val = val.join
43
+ elsif !val.is_a?(String)
44
+ val = val.to_s
43
45
  end
46
+
47
+ output << val
48
+
49
+ iteration += 1
50
+ iteration = 0 if iteration >= @variables.size
51
+
52
+ context.registers[:cycle][key] = iteration
53
+ output
44
54
  end
45
55
 
46
56
  private
47
57
 
58
+ # cycle [name:] expression(, expression)*
59
+ def rigid_parse(markup)
60
+ p = @parse_context.new_parser(markup)
61
+
62
+ @variables = []
63
+
64
+ raise SyntaxError, options[:locale].t("errors.syntax.cycle") if p.look(:end_of_string)
65
+
66
+ first_expression = safe_parse_expression(p)
67
+ if p.look(:colon)
68
+ # cycle name: expr1, expr2, ...
69
+ @name = first_expression
70
+ @is_named = true
71
+ p.consume(:colon)
72
+ # After the colon, parse the first variable (required for named cycles)
73
+ @variables << maybe_dup_lookup(safe_parse_expression(p))
74
+ else
75
+ # cycle expr1, expr2, ...
76
+ @variables << maybe_dup_lookup(first_expression)
77
+ end
78
+
79
+ # Parse remaining comma-separated expressions
80
+ while p.consume?(:comma)
81
+ break if p.look(:end_of_string)
82
+
83
+ @variables << maybe_dup_lookup(safe_parse_expression(p))
84
+ end
85
+
86
+ p.consume(:end_of_string)
87
+
88
+ unless @is_named
89
+ @name = @variables.to_s
90
+ @is_named = !@name.match?(UNNAMED_CYCLE_PATTERN)
91
+ end
92
+ end
93
+
94
+ def strict_parse(markup)
95
+ lax_parse(markup)
96
+ end
97
+
98
+ def lax_parse(markup)
99
+ case markup
100
+ when NamedSyntax
101
+ @variables = variables_from_string(Regexp.last_match(2))
102
+ @name = parse_expression(Regexp.last_match(1))
103
+ @is_named = true
104
+ when SimpleSyntax
105
+ @variables = variables_from_string(markup)
106
+ @name = @variables.to_s
107
+ @is_named = !@name.match?(UNNAMED_CYCLE_PATTERN)
108
+ else
109
+ raise SyntaxError, options[:locale].t("errors.syntax.cycle")
110
+ end
111
+ end
112
+
48
113
  def variables_from_string(markup)
49
114
  markup.split(',').collect do |var|
50
115
  var =~ /\s*(#{QuotedFragment})\s*/o
51
- $1 ? Expression.parse($1) : nil
116
+ next unless Regexp.last_match(1)
117
+
118
+ var = parse_expression(Regexp.last_match(1))
119
+ maybe_dup_lookup(var)
52
120
  end.compact
53
121
  end
54
- end
55
122
 
56
- Template.register_tag('cycle', Cycle)
123
+ # For backwards compatibility, whenever a lookup is used in an unnamed cycle,
124
+ # we make it so that the @variables.to_s produces different strings for cycles
125
+ # called with the same arguments (since @variables.to_s is used as the cycle counter key)
126
+ # This makes it so {% cycle a, b %} and {% cycle a, b %} have independent counters even if a and b share value.
127
+ # This is not true for literal values, {% cycle "a", "b" %} and {% cycle "a", "b" %} share the same counter.
128
+ # I was really scratching my head about this one, but migrating away from this would be more headache
129
+ # than it's worth. So we're keeping this quirk for now.
130
+ def maybe_dup_lookup(var)
131
+ var.is_a?(VariableLookup) ? var.dup : var
132
+ end
133
+
134
+ class ParseTreeVisitor < Liquid::ParseTreeVisitor
135
+ def children
136
+ Array(@node.variables)
137
+ end
138
+ end
139
+ end
57
140
  end
@@ -1,35 +1,42 @@
1
- module Liquid
2
- # decrement is used in a place where one needs to insert a counter
3
- # into a template, and needs the counter to survive across
4
- # multiple instantiations of the template.
5
- # NOTE: decrement is a pre-decrement, --i,
6
- # while increment is post: i++.
7
- #
8
- # (To achieve the survival, the application must keep the context)
9
- #
10
- # if the variable does not exist, it is created with value 0.
1
+ # frozen_string_literal: true
11
2
 
12
- # Hello: {% decrement variable %}
13
- #
14
- # gives you:
3
+ module Liquid
4
+ # @liquid_public_docs
5
+ # @liquid_type tag
6
+ # @liquid_category variable
7
+ # @liquid_name decrement
8
+ # @liquid_summary
9
+ # Creates a new variable, with a default value of -1, that's decreased by 1 with each subsequent call.
15
10
  #
16
- # Hello: -1
17
- # Hello: -2
18
- # Hello: -3
11
+ # > Caution:
12
+ # > Predefined Liquid objects can be overridden by variables with the same name.
13
+ # > To make sure that you can access all Liquid objects, make sure that your variable name doesn't match a predefined object's name.
14
+ # @liquid_description
15
+ # Variables that are declared with `decrement` are unique to the [layout](/themes/architecture/layouts), [template](/themes/architecture/templates),
16
+ # or [section](/themes/architecture/sections) file that they're created in. However, the variable is shared across
17
+ # [snippets](/themes/architecture/snippets) included in the file.
19
18
  #
19
+ # Similarly, variables that are created with `decrement` are independent from those created with [`assign`](/docs/api/liquid/tags/assign)
20
+ # and [`capture`](/docs/api/liquid/tags/capture). However, `decrement` and [`increment`](/docs/api/liquid/tags/increment) share
21
+ # variables.
22
+ # @liquid_syntax
23
+ # {% decrement variable_name %}
24
+ # @liquid_syntax_keyword variable_name The name of the variable being decremented.
20
25
  class Decrement < Tag
26
+ attr_reader :variable_name
27
+
21
28
  def initialize(tag_name, markup, options)
22
29
  super
23
- @variable = markup.strip
30
+ @variable_name = markup.strip
24
31
  end
25
32
 
26
- def render(context)
27
- value = context.environments.first[@variable] ||= 0
33
+ def render_to_output_buffer(context, output)
34
+ counter_environment = context.environments.first
35
+ value = counter_environment[@variable_name] || 0
28
36
  value -= 1
29
- context.environments.first[@variable] = value
30
- value.to_s
37
+ counter_environment[@variable_name] = value
38
+ output << value.to_s
39
+ output
31
40
  end
32
41
  end
33
-
34
- Template.register_tag('decrement'.freeze, Decrement)
35
42
  end
@@ -0,0 +1,81 @@
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
+ #
17
+ # For detailed documentation syntax and examples, see the
18
+ # [`LiquidDoc` reference](/docs/storefronts/themes/tools/liquid-doc).
19
+ #
20
+ # @liquid_syntax
21
+ # {% doc %}
22
+ # Renders a message.
23
+ #
24
+ # @param {string} foo - A string value.
25
+ # @param {string} [bar] - An optional string value.
26
+ #
27
+ # @example
28
+ # {% render 'message', foo: 'Hello', bar: 'World' %}
29
+ # {% enddoc %}
30
+ class Doc < Block
31
+ NO_UNEXPECTED_ARGS = /\A\s*\z/
32
+
33
+ def initialize(tag_name, markup, parse_context)
34
+ super
35
+ ensure_valid_markup(tag_name, markup, parse_context)
36
+ end
37
+
38
+ def parse(tokens)
39
+ @body = +""
40
+
41
+ while (token = tokens.shift)
42
+ tag_name = token =~ BlockBody::FullTokenPossiblyInvalid && Regexp.last_match(2)
43
+
44
+ raise_nested_doc_error if tag_name == @tag_name
45
+
46
+ if tag_name == block_delimiter
47
+ parse_context.trim_whitespace = (token[-3] == WhitespaceControl)
48
+ @body << Regexp.last_match(1) if Regexp.last_match(1) != ""
49
+ return
50
+ end
51
+ @body << token unless token.empty?
52
+ end
53
+
54
+ raise_tag_never_closed(block_name)
55
+ end
56
+
57
+ def render_to_output_buffer(_context, output)
58
+ output
59
+ end
60
+
61
+ def blank?
62
+ @body.empty?
63
+ end
64
+
65
+ def nodelist
66
+ [@body]
67
+ end
68
+
69
+ private
70
+
71
+ def ensure_valid_markup(tag_name, markup, parse_context)
72
+ unless NO_UNEXPECTED_ARGS.match?(markup)
73
+ raise SyntaxError, parse_context.locale.t("errors.syntax.block_tag_unexpected_args", tag: tag_name)
74
+ end
75
+ end
76
+
77
+ def raise_nested_doc_error
78
+ raise SyntaxError, parse_context.locale.t("errors.syntax.doc_invalid_nested")
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Liquid
4
+ # @liquid_public_docs
5
+ # @liquid_type tag
6
+ # @liquid_category syntax
7
+ # @liquid_name echo
8
+ # @liquid_summary
9
+ # Outputs an expression.
10
+ # @liquid_description
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](/docs/api/liquid/tags/liquid).
13
+ #
14
+ # > Tip:
15
+ # > You can use [filters](/docs/api/liquid/filters) on expressions inside `echo` tags.
16
+ # @liquid_syntax
17
+ # {% liquid
18
+ # echo expression
19
+ # %}
20
+ # @liquid_syntax_keyword expression The expression to be output.
21
+ class Echo < Tag
22
+ attr_reader :variable
23
+
24
+ def initialize(tag_name, markup, parse_context)
25
+ super
26
+ @variable = Variable.new(markup, parse_context)
27
+ end
28
+
29
+ def render(context)
30
+ @variable.render_to_output_buffer(context, +'')
31
+ end
32
+
33
+ class ParseTreeVisitor < Liquid::ParseTreeVisitor
34
+ def children
35
+ [@node.variable]
36
+ end
37
+ end
38
+ end
39
+ end