liquid 5.4.0 → 5.6.4
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 +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
|
|