liquid 5.12.0 → 5.13.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 +4 -4
- data/History.md +12 -0
- data/lib/liquid/context.rb +19 -1
- data/lib/liquid/expression.rb +2 -0
- data/lib/liquid/parse_context.rb +1 -1
- data/lib/liquid/parser.rb +5 -1
- data/lib/liquid/self_drop.rb +54 -0
- data/lib/liquid/standardfilters.rb +16 -7
- data/lib/liquid/tags/assign.rb +25 -0
- data/lib/liquid/tags/capture.rb +18 -0
- data/lib/liquid/tags/decrement.rb +16 -0
- data/lib/liquid/tags/include.rb +13 -4
- data/lib/liquid/tags/increment.rb +16 -0
- data/lib/liquid/template.rb +4 -2
- data/lib/liquid/variable.rb +4 -0
- data/lib/liquid/version.rb +1 -1
- data/lib/liquid.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 23eed44a196b2949d25540274ac7bf9c721f8f620c55f2e0740ff864dbf83488
|
|
4
|
+
data.tar.gz: 561903b058863a29e6f8af0ac12fdda0c7941501b617f9445cb1aa5b7160cb97
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b085504dfad17c6a0de0c152f5500eb6fe27efc588fad25b4e7ed40a3c4287642b92fb5e83f912c54534541d15e218d8721b7245d2819432d8b00f81d801a337
|
|
7
|
+
data.tar.gz: 5d8fec9349651ea1fc4799604c3bbceae5373f9dab9c161029496479bc23f2d3450dab4ac0e36842ced63b1e92ee2d659d7ea937b8ee6edfc2efa5b51da23758
|
data/History.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Liquid Change Log
|
|
2
2
|
|
|
3
|
+
## 5.13.0
|
|
4
|
+
|
|
5
|
+
* Add TruffleRuby in CI [Benoit Daloze]
|
|
6
|
+
* Skip slow test raising many exceptions on non-CRuby [Benoit Daloze]
|
|
7
|
+
* Reject bare-bracket syntax in strict2 and introduce `self` keyword by [Alok Swamy]
|
|
8
|
+
* Add strict2_parse to assign and capture tags by [Alok Swamy]
|
|
9
|
+
* Add strict2_parse to increment and decrement tags by [Alok Swamy]
|
|
10
|
+
* Update liquid-spec adapters for `missing_features` [Ian Ker-Seymer]
|
|
11
|
+
* Prevent `SelfDrop` context mutation across render boundaries [Guilherme Carreiro]
|
|
12
|
+
* Fix `SelfDrop` equality [Guilherme Carreiro]
|
|
13
|
+
* Let environment `self` shadow `SelfDrop` [Ian Ker-Seymer]
|
|
14
|
+
|
|
3
15
|
## 5.11.0
|
|
4
16
|
* Revert the Inline Snippets tag (#2001), treat its inclusion in the latest Liquid release as a bug, and allow for feedback on RFC#1916 to better support Liquid developers [Guilherme Carreiro]
|
|
5
17
|
* Rename the `:rigid` error mode to `:strict2` and display a warning when users attempt to use the `:rigid` mode [Guilherme Carreiro]
|
data/lib/liquid/context.rb
CHANGED
|
@@ -187,6 +187,15 @@ module Liquid
|
|
|
187
187
|
find_variable(key, raise_on_not_found: false) != nil
|
|
188
188
|
end
|
|
189
189
|
|
|
190
|
+
# Checks whether a variable is defined in any scope, including nil-valued keys.
|
|
191
|
+
# Unlike #key?, this uses Hash#key? so that variables explicitly set to nil
|
|
192
|
+
# are still considered defined.
|
|
193
|
+
def variable_defined?(key)
|
|
194
|
+
@scopes.any? { |s| s.key?(key) } ||
|
|
195
|
+
@environments.any? { |e| e.key?(key) } ||
|
|
196
|
+
@static_environments.any? { |e| e.key?(key) }
|
|
197
|
+
end
|
|
198
|
+
|
|
190
199
|
def evaluate(object)
|
|
191
200
|
object.respond_to?(:evaluate) ? object.evaluate(self) : object
|
|
192
201
|
end
|
|
@@ -197,12 +206,21 @@ module Liquid
|
|
|
197
206
|
# path and find_index() is optimized in MRI to reduce object allocation
|
|
198
207
|
index = @scopes.find_index { |s| s.key?(key) }
|
|
199
208
|
|
|
209
|
+
fallback_to_self_drop = key == Expression::SELF && index.nil?
|
|
210
|
+
|
|
200
211
|
variable = if index
|
|
201
212
|
lookup_and_evaluate(@scopes[index], key, raise_on_not_found: raise_on_not_found)
|
|
202
213
|
else
|
|
203
|
-
try_variable_find_in_environments(
|
|
214
|
+
try_variable_find_in_environments(
|
|
215
|
+
key,
|
|
216
|
+
raise_on_not_found: raise_on_not_found && !fallback_to_self_drop,
|
|
217
|
+
)
|
|
204
218
|
end
|
|
205
219
|
|
|
220
|
+
# `self` resolves to a SelfDrop (enabling `self['var']` lookups),
|
|
221
|
+
# but only after the normal environment lookup doesn't find a value.
|
|
222
|
+
return @self_drop ||= SelfDrop.new(self) if fallback_to_self_drop && variable.nil?
|
|
223
|
+
|
|
206
224
|
# update variable's context before invoking #to_liquid
|
|
207
225
|
variable.context = self if variable.respond_to?(:context=)
|
|
208
226
|
|
data/lib/liquid/expression.rb
CHANGED
data/lib/liquid/parse_context.rb
CHANGED
|
@@ -38,7 +38,7 @@ module Liquid
|
|
|
38
38
|
|
|
39
39
|
def new_parser(input)
|
|
40
40
|
@string_scanner.string = input
|
|
41
|
-
Parser.new(@string_scanner)
|
|
41
|
+
Parser.new(@string_scanner, reject_bare_brackets: @error_mode == :strict2 || @error_mode == :rigid)
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
def new_tokenizer(source, start_line_number: nil, for_liquid_tag: false)
|
data/lib/liquid/parser.rb
CHANGED
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
module Liquid
|
|
4
4
|
class Parser
|
|
5
|
-
def initialize(input)
|
|
5
|
+
def initialize(input, reject_bare_brackets: false)
|
|
6
6
|
ss = input.is_a?(StringScanner) ? input : StringScanner.new(input)
|
|
7
7
|
@tokens = Lexer.tokenize(ss)
|
|
8
8
|
@p = 0 # pointer to current location
|
|
9
|
+
@reject_bare_brackets = reject_bare_brackets
|
|
9
10
|
end
|
|
10
11
|
|
|
11
12
|
def jump(point)
|
|
@@ -53,6 +54,9 @@ module Liquid
|
|
|
53
54
|
str = consume
|
|
54
55
|
str << variable_lookups
|
|
55
56
|
when :open_square
|
|
57
|
+
if @reject_bare_brackets
|
|
58
|
+
raise SyntaxError, "Bare bracket access is not allowed. Use #{Expression::SELF}['...'] instead"
|
|
59
|
+
end
|
|
56
60
|
str = consume.dup
|
|
57
61
|
str << expression
|
|
58
62
|
str << consume(:close_square)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Liquid
|
|
4
|
+
# @liquid_public_docs
|
|
5
|
+
# @liquid_type object
|
|
6
|
+
# @liquid_name self
|
|
7
|
+
# @liquid_summary
|
|
8
|
+
# Provides access to variables through the current scope chain.
|
|
9
|
+
# @liquid_description
|
|
10
|
+
# The `self` object resolves variables through the normal lookup hierarchy
|
|
11
|
+
# (local > file > global) without exposing filters, interrupts, errors,
|
|
12
|
+
# or other context internals. It's used when bare bracket notation
|
|
13
|
+
# (`['variable']`) needs to be replaced with an explicit variable lookup.
|
|
14
|
+
#
|
|
15
|
+
# If `self` is explicitly assigned as a local variable (e.g. `{% assign self = 'value' %}`),
|
|
16
|
+
# then the local value takes precedence over the `self` object.
|
|
17
|
+
# @liquid_access global
|
|
18
|
+
class SelfDrop < Drop
|
|
19
|
+
def initialize(self_context)
|
|
20
|
+
super()
|
|
21
|
+
@self_context = self_context
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def [](key)
|
|
25
|
+
@self_context.find_variable(key)
|
|
26
|
+
rescue UndefinedVariable
|
|
27
|
+
nil
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def key?(key)
|
|
31
|
+
@self_context.variable_defined?(key)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def to_liquid
|
|
35
|
+
self
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def ==(other)
|
|
39
|
+
other.is_a?(SelfDrop) && other.self_context.equal?(@self_context)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
alias_method :eql?, :==
|
|
43
|
+
|
|
44
|
+
def hash
|
|
45
|
+
@self_context.object_id.hash
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
protected
|
|
49
|
+
|
|
50
|
+
attr_reader :self_context
|
|
51
|
+
|
|
52
|
+
undef context=
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -8,10 +8,19 @@ module Liquid
|
|
|
8
8
|
MAX_I32 = (1 << 31) - 1
|
|
9
9
|
private_constant :MAX_I32
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
supports_64bit_indices = begin
|
|
12
|
+
[][1 << 33, 1 << 33]
|
|
13
|
+
true
|
|
14
|
+
rescue RangeError
|
|
15
|
+
false
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
INDEX_RANGE = if supports_64bit_indices
|
|
19
|
+
(-(1 << 63))..((1 << 63) - 1)
|
|
20
|
+
else
|
|
21
|
+
(-(1 << 31))..((1 << 31) - 1)
|
|
22
|
+
end
|
|
23
|
+
private_constant :INDEX_RANGE
|
|
15
24
|
|
|
16
25
|
HTML_ESCAPE = {
|
|
17
26
|
'&' => '&',
|
|
@@ -214,11 +223,11 @@ module Liquid
|
|
|
214
223
|
Utils.to_s(input).slice(offset, length) || ''
|
|
215
224
|
end
|
|
216
225
|
rescue RangeError
|
|
217
|
-
if
|
|
226
|
+
if INDEX_RANGE.cover?(length) && INDEX_RANGE.cover?(offset)
|
|
218
227
|
raise # unexpected error
|
|
219
228
|
end
|
|
220
|
-
offset = offset.clamp(
|
|
221
|
-
length = length.clamp(
|
|
229
|
+
offset = offset.clamp(INDEX_RANGE)
|
|
230
|
+
length = length.clamp(INDEX_RANGE)
|
|
222
231
|
retry
|
|
223
232
|
end
|
|
224
233
|
end
|
data/lib/liquid/tags/assign.rb
CHANGED
|
@@ -18,6 +18,8 @@ module Liquid
|
|
|
18
18
|
# @liquid_syntax_keyword variable_name The name of the variable being created.
|
|
19
19
|
# @liquid_syntax_keyword value The value you want to assign to the variable.
|
|
20
20
|
class Assign < Tag
|
|
21
|
+
include ParserSwitching
|
|
22
|
+
|
|
21
23
|
Syntax = /(#{VariableSignature}+)\s*=\s*(.*)\s*/om
|
|
22
24
|
|
|
23
25
|
# @api private
|
|
@@ -29,6 +31,10 @@ module Liquid
|
|
|
29
31
|
|
|
30
32
|
def initialize(tag_name, markup, parse_context)
|
|
31
33
|
super
|
|
34
|
+
parse_with_selected_parser(markup)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def lax_parse(markup)
|
|
32
38
|
if markup =~ Syntax
|
|
33
39
|
@to = Regexp.last_match(1)
|
|
34
40
|
@from = Variable.new(Regexp.last_match(2), parse_context)
|
|
@@ -37,6 +43,25 @@ module Liquid
|
|
|
37
43
|
end
|
|
38
44
|
end
|
|
39
45
|
|
|
46
|
+
def strict_parse(markup)
|
|
47
|
+
lax_parse(markup)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def strict2_parse(markup)
|
|
51
|
+
unless markup =~ Syntax
|
|
52
|
+
self.class.raise_syntax_error(parse_context)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
lhs = Regexp.last_match(1).strip
|
|
56
|
+
rhs = Regexp.last_match(2)
|
|
57
|
+
|
|
58
|
+
p = @parse_context.new_parser(lhs)
|
|
59
|
+
@to = p.consume(:id)
|
|
60
|
+
p.consume(:end_of_string)
|
|
61
|
+
|
|
62
|
+
@from = Variable.new(rhs, parse_context)
|
|
63
|
+
end
|
|
64
|
+
|
|
40
65
|
def render_to_output_buffer(context, output)
|
|
41
66
|
val = @from.render(context)
|
|
42
67
|
context.scopes.last[@to] = val
|
data/lib/liquid/tags/capture.rb
CHANGED
|
@@ -20,10 +20,18 @@ module Liquid
|
|
|
20
20
|
# @liquid_syntax_keyword variable The name of the variable being created.
|
|
21
21
|
# @liquid_syntax_keyword value The value you want to assign to the variable.
|
|
22
22
|
class Capture < Block
|
|
23
|
+
include ParserSwitching
|
|
24
|
+
|
|
23
25
|
Syntax = /(#{VariableSignature}+)/o
|
|
24
26
|
|
|
27
|
+
attr_reader :to
|
|
28
|
+
|
|
25
29
|
def initialize(tag_name, markup, options)
|
|
26
30
|
super
|
|
31
|
+
parse_with_selected_parser(markup)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def lax_parse(markup)
|
|
27
35
|
if markup =~ Syntax
|
|
28
36
|
@to = Regexp.last_match(1)
|
|
29
37
|
else
|
|
@@ -31,6 +39,16 @@ module Liquid
|
|
|
31
39
|
end
|
|
32
40
|
end
|
|
33
41
|
|
|
42
|
+
def strict_parse(markup)
|
|
43
|
+
lax_parse(markup)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def strict2_parse(markup)
|
|
47
|
+
p = @parse_context.new_parser(markup.strip)
|
|
48
|
+
@to = p.consume(:id)
|
|
49
|
+
p.consume(:end_of_string)
|
|
50
|
+
end
|
|
51
|
+
|
|
34
52
|
def render_to_output_buffer(context, output)
|
|
35
53
|
context.resource_limits.with_capture do
|
|
36
54
|
capture_output = render(context)
|
|
@@ -23,13 +23,29 @@ module Liquid
|
|
|
23
23
|
# {% decrement variable_name %}
|
|
24
24
|
# @liquid_syntax_keyword variable_name The name of the variable being decremented.
|
|
25
25
|
class Decrement < Tag
|
|
26
|
+
include ParserSwitching
|
|
27
|
+
|
|
26
28
|
attr_reader :variable_name
|
|
27
29
|
|
|
28
30
|
def initialize(tag_name, markup, options)
|
|
29
31
|
super
|
|
32
|
+
parse_with_selected_parser(markup)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def lax_parse(markup)
|
|
30
36
|
@variable_name = markup.strip
|
|
31
37
|
end
|
|
32
38
|
|
|
39
|
+
def strict_parse(markup)
|
|
40
|
+
lax_parse(markup)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def strict2_parse(markup)
|
|
44
|
+
p = @parse_context.new_parser(markup.strip)
|
|
45
|
+
@variable_name = p.consume(:id)
|
|
46
|
+
p.consume(:end_of_string)
|
|
47
|
+
end
|
|
48
|
+
|
|
33
49
|
def render_to_output_buffer(context, output)
|
|
34
50
|
counter_environment = context.environments.first
|
|
35
51
|
value = counter_environment[@variable_name] || 0
|
data/lib/liquid/tags/include.rb
CHANGED
|
@@ -20,7 +20,8 @@ module Liquid
|
|
|
20
20
|
class Include < Tag
|
|
21
21
|
prepend Tag::Disableable
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
FOR = 'for'
|
|
24
|
+
SYNTAX = /(#{QuotedFragment}+)(\s+(with|#{FOR})\s+(#{QuotedFragment}+))?(\s+(?:as)\s+(#{VariableSegment}+))?/o
|
|
24
25
|
Syntax = SYNTAX
|
|
25
26
|
|
|
26
27
|
attr_reader :template_name_expr, :variable_name_expr, :attributes
|
|
@@ -84,12 +85,18 @@ module Liquid
|
|
|
84
85
|
alias_method :parse_context, :options
|
|
85
86
|
private :parse_context
|
|
86
87
|
|
|
88
|
+
def for_loop?
|
|
89
|
+
@is_for_loop
|
|
90
|
+
end
|
|
91
|
+
|
|
87
92
|
def strict2_parse(markup)
|
|
88
93
|
p = @parse_context.new_parser(markup)
|
|
89
94
|
|
|
90
95
|
@template_name_expr = safe_parse_expression(p)
|
|
91
|
-
|
|
96
|
+
with_or_for = p.id?("for") || p.id?("with")
|
|
97
|
+
@variable_name_expr = safe_parse_expression(p) if with_or_for
|
|
92
98
|
@alias_name = p.consume(:id) if p.id?("as")
|
|
99
|
+
@is_for_loop = (with_or_for == FOR)
|
|
93
100
|
|
|
94
101
|
p.consume?(:comma)
|
|
95
102
|
|
|
@@ -111,11 +118,13 @@ module Liquid
|
|
|
111
118
|
def lax_parse(markup)
|
|
112
119
|
if markup =~ SYNTAX
|
|
113
120
|
template_name = Regexp.last_match(1)
|
|
114
|
-
|
|
121
|
+
with_or_for = Regexp.last_match(3)
|
|
122
|
+
variable_name = Regexp.last_match(4)
|
|
115
123
|
|
|
116
|
-
@alias_name = Regexp.last_match(
|
|
124
|
+
@alias_name = Regexp.last_match(6)
|
|
117
125
|
@variable_name_expr = variable_name ? parse_expression(variable_name) : nil
|
|
118
126
|
@template_name_expr = parse_expression(template_name)
|
|
127
|
+
@is_for_loop = (with_or_for == FOR)
|
|
119
128
|
@attributes = {}
|
|
120
129
|
|
|
121
130
|
markup.scan(TagAttributes) do |key, value|
|
|
@@ -23,13 +23,29 @@ module Liquid
|
|
|
23
23
|
# {% increment variable_name %}
|
|
24
24
|
# @liquid_syntax_keyword variable_name The name of the variable being incremented.
|
|
25
25
|
class Increment < Tag
|
|
26
|
+
include ParserSwitching
|
|
27
|
+
|
|
26
28
|
attr_reader :variable_name
|
|
27
29
|
|
|
28
30
|
def initialize(tag_name, markup, options)
|
|
29
31
|
super
|
|
32
|
+
parse_with_selected_parser(markup)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def lax_parse(markup)
|
|
30
36
|
@variable_name = markup.strip
|
|
31
37
|
end
|
|
32
38
|
|
|
39
|
+
def strict_parse(markup)
|
|
40
|
+
lax_parse(markup)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def strict2_parse(markup)
|
|
44
|
+
p = @parse_context.new_parser(markup.strip)
|
|
45
|
+
@variable_name = p.consume(:id)
|
|
46
|
+
p.consume(:end_of_string)
|
|
47
|
+
end
|
|
48
|
+
|
|
33
49
|
def render_to_output_buffer(context, output)
|
|
34
50
|
counter_environment = context.environments.first
|
|
35
51
|
value = counter_environment[@variable_name] || 0
|
data/lib/liquid/template.rb
CHANGED
|
@@ -151,8 +151,10 @@ module Liquid
|
|
|
151
151
|
|
|
152
152
|
c
|
|
153
153
|
when Liquid::Drop
|
|
154
|
-
drop
|
|
155
|
-
|
|
154
|
+
drop = args.shift
|
|
155
|
+
c = Context.new([drop, assigns], instance_assigns, registers, @rethrow_errors, @resource_limits, {}, @environment)
|
|
156
|
+
drop.context = c if drop.respond_to?(:context=)
|
|
157
|
+
c
|
|
156
158
|
when Hash
|
|
157
159
|
Context.new([args.shift, assigns], instance_assigns, registers, @rethrow_errors, @resource_limits, {}, @environment)
|
|
158
160
|
when nil
|
data/lib/liquid/variable.rb
CHANGED
data/lib/liquid/version.rb
CHANGED
data/lib/liquid.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: liquid
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 5.
|
|
4
|
+
version: 5.13.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tobias Lütke
|
|
@@ -105,6 +105,7 @@ files:
|
|
|
105
105
|
- lib/liquid/range_lookup.rb
|
|
106
106
|
- lib/liquid/registers.rb
|
|
107
107
|
- lib/liquid/resource_limits.rb
|
|
108
|
+
- lib/liquid/self_drop.rb
|
|
108
109
|
- lib/liquid/standardfilters.rb
|
|
109
110
|
- lib/liquid/strainer_template.rb
|
|
110
111
|
- lib/liquid/tablerowloop_drop.rb
|
|
@@ -159,7 +160,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
159
160
|
- !ruby/object:Gem::Version
|
|
160
161
|
version: 1.3.7
|
|
161
162
|
requirements: []
|
|
162
|
-
rubygems_version: 4.0.
|
|
163
|
+
rubygems_version: 4.0.14
|
|
163
164
|
specification_version: 4
|
|
164
165
|
summary: A secure, non-evaling end user template engine with aesthetic markup.
|
|
165
166
|
test_files: []
|