liquid 5.11.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/condition.rb +54 -11
- data/lib/liquid/context.rb +19 -1
- data/lib/liquid/drop.rb +1 -1
- data/lib/liquid/expression.rb +3 -1
- data/lib/liquid/i18n.rb +1 -1
- data/lib/liquid/parse_context.rb +1 -1
- data/lib/liquid/parser.rb +5 -1
- data/lib/liquid/resource_limits.rb +22 -5
- data/lib/liquid/self_drop.rb +54 -0
- data/lib/liquid/standardfilters.rb +33 -7
- data/lib/liquid/tags/assign.rb +25 -0
- data/lib/liquid/tags/capture.rb +18 -0
- data/lib/liquid/tags/case.rb +1 -1
- 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/tokenizer.rb +1 -1
- data/lib/liquid/utils.rb +3 -1
- data/lib/liquid/variable.rb +4 -0
- data/lib/liquid/variable_lookup.rb +5 -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/condition.rb
CHANGED
|
@@ -113,22 +113,65 @@ module Liquid
|
|
|
113
113
|
|
|
114
114
|
def equal_variables(left, right)
|
|
115
115
|
if left.is_a?(MethodLiteral)
|
|
116
|
-
|
|
117
|
-
return right.send(left.method_name)
|
|
118
|
-
else
|
|
119
|
-
return nil
|
|
120
|
-
end
|
|
116
|
+
return call_method_literal(left, right)
|
|
121
117
|
end
|
|
122
118
|
|
|
123
119
|
if right.is_a?(MethodLiteral)
|
|
124
|
-
|
|
125
|
-
|
|
120
|
+
return call_method_literal(right, left)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
left == right
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def call_method_literal(literal, value)
|
|
127
|
+
method_name = literal.method_name
|
|
128
|
+
|
|
129
|
+
# If the object responds to the method (e.g., ActiveSupport is loaded), use it
|
|
130
|
+
if value.respond_to?(method_name)
|
|
131
|
+
value.send(method_name)
|
|
132
|
+
else
|
|
133
|
+
# Emulate ActiveSupport's blank?/empty? to make Liquid invariant
|
|
134
|
+
# to whether ActiveSupport is loaded or not
|
|
135
|
+
case method_name
|
|
136
|
+
when :blank?
|
|
137
|
+
liquid_blank?(value)
|
|
138
|
+
when :empty?
|
|
139
|
+
liquid_empty?(value)
|
|
126
140
|
else
|
|
127
|
-
|
|
141
|
+
false
|
|
128
142
|
end
|
|
129
143
|
end
|
|
144
|
+
end
|
|
130
145
|
|
|
131
|
-
|
|
146
|
+
# Implement blank? semantics matching ActiveSupport
|
|
147
|
+
# blank? returns true for nil, false, empty strings, whitespace-only strings,
|
|
148
|
+
# empty arrays, and empty hashes
|
|
149
|
+
def liquid_blank?(value)
|
|
150
|
+
case value
|
|
151
|
+
when NilClass, FalseClass
|
|
152
|
+
true
|
|
153
|
+
when TrueClass, Numeric
|
|
154
|
+
false
|
|
155
|
+
when String
|
|
156
|
+
# Blank if empty or whitespace only (matches ActiveSupport)
|
|
157
|
+
value.empty? || value.match?(/\A\s*\z/)
|
|
158
|
+
when Array, Hash
|
|
159
|
+
value.empty?
|
|
160
|
+
else
|
|
161
|
+
# Fall back to empty? if available, otherwise false
|
|
162
|
+
value.respond_to?(:empty?) ? value.empty? : false
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Implement empty? semantics
|
|
167
|
+
# Note: nil is NOT empty. empty? checks if a collection has zero elements.
|
|
168
|
+
def liquid_empty?(value)
|
|
169
|
+
case value
|
|
170
|
+
when String, Array, Hash
|
|
171
|
+
value.empty?
|
|
172
|
+
else
|
|
173
|
+
value.respond_to?(:empty?) ? value.empty? : false
|
|
174
|
+
end
|
|
132
175
|
end
|
|
133
176
|
|
|
134
177
|
def interpret_condition(left, right, op, context)
|
|
@@ -154,8 +197,8 @@ module Liquid
|
|
|
154
197
|
end
|
|
155
198
|
|
|
156
199
|
def deprecated_default_context
|
|
157
|
-
warn("DEPRECATION WARNING: Condition#evaluate without a context argument is deprecated" \
|
|
158
|
-
"
|
|
200
|
+
warn("DEPRECATION WARNING: Condition#evaluate without a context argument is deprecated " \
|
|
201
|
+
"and will be removed from Liquid 6.0.0.")
|
|
159
202
|
Context.new
|
|
160
203
|
end
|
|
161
204
|
|
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/drop.rb
CHANGED
data/lib/liquid/expression.rb
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module Liquid
|
|
4
4
|
class Expression
|
|
5
|
+
SELF = 'self'
|
|
6
|
+
|
|
5
7
|
LITERALS = {
|
|
6
8
|
nil => nil,
|
|
7
9
|
'nil' => nil,
|
|
@@ -55,7 +57,7 @@ module Liquid
|
|
|
55
57
|
end
|
|
56
58
|
|
|
57
59
|
def inner_parse(markup, ss, cache)
|
|
58
|
-
if
|
|
60
|
+
if markup.start_with?("(") && markup.end_with?(")") && markup =~ RANGES_REGEX
|
|
59
61
|
return RangeLookup.parse(
|
|
60
62
|
Regexp.last_match(1),
|
|
61
63
|
Regexp.last_match(2),
|
data/lib/liquid/i18n.rb
CHANGED
|
@@ -28,7 +28,7 @@ module Liquid
|
|
|
28
28
|
def interpolate(name, vars)
|
|
29
29
|
name.gsub(/%\{(\w+)\}/) do
|
|
30
30
|
# raise TranslationError, "Undefined key #{$1} for interpolation in translation #{name}" unless vars[$1.to_sym]
|
|
31
|
-
|
|
31
|
+
vars[Regexp.last_match(1).to_sym].to_s
|
|
32
32
|
end
|
|
33
33
|
end
|
|
34
34
|
|
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)
|
|
@@ -2,24 +2,39 @@
|
|
|
2
2
|
|
|
3
3
|
module Liquid
|
|
4
4
|
class ResourceLimits
|
|
5
|
-
attr_accessor :render_length_limit,
|
|
6
|
-
|
|
5
|
+
attr_accessor :render_length_limit,
|
|
6
|
+
:render_score_limit,
|
|
7
|
+
:assign_score_limit,
|
|
8
|
+
:cumulative_render_score_limit,
|
|
9
|
+
:cumulative_assign_score_limit
|
|
10
|
+
attr_reader :render_score,
|
|
11
|
+
:assign_score,
|
|
12
|
+
:cumulative_render_score,
|
|
13
|
+
:cumulative_assign_score
|
|
7
14
|
|
|
8
15
|
def initialize(limits)
|
|
9
|
-
@render_length_limit
|
|
10
|
-
@render_score_limit
|
|
11
|
-
@assign_score_limit
|
|
16
|
+
@render_length_limit = limits[:render_length_limit]
|
|
17
|
+
@render_score_limit = limits[:render_score_limit]
|
|
18
|
+
@assign_score_limit = limits[:assign_score_limit]
|
|
19
|
+
@cumulative_render_score_limit = limits[:cumulative_render_score_limit]
|
|
20
|
+
@cumulative_assign_score_limit = limits[:cumulative_assign_score_limit]
|
|
21
|
+
@cumulative_render_score = 0
|
|
22
|
+
@cumulative_assign_score = 0
|
|
12
23
|
reset
|
|
13
24
|
end
|
|
14
25
|
|
|
15
26
|
def increment_render_score(amount)
|
|
16
27
|
@render_score += amount
|
|
28
|
+
@cumulative_render_score += amount
|
|
17
29
|
raise_limits_reached if @render_score_limit && @render_score > @render_score_limit
|
|
30
|
+
raise_limits_reached if @cumulative_render_score_limit && @cumulative_render_score > @cumulative_render_score_limit
|
|
18
31
|
end
|
|
19
32
|
|
|
20
33
|
def increment_assign_score(amount)
|
|
21
34
|
@assign_score += amount
|
|
35
|
+
@cumulative_assign_score += amount
|
|
22
36
|
raise_limits_reached if @assign_score_limit && @assign_score > @assign_score_limit
|
|
37
|
+
raise_limits_reached if @cumulative_assign_score_limit && @cumulative_assign_score > @cumulative_assign_score_limit
|
|
23
38
|
end
|
|
24
39
|
|
|
25
40
|
# update either render_length or assign_score based on whether or not the writes are captured
|
|
@@ -47,6 +62,8 @@ module Liquid
|
|
|
47
62
|
@reached_limit = false
|
|
48
63
|
@last_capture_length = nil
|
|
49
64
|
@render_score = @assign_score = 0
|
|
65
|
+
raise_limits_reached if @cumulative_render_score_limit && @cumulative_render_score > @cumulative_render_score_limit
|
|
66
|
+
raise_limits_reached if @cumulative_assign_score_limit && @cumulative_assign_score > @cumulative_assign_score_limit
|
|
50
67
|
end
|
|
51
68
|
|
|
52
69
|
def with_capture
|
|
@@ -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
|
|
@@ -293,6 +302,19 @@ module Liquid
|
|
|
293
302
|
input.split(pattern)
|
|
294
303
|
end
|
|
295
304
|
|
|
305
|
+
# @liquid_public_docs
|
|
306
|
+
# @liquid_type filter
|
|
307
|
+
# @liquid_category string
|
|
308
|
+
# @liquid_summary
|
|
309
|
+
# Removes leading and trailing whitespace and collapses consecutive whitespace to a single space.
|
|
310
|
+
# @liquid_syntax string | squish
|
|
311
|
+
# @liquid_return [string]
|
|
312
|
+
def squish(input)
|
|
313
|
+
return if input.nil?
|
|
314
|
+
|
|
315
|
+
Utils.to_s(input).strip.gsub(/\s+/, ' ')
|
|
316
|
+
end
|
|
317
|
+
|
|
296
318
|
# @liquid_public_docs
|
|
297
319
|
# @liquid_type filter
|
|
298
320
|
# @liquid_category string
|
|
@@ -768,6 +790,8 @@ module Liquid
|
|
|
768
790
|
# @liquid_syntax array | first
|
|
769
791
|
# @liquid_return [untyped]
|
|
770
792
|
def first(array)
|
|
793
|
+
# ActiveSupport returns "" for empty strings, not nil
|
|
794
|
+
return array[0] || "" if array.is_a?(String)
|
|
771
795
|
array.first if array.respond_to?(:first)
|
|
772
796
|
end
|
|
773
797
|
|
|
@@ -779,6 +803,8 @@ module Liquid
|
|
|
779
803
|
# @liquid_syntax array | last
|
|
780
804
|
# @liquid_return [untyped]
|
|
781
805
|
def last(array)
|
|
806
|
+
# ActiveSupport returns "" for empty strings, not nil
|
|
807
|
+
return array[-1] || "" if array.is_a?(String)
|
|
782
808
|
array.last if array.respond_to?(:last)
|
|
783
809
|
end
|
|
784
810
|
|
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)
|
data/lib/liquid/tags/case.rb
CHANGED
|
@@ -118,7 +118,7 @@ module Liquid
|
|
|
118
118
|
parser = @parse_context.new_parser(markup)
|
|
119
119
|
|
|
120
120
|
loop do
|
|
121
|
-
expr =
|
|
121
|
+
expr = Condition.parse_expression(parse_context, parser.expression, safe: true)
|
|
122
122
|
block = Condition.new(@left, '==', expr)
|
|
123
123
|
block.attach(body)
|
|
124
124
|
@blocks << block
|
|
@@ -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/tokenizer.rb
CHANGED
|
@@ -117,7 +117,7 @@ module Liquid
|
|
|
117
117
|
byte_a = byte_b = @ss.scan_byte
|
|
118
118
|
|
|
119
119
|
while byte_b
|
|
120
|
-
byte_a = @ss.scan_byte while byte_a &&
|
|
120
|
+
byte_a = @ss.scan_byte while byte_a && byte_a != CLOSE_CURLEY && byte_a != OPEN_CURLEY
|
|
121
121
|
|
|
122
122
|
break unless byte_a
|
|
123
123
|
|
data/lib/liquid/utils.rb
CHANGED
|
@@ -69,7 +69,7 @@ module Liquid
|
|
|
69
69
|
return obj if obj.respond_to?(:strftime)
|
|
70
70
|
|
|
71
71
|
if obj.is_a?(String)
|
|
72
|
-
return
|
|
72
|
+
return if obj.empty?
|
|
73
73
|
obj = obj.downcase
|
|
74
74
|
end
|
|
75
75
|
|
|
@@ -95,6 +95,8 @@ module Liquid
|
|
|
95
95
|
|
|
96
96
|
def self.to_s(obj, seen = {})
|
|
97
97
|
case obj
|
|
98
|
+
when BigDecimal
|
|
99
|
+
obj.to_s("F")
|
|
98
100
|
when Hash
|
|
99
101
|
# If the custom hash implementation overrides `#to_s`, use their
|
|
100
102
|
# custom implementation. Otherwise we use Liquid's default
|
data/lib/liquid/variable.rb
CHANGED
|
@@ -70,6 +70,11 @@ module Liquid
|
|
|
70
70
|
elsif lookup_command?(i) && object.respond_to?(key)
|
|
71
71
|
object = object.send(key).to_liquid
|
|
72
72
|
|
|
73
|
+
# Handle string first/last like ActiveSupport does (returns first/last character)
|
|
74
|
+
# ActiveSupport returns "" for empty strings, not nil
|
|
75
|
+
elsif lookup_command?(i) && object.is_a?(String) && (key == "first" || key == "last")
|
|
76
|
+
object = key == "first" ? (object[0] || "") : (object[-1] || "")
|
|
77
|
+
|
|
73
78
|
# No key was present with the desired value and it wasn't one of the directly supported
|
|
74
79
|
# keywords either. The only thing we got left is to return nil or
|
|
75
80
|
# raise an exception if `strict_variables` option is set to true
|
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:
|
|
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: []
|