liquid 5.4.0 → 5.6.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/History.md +11 -0
- data/README.md +48 -6
- data/lib/liquid/block.rb +8 -4
- data/lib/liquid/block_body.rb +28 -10
- data/lib/liquid/condition.rb +9 -4
- data/lib/liquid/const.rb +8 -0
- data/lib/liquid/context.rb +24 -14
- data/lib/liquid/deprecations.rb +22 -0
- data/lib/liquid/drop.rb +4 -0
- data/lib/liquid/environment.rb +159 -0
- data/lib/liquid/errors.rb +16 -15
- data/lib/liquid/expression.rb +101 -22
- data/lib/liquid/forloop_drop.rb +2 -5
- data/lib/liquid/lexer.rb +155 -44
- data/lib/liquid/locales/en.yml +1 -0
- data/lib/liquid/parse_context.rb +29 -6
- data/lib/liquid/parse_tree_visitor.rb +1 -1
- data/lib/liquid/parser.rb +3 -3
- data/lib/liquid/partial_cache.rb +12 -3
- data/lib/liquid/range_lookup.rb +14 -4
- data/lib/liquid/standardfilters.rb +82 -21
- data/lib/liquid/tablerowloop_drop.rb +1 -1
- data/lib/liquid/tag/disabler.rb +0 -8
- data/lib/liquid/tag.rb +13 -3
- data/lib/liquid/tags/assign.rb +1 -3
- data/lib/liquid/tags/break.rb +1 -3
- data/lib/liquid/tags/capture.rb +0 -2
- data/lib/liquid/tags/case.rb +1 -3
- data/lib/liquid/tags/comment.rb +60 -3
- data/lib/liquid/tags/continue.rb +1 -3
- data/lib/liquid/tags/cycle.rb +14 -4
- data/lib/liquid/tags/decrement.rb +8 -7
- data/lib/liquid/tags/echo.rb +2 -4
- data/lib/liquid/tags/for.rb +6 -8
- data/lib/liquid/tags/if.rb +3 -5
- data/lib/liquid/tags/ifchanged.rb +0 -2
- data/lib/liquid/tags/include.rb +8 -8
- data/lib/liquid/tags/increment.rb +8 -7
- data/lib/liquid/tags/inline_comment.rb +0 -15
- data/lib/liquid/tags/raw.rb +2 -4
- data/lib/liquid/tags/render.rb +14 -12
- data/lib/liquid/tags/table_row.rb +18 -6
- data/lib/liquid/tags/unless.rb +3 -5
- data/lib/liquid/tags.rb +47 -0
- data/lib/liquid/template.rb +60 -57
- data/lib/liquid/tokenizer.rb +127 -11
- data/lib/liquid/variable.rb +14 -8
- data/lib/liquid/variable_lookup.rb +13 -5
- data/lib/liquid/version.rb +1 -1
- data/lib/liquid.rb +15 -16
- metadata +37 -10
- data/lib/liquid/strainer_factory.rb +0 -41
data/lib/liquid/expression.rb
CHANGED
@@ -3,41 +3,120 @@
|
|
3
3
|
module Liquid
|
4
4
|
class Expression
|
5
5
|
LITERALS = {
|
6
|
-
nil => nil,
|
6
|
+
nil => nil,
|
7
|
+
'nil' => nil,
|
8
|
+
'null' => nil,
|
9
|
+
'' => nil,
|
7
10
|
'true' => true,
|
8
11
|
'false' => false,
|
9
12
|
'blank' => '',
|
10
|
-
'empty' => ''
|
13
|
+
'empty' => '',
|
14
|
+
# in lax mode, minus sign can be a VariableLookup
|
15
|
+
# For simplicity and performace, we treat it like a literal
|
16
|
+
'-' => VariableLookup.parse("-", nil).freeze,
|
11
17
|
}.freeze
|
12
18
|
|
13
|
-
|
14
|
-
|
19
|
+
DOT = ".".ord
|
20
|
+
ZERO = "0".ord
|
21
|
+
NINE = "9".ord
|
22
|
+
DASH = "-".ord
|
15
23
|
|
16
24
|
# Use an atomic group (?>...) to avoid pathological backtracing from
|
17
25
|
# malicious input as described in https://github.com/Shopify/liquid/issues/1357
|
18
|
-
RANGES_REGEX
|
26
|
+
RANGES_REGEX = /\A\(\s*(?>(\S+)\s*\.\.)\s*(\S+)\s*\)\z/
|
27
|
+
INTEGER_REGEX = /\A(-?\d+)\z/
|
28
|
+
FLOAT_REGEX = /\A(-?\d+)\.\d+\z/
|
19
29
|
|
20
|
-
|
21
|
-
|
30
|
+
class << self
|
31
|
+
def parse(markup, ss = StringScanner.new(""), cache = nil)
|
32
|
+
return unless markup
|
22
33
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
34
|
+
markup = markup.strip # markup can be a frozen string
|
35
|
+
|
36
|
+
if (markup.start_with?('"') && markup.end_with?('"')) ||
|
37
|
+
(markup.start_with?("'") && markup.end_with?("'"))
|
38
|
+
return markup[1..-2]
|
39
|
+
elsif LITERALS.key?(markup)
|
40
|
+
return LITERALS[markup]
|
41
|
+
end
|
42
|
+
|
43
|
+
# Cache only exists during parsing
|
44
|
+
if cache
|
45
|
+
return cache[markup] if cache.key?(markup)
|
46
|
+
|
47
|
+
cache[markup] = inner_parse(markup, ss, cache).freeze
|
48
|
+
else
|
49
|
+
inner_parse(markup, ss, nil).freeze
|
50
|
+
end
|
27
51
|
end
|
28
52
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
53
|
+
def inner_parse(markup, ss, cache)
|
54
|
+
if (markup.start_with?("(") && markup.end_with?(")")) && markup =~ RANGES_REGEX
|
55
|
+
return RangeLookup.parse(
|
56
|
+
Regexp.last_match(1),
|
57
|
+
Regexp.last_match(2),
|
58
|
+
ss,
|
59
|
+
cache,
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
63
|
+
if (num = parse_number(markup, ss))
|
64
|
+
num
|
65
|
+
else
|
66
|
+
VariableLookup.parse(markup, ss, cache)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def parse_number(markup, ss)
|
71
|
+
# check if the markup is simple integer or float
|
72
|
+
case markup
|
73
|
+
when INTEGER_REGEX
|
74
|
+
return Integer(markup, 10)
|
75
|
+
when FLOAT_REGEX
|
76
|
+
return markup.to_f
|
77
|
+
end
|
78
|
+
|
79
|
+
ss.string = markup
|
80
|
+
# the first byte must be a digit or a dash
|
81
|
+
byte = ss.scan_byte
|
82
|
+
|
83
|
+
return false if byte != DASH && (byte < ZERO || byte > NINE)
|
84
|
+
|
85
|
+
if byte == DASH
|
86
|
+
peek_byte = ss.peek_byte
|
87
|
+
|
88
|
+
# if it starts with a dash, the next byte must be a digit
|
89
|
+
return false if peek_byte.nil? || !(peek_byte >= ZERO && peek_byte <= NINE)
|
90
|
+
end
|
91
|
+
|
92
|
+
# The markup could be a float with multiple dots
|
93
|
+
first_dot_pos = nil
|
94
|
+
num_end_pos = nil
|
95
|
+
|
96
|
+
while (byte = ss.scan_byte)
|
97
|
+
return false if byte != DOT && (byte < ZERO || byte > NINE)
|
98
|
+
|
99
|
+
# we found our number and now we are just scanning the rest of the string
|
100
|
+
next if num_end_pos
|
101
|
+
|
102
|
+
if byte == DOT
|
103
|
+
if first_dot_pos.nil?
|
104
|
+
first_dot_pos = ss.pos
|
105
|
+
else
|
106
|
+
# we found another dot, so we know that the number ends here
|
107
|
+
num_end_pos = ss.pos - 1
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
num_end_pos = markup.length if ss.eos?
|
113
|
+
|
114
|
+
if num_end_pos
|
115
|
+
# number ends with a number "123.123"
|
116
|
+
markup.byteslice(0, num_end_pos).to_f
|
39
117
|
else
|
40
|
-
|
118
|
+
# number ends with a dot "123."
|
119
|
+
markup.byteslice(0, first_dot_pos).to_f
|
41
120
|
end
|
42
121
|
end
|
43
122
|
end
|
data/lib/liquid/forloop_drop.rb
CHANGED
@@ -5,7 +5,7 @@ module Liquid
|
|
5
5
|
# @liquid_type object
|
6
6
|
# @liquid_name forloop
|
7
7
|
# @liquid_summary
|
8
|
-
# Information about a parent [`for` loop](/api/liquid/tags
|
8
|
+
# Information about a parent [`for` loop](/docs/api/liquid/tags/for).
|
9
9
|
class ForloopDrop < Drop
|
10
10
|
def initialize(name, length, parentloop)
|
11
11
|
@name = name
|
@@ -30,10 +30,7 @@ module Liquid
|
|
30
30
|
# @liquid_return [forloop]
|
31
31
|
attr_reader :parentloop
|
32
32
|
|
33
|
-
|
34
|
-
Usage.increment('forloop_drop_name')
|
35
|
-
@name
|
36
|
-
end
|
33
|
+
attr_reader :name
|
37
34
|
|
38
35
|
# @liquid_public_docs
|
39
36
|
# @liquid_summary
|
data/lib/liquid/lexer.rb
CHANGED
@@ -1,62 +1,173 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "strscan"
|
4
3
|
module Liquid
|
5
4
|
class Lexer
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
5
|
+
CLOSE_ROUND = [:close_round, ")"].freeze
|
6
|
+
CLOSE_SQUARE = [:close_square, "]"].freeze
|
7
|
+
COLON = [:colon, ":"].freeze
|
8
|
+
COMMA = [:comma, ","].freeze
|
9
|
+
COMPARISION_NOT_EQUAL = [:comparison, "!="].freeze
|
10
|
+
COMPARISON_CONTAINS = [:comparison, "contains"].freeze
|
11
|
+
COMPARISON_EQUAL = [:comparison, "=="].freeze
|
12
|
+
COMPARISON_GREATER_THAN = [:comparison, ">"].freeze
|
13
|
+
COMPARISON_GREATER_THAN_OR_EQUAL = [:comparison, ">="].freeze
|
14
|
+
COMPARISON_LESS_THAN = [:comparison, "<"].freeze
|
15
|
+
COMPARISON_LESS_THAN_OR_EQUAL = [:comparison, "<="].freeze
|
16
|
+
COMPARISON_NOT_EQUAL_ALT = [:comparison, "<>"].freeze
|
17
|
+
DASH = [:dash, "-"].freeze
|
18
|
+
DOT = [:dot, "."].freeze
|
19
|
+
DOTDOT = [:dotdot, ".."].freeze
|
20
|
+
DOT_ORD = ".".ord
|
20
21
|
DOUBLE_STRING_LITERAL = /"[^\"]*"/
|
22
|
+
EOS = [:end_of_string].freeze
|
23
|
+
IDENTIFIER = /[a-zA-Z_][\w-]*\??/
|
21
24
|
NUMBER_LITERAL = /-?\d+(\.\d+)?/
|
22
|
-
|
23
|
-
|
25
|
+
OPEN_ROUND = [:open_round, "("].freeze
|
26
|
+
OPEN_SQUARE = [:open_square, "["].freeze
|
27
|
+
PIPE = [:pipe, "|"].freeze
|
28
|
+
QUESTION = [:question, "?"].freeze
|
29
|
+
RUBY_WHITESPACE = [" ", "\t", "\r", "\n", "\f"].freeze
|
30
|
+
SINGLE_STRING_LITERAL = /'[^\']*'/
|
24
31
|
WHITESPACE_OR_NOTHING = /\s*/
|
25
32
|
|
26
|
-
|
27
|
-
|
33
|
+
SINGLE_COMPARISON_TOKENS = [].tap do |table|
|
34
|
+
table["<".ord] = COMPARISON_LESS_THAN
|
35
|
+
table[">".ord] = COMPARISON_GREATER_THAN
|
36
|
+
table.freeze
|
37
|
+
end
|
38
|
+
|
39
|
+
TWO_CHARS_COMPARISON_JUMP_TABLE = [].tap do |table|
|
40
|
+
table["=".ord] = [].tap do |sub_table|
|
41
|
+
sub_table["=".ord] = COMPARISON_EQUAL
|
42
|
+
sub_table.freeze
|
43
|
+
end
|
44
|
+
table["!".ord] = [].tap do |sub_table|
|
45
|
+
sub_table["=".ord] = COMPARISION_NOT_EQUAL
|
46
|
+
sub_table.freeze
|
47
|
+
end
|
48
|
+
table.freeze
|
28
49
|
end
|
29
50
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
51
|
+
COMPARISON_JUMP_TABLE = [].tap do |table|
|
52
|
+
table["<".ord] = [].tap do |sub_table|
|
53
|
+
sub_table["=".ord] = COMPARISON_LESS_THAN_OR_EQUAL
|
54
|
+
sub_table[">".ord] = COMPARISON_NOT_EQUAL_ALT
|
55
|
+
sub_table.freeze
|
56
|
+
end
|
57
|
+
table[">".ord] = [].tap do |sub_table|
|
58
|
+
sub_table["=".ord] = COMPARISON_GREATER_THAN_OR_EQUAL
|
59
|
+
sub_table.freeze
|
60
|
+
end
|
61
|
+
table.freeze
|
62
|
+
end
|
63
|
+
|
64
|
+
NEXT_MATCHER_JUMP_TABLE = [].tap do |table|
|
65
|
+
"a".upto("z") do |c|
|
66
|
+
table[c.ord] = [:id, IDENTIFIER].freeze
|
67
|
+
table[c.upcase.ord] = [:id, IDENTIFIER].freeze
|
68
|
+
end
|
69
|
+
table["_".ord] = [:id, IDENTIFIER].freeze
|
70
|
+
|
71
|
+
"0".upto("9") do |c|
|
72
|
+
table[c.ord] = [:number, NUMBER_LITERAL].freeze
|
73
|
+
end
|
74
|
+
table["-".ord] = [:number, NUMBER_LITERAL].freeze
|
75
|
+
|
76
|
+
table["'".ord] = [:string, SINGLE_STRING_LITERAL].freeze
|
77
|
+
table["\"".ord] = [:string, DOUBLE_STRING_LITERAL].freeze
|
78
|
+
table.freeze
|
79
|
+
end
|
80
|
+
|
81
|
+
SPECIAL_TABLE = [].tap do |table|
|
82
|
+
table["|".ord] = PIPE
|
83
|
+
table[".".ord] = DOT
|
84
|
+
table[":".ord] = COLON
|
85
|
+
table[",".ord] = COMMA
|
86
|
+
table["[".ord] = OPEN_SQUARE
|
87
|
+
table["]".ord] = CLOSE_SQUARE
|
88
|
+
table["(".ord] = OPEN_ROUND
|
89
|
+
table[")".ord] = CLOSE_ROUND
|
90
|
+
table["?".ord] = QUESTION
|
91
|
+
table["-".ord] = DASH
|
92
|
+
end
|
93
|
+
|
94
|
+
NUMBER_TABLE = [].tap do |table|
|
95
|
+
"0".upto("9") do |c|
|
96
|
+
table[c.ord] = true
|
97
|
+
end
|
98
|
+
table.freeze
|
99
|
+
end
|
100
|
+
|
101
|
+
# rubocop:disable Metrics/BlockNesting
|
102
|
+
class << self
|
103
|
+
def tokenize(ss)
|
104
|
+
output = []
|
105
|
+
|
106
|
+
until ss.eos?
|
107
|
+
ss.skip(WHITESPACE_OR_NOTHING)
|
108
|
+
|
109
|
+
break if ss.eos?
|
110
|
+
|
111
|
+
start_pos = ss.pos
|
112
|
+
peeked = ss.peek_byte
|
113
|
+
|
114
|
+
if (special = SPECIAL_TABLE[peeked])
|
115
|
+
ss.scan_byte
|
116
|
+
# Special case for ".."
|
117
|
+
if special == DOT && ss.peek_byte == DOT_ORD
|
118
|
+
ss.scan_byte
|
119
|
+
output << DOTDOT
|
120
|
+
elsif special == DASH
|
121
|
+
# Special case for negative numbers
|
122
|
+
if (peeked_byte = ss.peek_byte) && NUMBER_TABLE[peeked_byte]
|
123
|
+
ss.pos -= 1
|
124
|
+
output << [:number, ss.scan(NUMBER_LITERAL)]
|
125
|
+
else
|
126
|
+
output << special
|
127
|
+
end
|
128
|
+
else
|
129
|
+
output << special
|
130
|
+
end
|
131
|
+
elsif (sub_table = TWO_CHARS_COMPARISON_JUMP_TABLE[peeked])
|
132
|
+
ss.scan_byte
|
133
|
+
if (peeked_byte = ss.peek_byte) && (found = sub_table[peeked_byte])
|
134
|
+
output << found
|
135
|
+
ss.scan_byte
|
136
|
+
else
|
137
|
+
raise_syntax_error(start_pos, ss)
|
138
|
+
end
|
139
|
+
elsif (sub_table = COMPARISON_JUMP_TABLE[peeked])
|
140
|
+
ss.scan_byte
|
141
|
+
if (peeked_byte = ss.peek_byte) && (found = sub_table[peeked_byte])
|
142
|
+
output << found
|
143
|
+
ss.scan_byte
|
144
|
+
else
|
145
|
+
output << SINGLE_COMPARISON_TOKENS[peeked]
|
146
|
+
end
|
52
147
|
else
|
53
|
-
|
148
|
+
type, pattern = NEXT_MATCHER_JUMP_TABLE[peeked]
|
149
|
+
|
150
|
+
if type && (t = ss.scan(pattern))
|
151
|
+
# Special case for "contains"
|
152
|
+
output << if type == :id && t == "contains" && output.last&.first != :dot
|
153
|
+
COMPARISON_CONTAINS
|
154
|
+
else
|
155
|
+
[type, t]
|
156
|
+
end
|
157
|
+
else
|
158
|
+
raise_syntax_error(start_pos, ss)
|
159
|
+
end
|
54
160
|
end
|
55
161
|
end
|
56
|
-
|
162
|
+
# rubocop:enable Metrics/BlockNesting
|
163
|
+
output << EOS
|
57
164
|
end
|
58
165
|
|
59
|
-
|
166
|
+
def raise_syntax_error(start_pos, ss)
|
167
|
+
ss.pos = start_pos
|
168
|
+
# the character could be a UTF-8 character, use getch to get all the bytes
|
169
|
+
raise SyntaxError, "Unexpected character #{ss.getch}"
|
170
|
+
end
|
60
171
|
end
|
61
172
|
end
|
62
173
|
end
|
data/lib/liquid/locales/en.yml
CHANGED
@@ -15,6 +15,7 @@
|
|
15
15
|
include: "Error in tag 'include' - Valid syntax: include '[template]' (with|for) [object|collection]"
|
16
16
|
inline_comment_invalid: "Syntax error in tag '#' - Each line of comments must be prefixed by the '#' character"
|
17
17
|
invalid_delimiter: "'%{tag}' is not a valid delimiter for %{block_name} tags. use %{block_delimiter}"
|
18
|
+
invalid_template_encoding: "Invalid template encoding"
|
18
19
|
render: "Syntax error in tag 'render' - Template name must be a quoted string"
|
19
20
|
table_row: "Syntax Error in 'table_row loop' - Valid syntax: table_row [item] in [collection] cols=3"
|
20
21
|
tag_never_closed: "'%{block_name}' tag was never closed"
|
data/lib/liquid/parse_context.rb
CHANGED
@@ -3,14 +3,27 @@
|
|
3
3
|
module Liquid
|
4
4
|
class ParseContext
|
5
5
|
attr_accessor :locale, :line_number, :trim_whitespace, :depth
|
6
|
-
attr_reader :partial, :warnings, :error_mode
|
6
|
+
attr_reader :partial, :warnings, :error_mode, :environment
|
7
7
|
|
8
|
-
def initialize(options =
|
8
|
+
def initialize(options = Const::EMPTY_HASH)
|
9
|
+
@environment = options.fetch(:environment, Environment.default)
|
9
10
|
@template_options = options ? options.dup : {}
|
10
11
|
|
11
12
|
@locale = @template_options[:locale] ||= I18n.new
|
12
13
|
@warnings = []
|
13
14
|
|
15
|
+
# constructing new StringScanner in Lexer, Tokenizer, etc is expensive
|
16
|
+
# This StringScanner will be shared by all of them
|
17
|
+
@string_scanner = StringScanner.new("")
|
18
|
+
|
19
|
+
@expression_cache = if options[:expression_cache].nil?
|
20
|
+
{}
|
21
|
+
elsif options[:expression_cache].respond_to?(:[]) && options[:expression_cache].respond_to?(:[]=)
|
22
|
+
options[:expression_cache]
|
23
|
+
elsif options[:expression_cache]
|
24
|
+
{}
|
25
|
+
end
|
26
|
+
|
14
27
|
self.depth = 0
|
15
28
|
self.partial = false
|
16
29
|
end
|
@@ -23,19 +36,29 @@ module Liquid
|
|
23
36
|
Liquid::BlockBody.new
|
24
37
|
end
|
25
38
|
|
26
|
-
def
|
27
|
-
|
39
|
+
def new_parser(input)
|
40
|
+
@string_scanner.string = input
|
41
|
+
Parser.new(@string_scanner)
|
42
|
+
end
|
43
|
+
|
44
|
+
def new_tokenizer(source, start_line_number: nil, for_liquid_tag: false)
|
45
|
+
Tokenizer.new(
|
46
|
+
source: source,
|
47
|
+
string_scanner: @string_scanner,
|
48
|
+
line_number: start_line_number,
|
49
|
+
for_liquid_tag: for_liquid_tag,
|
50
|
+
)
|
28
51
|
end
|
29
52
|
|
30
53
|
def parse_expression(markup)
|
31
|
-
Expression.parse(markup)
|
54
|
+
Expression.parse(markup, @string_scanner, @expression_cache)
|
32
55
|
end
|
33
56
|
|
34
57
|
def partial=(value)
|
35
58
|
@partial = value
|
36
59
|
@options = value ? partial_options : @template_options
|
37
60
|
|
38
|
-
@error_mode = @options[:error_mode] ||
|
61
|
+
@error_mode = @options[:error_mode] || @environment.error_mode
|
39
62
|
end
|
40
63
|
|
41
64
|
def partial_options
|
data/lib/liquid/parser.rb
CHANGED
@@ -3,8 +3,8 @@
|
|
3
3
|
module Liquid
|
4
4
|
class Parser
|
5
5
|
def initialize(input)
|
6
|
-
|
7
|
-
@tokens =
|
6
|
+
ss = input.is_a?(StringScanner) ? input : StringScanner.new(input)
|
7
|
+
@tokens = Lexer.tokenize(ss)
|
8
8
|
@p = 0 # pointer to current location
|
9
9
|
end
|
10
10
|
|
@@ -53,7 +53,7 @@ module Liquid
|
|
53
53
|
str = consume
|
54
54
|
str << variable_lookups
|
55
55
|
when :open_square
|
56
|
-
str = consume
|
56
|
+
str = consume.dup
|
57
57
|
str << expression
|
58
58
|
str << consume(:close_square)
|
59
59
|
str << variable_lookups
|
data/lib/liquid/partial_cache.rb
CHANGED
@@ -4,7 +4,8 @@ module Liquid
|
|
4
4
|
class PartialCache
|
5
5
|
def self.load(template_name, context:, parse_context:)
|
6
6
|
cached_partials = context.registers[:cached_partials]
|
7
|
-
|
7
|
+
cache_key = "#{template_name}:#{parse_context.error_mode}"
|
8
|
+
cached = cached_partials[cache_key]
|
8
9
|
return cached if cached
|
9
10
|
|
10
11
|
file_system = context.registers[:file_system]
|
@@ -15,8 +16,16 @@ module Liquid
|
|
15
16
|
template_factory = context.registers[:template_factory]
|
16
17
|
template = template_factory.for(template_name)
|
17
18
|
|
18
|
-
|
19
|
-
|
19
|
+
begin
|
20
|
+
partial = template.parse(source, parse_context)
|
21
|
+
rescue Liquid::Error => e
|
22
|
+
e.template_name = template&.name || template_name
|
23
|
+
raise e
|
24
|
+
end
|
25
|
+
|
26
|
+
partial.name ||= template_name
|
27
|
+
|
28
|
+
cached_partials[cache_key] = partial
|
20
29
|
ensure
|
21
30
|
parse_context.partial = false
|
22
31
|
end
|
data/lib/liquid/range_lookup.rb
CHANGED
@@ -2,13 +2,23 @@
|
|
2
2
|
|
3
3
|
module Liquid
|
4
4
|
class RangeLookup
|
5
|
-
def self.parse(start_markup, end_markup)
|
6
|
-
start_obj = Expression.parse(start_markup)
|
7
|
-
end_obj = Expression.parse(end_markup)
|
5
|
+
def self.parse(start_markup, end_markup, string_scanner, cache = nil)
|
6
|
+
start_obj = Expression.parse(start_markup, string_scanner, cache)
|
7
|
+
end_obj = Expression.parse(end_markup, string_scanner, cache)
|
8
8
|
if start_obj.respond_to?(:evaluate) || end_obj.respond_to?(:evaluate)
|
9
9
|
new(start_obj, end_obj)
|
10
10
|
else
|
11
|
-
|
11
|
+
begin
|
12
|
+
start_obj.to_i..end_obj.to_i
|
13
|
+
rescue NoMethodError
|
14
|
+
invalid_expr = start_markup unless start_obj.respond_to?(:to_i)
|
15
|
+
invalid_expr ||= end_markup unless end_obj.respond_to?(:to_i)
|
16
|
+
if invalid_expr
|
17
|
+
raise Liquid::SyntaxError, "Invalid expression type '#{invalid_expr}' in range expression"
|
18
|
+
end
|
19
|
+
|
20
|
+
raise
|
21
|
+
end
|
12
22
|
end
|
13
23
|
end
|
14
24
|
|