liquid 3.0.6 → 5.4.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 +5 -5
- data/History.md +243 -58
- data/README.md +43 -4
- data/lib/liquid/block.rb +57 -123
- data/lib/liquid/block_body.rb +217 -85
- data/lib/liquid/condition.rb +92 -45
- data/lib/liquid/context.rb +154 -89
- data/lib/liquid/document.rb +57 -9
- data/lib/liquid/drop.rb +20 -17
- data/lib/liquid/errors.rb +27 -29
- data/lib/liquid/expression.rb +32 -20
- data/lib/liquid/extensions.rb +21 -7
- data/lib/liquid/file_system.rb +17 -15
- data/lib/liquid/forloop_drop.rb +92 -0
- data/lib/liquid/i18n.rb +10 -8
- data/lib/liquid/interrupts.rb +4 -3
- data/lib/liquid/lexer.rb +37 -26
- data/lib/liquid/locales/en.yml +13 -6
- data/lib/liquid/parse_context.rb +54 -0
- data/lib/liquid/parse_tree_visitor.rb +42 -0
- data/lib/liquid/parser.rb +30 -18
- data/lib/liquid/parser_switching.rb +20 -6
- data/lib/liquid/partial_cache.rb +24 -0
- data/lib/liquid/profiler/hooks.rb +26 -14
- data/lib/liquid/profiler.rb +72 -92
- data/lib/liquid/range_lookup.rb +28 -3
- data/lib/liquid/registers.rb +51 -0
- data/lib/liquid/resource_limits.rb +62 -0
- data/lib/liquid/standardfilters.rb +715 -132
- data/lib/liquid/strainer_factory.rb +41 -0
- data/lib/liquid/strainer_template.rb +62 -0
- data/lib/liquid/tablerowloop_drop.rb +121 -0
- data/lib/liquid/tag/disableable.rb +22 -0
- data/lib/liquid/tag/disabler.rb +21 -0
- data/lib/liquid/tag.rb +35 -12
- data/lib/liquid/tags/assign.rb +57 -18
- data/lib/liquid/tags/break.rb +15 -5
- data/lib/liquid/tags/capture.rb +24 -18
- data/lib/liquid/tags/case.rb +79 -30
- data/lib/liquid/tags/comment.rb +19 -4
- data/lib/liquid/tags/continue.rb +16 -12
- data/lib/liquid/tags/cycle.rb +47 -27
- data/lib/liquid/tags/decrement.rb +23 -24
- data/lib/liquid/tags/echo.rb +41 -0
- data/lib/liquid/tags/for.rb +155 -124
- data/lib/liquid/tags/if.rb +97 -63
- data/lib/liquid/tags/ifchanged.rb +11 -12
- data/lib/liquid/tags/include.rb +82 -73
- data/lib/liquid/tags/increment.rb +23 -17
- data/lib/liquid/tags/inline_comment.rb +43 -0
- data/lib/liquid/tags/raw.rb +50 -8
- data/lib/liquid/tags/render.rb +109 -0
- data/lib/liquid/tags/table_row.rb +57 -41
- data/lib/liquid/tags/unless.rb +38 -20
- data/lib/liquid/template.rb +71 -103
- data/lib/liquid/template_factory.rb +9 -0
- data/lib/liquid/tokenizer.rb +39 -0
- data/lib/liquid/usage.rb +8 -0
- data/lib/liquid/utils.rb +63 -9
- data/lib/liquid/variable.rb +74 -56
- data/lib/liquid/variable_lookup.rb +31 -15
- data/lib/liquid/version.rb +3 -1
- data/lib/liquid.rb +27 -12
- metadata +30 -106
- data/lib/liquid/module_ex.rb +0 -62
- data/lib/liquid/strainer.rb +0 -59
- data/lib/liquid/token.rb +0 -18
- data/test/fixtures/en_locale.yml +0 -9
- data/test/integration/assign_test.rb +0 -48
- data/test/integration/blank_test.rb +0 -106
- data/test/integration/capture_test.rb +0 -50
- data/test/integration/context_test.rb +0 -32
- data/test/integration/drop_test.rb +0 -271
- data/test/integration/error_handling_test.rb +0 -207
- data/test/integration/filter_test.rb +0 -138
- data/test/integration/hash_ordering_test.rb +0 -23
- data/test/integration/output_test.rb +0 -124
- data/test/integration/parsing_quirks_test.rb +0 -116
- data/test/integration/render_profiling_test.rb +0 -154
- data/test/integration/security_test.rb +0 -64
- data/test/integration/standard_filter_test.rb +0 -396
- data/test/integration/tags/break_tag_test.rb +0 -16
- data/test/integration/tags/continue_tag_test.rb +0 -16
- data/test/integration/tags/for_tag_test.rb +0 -375
- data/test/integration/tags/if_else_tag_test.rb +0 -190
- data/test/integration/tags/include_tag_test.rb +0 -234
- data/test/integration/tags/increment_tag_test.rb +0 -24
- data/test/integration/tags/raw_tag_test.rb +0 -25
- data/test/integration/tags/standard_tag_test.rb +0 -297
- data/test/integration/tags/statements_test.rb +0 -113
- data/test/integration/tags/table_row_test.rb +0 -63
- data/test/integration/tags/unless_else_tag_test.rb +0 -26
- data/test/integration/template_test.rb +0 -182
- data/test/integration/variable_test.rb +0 -82
- data/test/test_helper.rb +0 -89
- data/test/unit/block_unit_test.rb +0 -55
- data/test/unit/condition_unit_test.rb +0 -149
- data/test/unit/context_unit_test.rb +0 -492
- data/test/unit/file_system_unit_test.rb +0 -35
- data/test/unit/i18n_unit_test.rb +0 -37
- data/test/unit/lexer_unit_test.rb +0 -48
- data/test/unit/module_ex_unit_test.rb +0 -87
- data/test/unit/parser_unit_test.rb +0 -82
- data/test/unit/regexp_unit_test.rb +0 -44
- data/test/unit/strainer_unit_test.rb +0 -69
- data/test/unit/tag_unit_test.rb +0 -16
- data/test/unit/tags/case_tag_unit_test.rb +0 -10
- data/test/unit/tags/for_tag_unit_test.rb +0 -13
- data/test/unit/tags/if_tag_unit_test.rb +0 -8
- data/test/unit/template_unit_test.rb +0 -69
- data/test/unit/tokenizer_unit_test.rb +0 -38
- data/test/unit/variable_unit_test.rb +0 -145
- /data/{MIT-LICENSE → LICENSE} +0 -0
data/lib/liquid/block.rb
CHANGED
@@ -1,93 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Liquid
|
2
4
|
class Block < Tag
|
3
|
-
|
4
|
-
ContentOfVariable = /\A#{VariableStart}(.*)#{VariableEnd}\z/om
|
5
|
-
TAGSTART = "{%".freeze
|
6
|
-
VARSTART = "{{".freeze
|
5
|
+
MAX_DEPTH = 100
|
7
6
|
|
8
|
-
def
|
9
|
-
|
7
|
+
def initialize(tag_name, markup, options)
|
8
|
+
super
|
9
|
+
@blank = true
|
10
10
|
end
|
11
11
|
|
12
12
|
def parse(tokens)
|
13
|
-
@
|
14
|
-
@
|
15
|
-
@nodelist.clear
|
16
|
-
|
17
|
-
while token = tokens.shift
|
18
|
-
begin
|
19
|
-
unless token.empty?
|
20
|
-
case
|
21
|
-
when token.start_with?(TAGSTART)
|
22
|
-
if token =~ FullToken
|
23
|
-
|
24
|
-
# if we found the proper block delimiter just end parsing here and let the outer block
|
25
|
-
# proceed
|
26
|
-
return if block_delimiter == $1
|
27
|
-
|
28
|
-
# fetch the tag from registered blocks
|
29
|
-
if tag = Template.tags[$1]
|
30
|
-
markup = token.is_a?(Token) ? token.child($2) : $2
|
31
|
-
new_tag = tag.parse($1, markup, tokens, @options)
|
32
|
-
new_tag.line_number = token.line_number if token.is_a?(Token)
|
33
|
-
@blank &&= new_tag.blank?
|
34
|
-
@nodelist << new_tag
|
35
|
-
else
|
36
|
-
# this tag is not registered with the system
|
37
|
-
# pass it to the current block for special handling or error reporting
|
38
|
-
unknown_tag($1, $2, tokens)
|
39
|
-
end
|
40
|
-
else
|
41
|
-
raise SyntaxError.new(options[:locale].t("errors.syntax.tag_termination".freeze, :token => token, :tag_end => TagEnd.inspect))
|
42
|
-
end
|
43
|
-
when token.start_with?(VARSTART)
|
44
|
-
new_var = create_variable(token)
|
45
|
-
new_var.line_number = token.line_number if token.is_a?(Token)
|
46
|
-
@nodelist << new_var
|
47
|
-
@blank = false
|
48
|
-
else
|
49
|
-
@nodelist << token
|
50
|
-
@blank &&= (token =~ /\A\s*\z/)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
rescue SyntaxError => e
|
54
|
-
e.set_line_number_from_token(token)
|
55
|
-
raise
|
56
|
-
end
|
13
|
+
@body = new_body
|
14
|
+
while parse_body(@body, tokens)
|
57
15
|
end
|
16
|
+
@body.freeze
|
17
|
+
end
|
58
18
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
assert_missing_delimitation!
|
19
|
+
# For backwards compatibility
|
20
|
+
def render(context)
|
21
|
+
@body.render(context)
|
63
22
|
end
|
64
23
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
all_warnings.concat(@warnings) if @warnings
|
24
|
+
def blank?
|
25
|
+
@blank
|
26
|
+
end
|
69
27
|
|
70
|
-
|
71
|
-
|
72
|
-
|
28
|
+
def nodelist
|
29
|
+
@body.nodelist
|
30
|
+
end
|
73
31
|
|
74
|
-
|
32
|
+
def unknown_tag(tag_name, _markup, _tokenizer)
|
33
|
+
Block.raise_unknown_tag(tag_name, block_name, block_delimiter, parse_context)
|
75
34
|
end
|
76
35
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
raise SyntaxError.
|
81
|
-
|
82
|
-
|
83
|
-
raise SyntaxError.
|
84
|
-
|
85
|
-
|
36
|
+
# @api private
|
37
|
+
def self.raise_unknown_tag(tag, block_name, block_delimiter, parse_context)
|
38
|
+
if tag == 'else'
|
39
|
+
raise SyntaxError, parse_context.locale.t("errors.syntax.unexpected_else",
|
40
|
+
block_name: block_name)
|
41
|
+
elsif tag.start_with?('end')
|
42
|
+
raise SyntaxError, parse_context.locale.t("errors.syntax.invalid_delimiter",
|
43
|
+
tag: tag,
|
44
|
+
block_name: block_name,
|
45
|
+
block_delimiter: block_delimiter)
|
86
46
|
else
|
87
|
-
raise SyntaxError.
|
47
|
+
raise SyntaxError, parse_context.locale.t("errors.syntax.unknown_tag", tag: tag)
|
88
48
|
end
|
89
49
|
end
|
90
50
|
|
51
|
+
def raise_tag_never_closed(block_name)
|
52
|
+
raise SyntaxError, parse_context.locale.t("errors.syntax.tag_never_closed", block_name: block_name)
|
53
|
+
end
|
54
|
+
|
91
55
|
def block_name
|
92
56
|
@tag_name
|
93
57
|
end
|
@@ -96,65 +60,35 @@ module Liquid
|
|
96
60
|
@block_delimiter ||= "end#{block_name}"
|
97
61
|
end
|
98
62
|
|
99
|
-
|
100
|
-
token.scan(ContentOfVariable) do |content|
|
101
|
-
markup = token.is_a?(Token) ? token.child(content.first) : content.first
|
102
|
-
return Variable.new(markup, @options)
|
103
|
-
end
|
104
|
-
raise SyntaxError.new(options[:locale].t("errors.syntax.variable_termination".freeze, :token => token, :tag_end => VariableEnd.inspect))
|
105
|
-
end
|
63
|
+
private
|
106
64
|
|
107
|
-
|
108
|
-
|
65
|
+
# @api public
|
66
|
+
def new_body
|
67
|
+
parse_context.new_block_body
|
109
68
|
end
|
110
69
|
|
111
|
-
|
70
|
+
# @api public
|
71
|
+
def parse_body(body, tokens)
|
72
|
+
if parse_context.depth >= MAX_DEPTH
|
73
|
+
raise StackLevelError, "Nesting too deep"
|
74
|
+
end
|
75
|
+
parse_context.depth += 1
|
76
|
+
begin
|
77
|
+
body.parse(tokens, parse_context) do |end_tag_name, end_tag_params|
|
78
|
+
@blank &&= body.blank?
|
112
79
|
|
113
|
-
|
114
|
-
|
115
|
-
end
|
80
|
+
return false if end_tag_name == block_delimiter
|
81
|
+
raise_tag_never_closed(block_name) unless end_tag_name
|
116
82
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
context.resource_limits[:render_score_current] += list.length
|
121
|
-
|
122
|
-
list.each do |token|
|
123
|
-
# Break out if we have any unhanded interrupts.
|
124
|
-
break if context.has_interrupt?
|
125
|
-
|
126
|
-
begin
|
127
|
-
# If we get an Interrupt that means the block must stop processing. An
|
128
|
-
# Interrupt is any command that stops block execution such as {% break %}
|
129
|
-
# or {% continue %}
|
130
|
-
if token.is_a? Continue or token.is_a? Break
|
131
|
-
context.push_interrupt(token.interrupt)
|
132
|
-
break
|
133
|
-
end
|
134
|
-
|
135
|
-
token_output = render_token(token, context)
|
136
|
-
|
137
|
-
unless token.is_a?(Block) && token.blank?
|
138
|
-
output << token_output
|
139
|
-
end
|
140
|
-
rescue MemoryError => e
|
141
|
-
raise e
|
142
|
-
rescue ::StandardError => e
|
143
|
-
output << (context.handle_error(e, token))
|
83
|
+
# this tag is not registered with the system
|
84
|
+
# pass it to the current block for special handling or error reporting
|
85
|
+
unknown_tag(end_tag_name, end_tag_params, tokens)
|
144
86
|
end
|
87
|
+
ensure
|
88
|
+
parse_context.depth -= 1
|
145
89
|
end
|
146
90
|
|
147
|
-
|
148
|
-
end
|
149
|
-
|
150
|
-
def render_token(token, context)
|
151
|
-
token_output = (token.respond_to?(:render) ? token.render(context) : token)
|
152
|
-
context.increment_used_resources(:render_length_current, token_output)
|
153
|
-
if context.resource_limits_reached?
|
154
|
-
context.resource_limits[:reached] = true
|
155
|
-
raise MemoryError.new("Memory limits exceeded".freeze)
|
156
|
-
end
|
157
|
-
token_output
|
91
|
+
true
|
158
92
|
end
|
159
93
|
end
|
160
94
|
end
|
data/lib/liquid/block_body.rb
CHANGED
@@ -1,123 +1,255 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'English'
|
4
|
+
|
1
5
|
module Liquid
|
2
6
|
class BlockBody
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
+
LiquidTagToken = /\A\s*(#{TagName})\s*(.*?)\z/o
|
8
|
+
FullToken = /\A#{TagStart}#{WhitespaceControl}?(\s*)(#{TagName})(\s*)(.*?)#{WhitespaceControl}?#{TagEnd}\z/om
|
9
|
+
ContentOfVariable = /\A#{VariableStart}#{WhitespaceControl}?(.*?)#{WhitespaceControl}?#{VariableEnd}\z/om
|
10
|
+
WhitespaceOrNothing = /\A\s*\z/
|
11
|
+
TAGSTART = "{%"
|
12
|
+
VARSTART = "{{"
|
7
13
|
|
8
14
|
attr_reader :nodelist
|
9
15
|
|
10
16
|
def initialize
|
11
17
|
@nodelist = []
|
12
|
-
@blank
|
13
|
-
end
|
14
|
-
|
15
|
-
def parse(
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
@blank &&= !!(token =~ /\A\s*\z/)
|
47
|
-
end
|
18
|
+
@blank = true
|
19
|
+
end
|
20
|
+
|
21
|
+
def parse(tokenizer, parse_context, &block)
|
22
|
+
raise FrozenError, "can't modify frozen Liquid::BlockBody" if frozen?
|
23
|
+
|
24
|
+
parse_context.line_number = tokenizer.line_number
|
25
|
+
|
26
|
+
if tokenizer.for_liquid_tag
|
27
|
+
parse_for_liquid_tag(tokenizer, parse_context, &block)
|
28
|
+
else
|
29
|
+
parse_for_document(tokenizer, parse_context, &block)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def freeze
|
34
|
+
@nodelist.freeze
|
35
|
+
super
|
36
|
+
end
|
37
|
+
|
38
|
+
private def parse_for_liquid_tag(tokenizer, parse_context)
|
39
|
+
while (token = tokenizer.shift)
|
40
|
+
unless token.empty? || token.match?(WhitespaceOrNothing)
|
41
|
+
unless token =~ LiquidTagToken
|
42
|
+
# line isn't empty but didn't match tag syntax, yield and let the
|
43
|
+
# caller raise a syntax error
|
44
|
+
return yield token, token
|
45
|
+
end
|
46
|
+
tag_name = Regexp.last_match(1)
|
47
|
+
markup = Regexp.last_match(2)
|
48
|
+
unless (tag = registered_tags[tag_name])
|
49
|
+
# end parsing if we reach an unknown tag and let the caller decide
|
50
|
+
# determine how to proceed
|
51
|
+
return yield tag_name, markup
|
48
52
|
end
|
49
|
-
|
50
|
-
|
51
|
-
|
53
|
+
new_tag = tag.parse(tag_name, markup, tokenizer, parse_context)
|
54
|
+
@blank &&= new_tag.blank?
|
55
|
+
@nodelist << new_tag
|
52
56
|
end
|
57
|
+
parse_context.line_number = tokenizer.line_number
|
53
58
|
end
|
54
59
|
|
55
60
|
yield nil, nil
|
56
61
|
end
|
57
62
|
|
58
|
-
|
59
|
-
|
63
|
+
# @api private
|
64
|
+
def self.unknown_tag_in_liquid_tag(tag, parse_context)
|
65
|
+
Block.raise_unknown_tag(tag, 'liquid', '%}', parse_context)
|
66
|
+
end
|
67
|
+
|
68
|
+
# @api private
|
69
|
+
def self.raise_missing_tag_terminator(token, parse_context)
|
70
|
+
raise SyntaxError, parse_context.locale.t("errors.syntax.tag_termination", token: token, tag_end: TagEnd.inspect)
|
60
71
|
end
|
61
72
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
73
|
+
# @api private
|
74
|
+
def self.raise_missing_variable_terminator(token, parse_context)
|
75
|
+
raise SyntaxError, parse_context.locale.t("errors.syntax.variable_termination", token: token, tag_end: VariableEnd.inspect)
|
76
|
+
end
|
77
|
+
|
78
|
+
# @api private
|
79
|
+
def self.render_node(context, output, node)
|
80
|
+
node.render_to_output_buffer(context, output)
|
81
|
+
rescue => exc
|
82
|
+
blank_tag = !node.instance_of?(Variable) && node.blank?
|
83
|
+
rescue_render_node(context, output, node.line_number, exc, blank_tag)
|
84
|
+
end
|
85
|
+
|
86
|
+
# @api private
|
87
|
+
def self.rescue_render_node(context, output, line_number, exc, blank_tag)
|
88
|
+
case exc
|
89
|
+
when MemoryError
|
90
|
+
raise
|
91
|
+
when UndefinedVariable, UndefinedDropMethod, UndefinedFilter
|
92
|
+
context.handle_error(exc, line_number)
|
93
|
+
else
|
94
|
+
error_message = context.handle_error(exc, line_number)
|
95
|
+
unless blank_tag # conditional for backwards compatibility
|
96
|
+
output << error_message
|
97
|
+
end
|
66
98
|
end
|
67
|
-
all_warnings
|
68
99
|
end
|
69
100
|
|
70
|
-
def
|
71
|
-
|
72
|
-
|
73
|
-
|
101
|
+
private def parse_liquid_tag(markup, parse_context)
|
102
|
+
liquid_tag_tokenizer = parse_context.new_tokenizer(
|
103
|
+
markup, start_line_number: parse_context.line_number, for_liquid_tag: true
|
104
|
+
)
|
105
|
+
parse_for_liquid_tag(liquid_tag_tokenizer, parse_context) do |end_tag_name, _end_tag_markup|
|
106
|
+
if end_tag_name
|
107
|
+
BlockBody.unknown_tag_in_liquid_tag(end_tag_name, parse_context)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
74
111
|
|
75
|
-
|
76
|
-
|
77
|
-
|
112
|
+
private def parse_for_document(tokenizer, parse_context)
|
113
|
+
while (token = tokenizer.shift)
|
114
|
+
next if token.empty?
|
115
|
+
case
|
116
|
+
when token.start_with?(TAGSTART)
|
117
|
+
whitespace_handler(token, parse_context)
|
118
|
+
unless token =~ FullToken
|
119
|
+
BlockBody.raise_missing_tag_terminator(token, parse_context)
|
120
|
+
end
|
121
|
+
tag_name = Regexp.last_match(2)
|
122
|
+
markup = Regexp.last_match(4)
|
78
123
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
124
|
+
if parse_context.line_number
|
125
|
+
# newlines inside the tag should increase the line number,
|
126
|
+
# particularly important for multiline {% liquid %} tags
|
127
|
+
parse_context.line_number += Regexp.last_match(1).count("\n") + Regexp.last_match(3).count("\n")
|
128
|
+
end
|
129
|
+
|
130
|
+
if tag_name == 'liquid'
|
131
|
+
parse_liquid_tag(markup, parse_context)
|
132
|
+
next
|
133
|
+
end
|
134
|
+
|
135
|
+
unless (tag = registered_tags[tag_name])
|
136
|
+
# end parsing if we reach an unknown tag and let the caller decide
|
137
|
+
# determine how to proceed
|
138
|
+
return yield tag_name, markup
|
139
|
+
end
|
140
|
+
new_tag = tag.parse(tag_name, markup, tokenizer, parse_context)
|
141
|
+
@blank &&= new_tag.blank?
|
142
|
+
@nodelist << new_tag
|
143
|
+
when token.start_with?(VARSTART)
|
144
|
+
whitespace_handler(token, parse_context)
|
145
|
+
@nodelist << create_variable(token, parse_context)
|
146
|
+
@blank = false
|
147
|
+
else
|
148
|
+
if parse_context.trim_whitespace
|
149
|
+
token.lstrip!
|
86
150
|
end
|
151
|
+
parse_context.trim_whitespace = false
|
152
|
+
@nodelist << token
|
153
|
+
@blank &&= token.match?(WhitespaceOrNothing)
|
154
|
+
end
|
155
|
+
parse_context.line_number = tokenizer.line_number
|
156
|
+
end
|
87
157
|
|
88
|
-
|
158
|
+
yield nil, nil
|
159
|
+
end
|
89
160
|
|
90
|
-
|
91
|
-
|
161
|
+
def whitespace_handler(token, parse_context)
|
162
|
+
if token[2] == WhitespaceControl
|
163
|
+
previous_token = @nodelist.last
|
164
|
+
if previous_token.is_a?(String)
|
165
|
+
first_byte = previous_token.getbyte(0)
|
166
|
+
previous_token.rstrip!
|
167
|
+
if previous_token.empty? && parse_context[:bug_compatible_whitespace_trimming] && first_byte
|
168
|
+
previous_token << first_byte
|
92
169
|
end
|
93
|
-
rescue MemoryError => e
|
94
|
-
raise e
|
95
|
-
rescue ::StandardError => e
|
96
|
-
output << context.handle_error(e, token)
|
97
170
|
end
|
98
171
|
end
|
172
|
+
parse_context.trim_whitespace = (token[-3] == WhitespaceControl)
|
173
|
+
end
|
174
|
+
|
175
|
+
def blank?
|
176
|
+
@blank
|
177
|
+
end
|
99
178
|
|
100
|
-
|
179
|
+
# Remove blank strings in the block body for a control flow tag (e.g. `if`, `for`, `case`, `unless`)
|
180
|
+
# with a blank body.
|
181
|
+
#
|
182
|
+
# For example, in a conditional assignment like the following
|
183
|
+
#
|
184
|
+
# ```
|
185
|
+
# {% if size > max_size %}
|
186
|
+
# {% assign size = max_size %}
|
187
|
+
# {% endif %}
|
188
|
+
# ```
|
189
|
+
#
|
190
|
+
# we assume the intention wasn't to output the blank spaces in the `if` tag's block body, so this method
|
191
|
+
# will remove them to reduce the render output size.
|
192
|
+
#
|
193
|
+
# Note that it is now preferred to use the `liquid` tag for this use case.
|
194
|
+
def remove_blank_strings
|
195
|
+
raise "remove_blank_strings only support being called on a blank block body" unless @blank
|
196
|
+
@nodelist.reject! { |node| node.instance_of?(String) }
|
101
197
|
end
|
102
198
|
|
103
|
-
|
199
|
+
def render(context)
|
200
|
+
render_to_output_buffer(context, +'')
|
201
|
+
end
|
202
|
+
|
203
|
+
def render_to_output_buffer(context, output)
|
204
|
+
freeze unless frozen?
|
205
|
+
|
206
|
+
context.resource_limits.increment_render_score(@nodelist.length)
|
104
207
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
208
|
+
idx = 0
|
209
|
+
while (node = @nodelist[idx])
|
210
|
+
if node.instance_of?(String)
|
211
|
+
output << node
|
212
|
+
else
|
213
|
+
render_node(context, output, node)
|
214
|
+
# If we get an Interrupt that means the block must stop processing. An
|
215
|
+
# Interrupt is any command that stops block execution such as {% break %}
|
216
|
+
# or {% continue %}. These tags may also occur through Block or Include tags.
|
217
|
+
break if context.interrupt? # might have happened in a for-block
|
218
|
+
end
|
219
|
+
idx += 1
|
220
|
+
|
221
|
+
context.resource_limits.increment_write_score(output)
|
111
222
|
end
|
112
|
-
|
223
|
+
|
224
|
+
output
|
113
225
|
end
|
114
226
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
227
|
+
private
|
228
|
+
|
229
|
+
def render_node(context, output, node)
|
230
|
+
BlockBody.render_node(context, output, node)
|
231
|
+
end
|
232
|
+
|
233
|
+
def create_variable(token, parse_context)
|
234
|
+
if token =~ ContentOfVariable
|
235
|
+
markup = Regexp.last_match(1)
|
236
|
+
return Variable.new(markup, parse_context)
|
119
237
|
end
|
120
|
-
|
238
|
+
BlockBody.raise_missing_variable_terminator(token, parse_context)
|
239
|
+
end
|
240
|
+
|
241
|
+
# @deprecated Use {.raise_missing_tag_terminator} instead
|
242
|
+
def raise_missing_tag_terminator(token, parse_context)
|
243
|
+
BlockBody.raise_missing_tag_terminator(token, parse_context)
|
244
|
+
end
|
245
|
+
|
246
|
+
# @deprecated Use {.raise_missing_variable_terminator} instead
|
247
|
+
def raise_missing_variable_terminator(token, parse_context)
|
248
|
+
BlockBody.raise_missing_variable_terminator(token, parse_context)
|
249
|
+
end
|
250
|
+
|
251
|
+
def registered_tags
|
252
|
+
Template.tags
|
121
253
|
end
|
122
254
|
end
|
123
255
|
end
|