liquid 5.0.1 → 5.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/History.md +31 -0
- data/README.md +2 -2
- data/lib/liquid/block_body.rb +2 -2
- data/lib/liquid/condition.rb +10 -4
- data/lib/liquid/context.rb +1 -1
- data/lib/liquid/expression.rb +11 -10
- data/lib/liquid/range_lookup.rb +8 -0
- data/lib/liquid/standardfilters.rb +87 -20
- data/lib/liquid/static_registers.rb +5 -1
- data/lib/liquid/strainer_factory.rb +11 -10
- data/lib/liquid/strainer_template.rb +5 -0
- data/lib/liquid/tags/case.rb +8 -1
- data/lib/liquid/tags/if.rb +5 -1
- data/lib/liquid/tags/unless.rb +10 -2
- data/lib/liquid/tokenizer.rb +2 -2
- data/lib/liquid/utils.rb +8 -0
- data/lib/liquid/variable_lookup.rb +3 -0
- data/lib/liquid/version.rb +1 -1
- data/lib/liquid.rb +2 -2
- data/test/integration/context_test.rb +12 -14
- data/test/integration/filter_kwarg_test.rb +24 -0
- data/test/integration/profiler_test.rb +33 -6
- data/test/integration/standard_filter_test.rb +119 -32
- data/test/integration/template_test.rb +14 -0
- data/test/integration/variable_test.rb +31 -0
- data/test/test_helper.rb +48 -10
- data/test/unit/condition_unit_test.rb +27 -14
- data/test/unit/parse_tree_visitor_test.rb +7 -0
- data/test/unit/strainer_factory_unit_test.rb +2 -1
- data/test/unit/strainer_template_unit_test.rb +1 -1
- metadata +48 -46
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c0e965825a9194672f6d2b9c3011db0125eb3dbbb6d44cacf67559140c3f0f9b
|
4
|
+
data.tar.gz: d1d98d881037c5ff8cc2b9da99d3b3e002eb1ca1d2bda593c806830c1607a98a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fa9caca36072ca79bb727b7bdb9a671e082039c3c999d6cbb79bf8b1feec0d514159daedf2b54f561e66ca5ab7e11273956fb49afbae3580004d3e1d3780b9ab
|
7
|
+
data.tar.gz: a766a7b068287a7db0a70222a149f8a52a659e93c6593bb53a31e5cee279cbc65b7db14eaa1cc0e89e816bd76020bf352814cd9c2c6b4e32436f743ab0c8e629
|
data/History.md
CHANGED
@@ -1,5 +1,36 @@
|
|
1
1
|
# Liquid Change Log
|
2
2
|
|
3
|
+
## 5.3.0 2022-03-22
|
4
|
+
|
5
|
+
### Fixes
|
6
|
+
* StandardFilter: Fix missing @context on iterations (#1525) [Thierry Joyal]
|
7
|
+
* Test under Ruby 3.1 (#1533) [petergoldstein]
|
8
|
+
* Fix warning about block and default value in `static_registers.rb` (#1531) [Peter Zhu]
|
9
|
+
|
10
|
+
### Deprecation
|
11
|
+
* Condition#evaluate to require mandatory context argument in Liquid 6.0.0 (#1527) [Thierry Joyal]
|
12
|
+
|
13
|
+
## 5.2.0 2022-03-01
|
14
|
+
|
15
|
+
### Features
|
16
|
+
* Add `remove_last`, and `replace_last` filters (#1422) [Anders Hagbard]
|
17
|
+
* Eagerly cache global filters (#1524) [Jean Boussier]
|
18
|
+
|
19
|
+
### Fixes
|
20
|
+
* Fix some internal errors in filters from invalid input (#1476) [Dylan Thacker-Smith]
|
21
|
+
* Allow dash in filter kwarg name for consistency with Liquid::C (#1518) [CP Clermont]
|
22
|
+
|
23
|
+
## 5.1.0 / 2021-09-09
|
24
|
+
|
25
|
+
### Features
|
26
|
+
* Add `base64_encode`, `base64_decode`, `base64_url_safe_encode`, and `base64_url_safe_decode` filters (#1450) [Daniel Insley]
|
27
|
+
* Introduce `to_liquid_value` in `Liquid::Drop` (#1441) [Michael Go]
|
28
|
+
|
29
|
+
### Fixes
|
30
|
+
* Fix support for using a String subclass for the liquid source (#1421) [Dylan Thacker-Smith]
|
31
|
+
* Add `ParseTreeVisitor` to `RangeLookup` (#1470) [CP Clermont]
|
32
|
+
* Translate `RangeError` to `Liquid::Error` for `truncatewords` with large int (#1431) [Dylan Thacker-Smith]
|
33
|
+
|
3
34
|
## 5.0.1 / 2021-03-24
|
4
35
|
|
5
36
|
### Fixes
|
data/README.md
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
|
6
6
|
* [Contributing guidelines](CONTRIBUTING.md)
|
7
7
|
* [Version history](History.md)
|
8
|
-
* [Liquid documentation from Shopify](
|
8
|
+
* [Liquid documentation from Shopify](https://shopify.dev/api/liquid)
|
9
9
|
* [Liquid Wiki at GitHub](https://github.com/Shopify/liquid/wiki)
|
10
10
|
* [Website](http://liquidmarkup.org/)
|
11
11
|
|
@@ -56,7 +56,7 @@ For standard use you can just pass it the content of a file and call render with
|
|
56
56
|
|
57
57
|
Setting the error mode of Liquid lets you specify how strictly you want your templates to be interpreted.
|
58
58
|
Normally the parser is very lax and will accept almost anything without error. Unfortunately this can make
|
59
|
-
it very hard to debug and can lead to unexpected behaviour.
|
59
|
+
it very hard to debug and can lead to unexpected behaviour.
|
60
60
|
|
61
61
|
Liquid also comes with a stricter parser that can be used when editing templates to give better error messages
|
62
62
|
when templates are invalid. You can enable this new parser like this:
|
data/lib/liquid/block_body.rb
CHANGED
@@ -231,8 +231,8 @@ module Liquid
|
|
231
231
|
end
|
232
232
|
|
233
233
|
def create_variable(token, parse_context)
|
234
|
-
token
|
235
|
-
markup =
|
234
|
+
if token =~ ContentOfVariable
|
235
|
+
markup = Regexp.last_match(1)
|
236
236
|
return Variable.new(markup, parse_context)
|
237
237
|
end
|
238
238
|
BlockBody.raise_missing_variable_terminator(token, parse_context)
|
data/lib/liquid/condition.rb
CHANGED
@@ -8,7 +8,7 @@ module Liquid
|
|
8
8
|
# c = Condition.new(1, '==', 1)
|
9
9
|
# c.evaluate #=> true
|
10
10
|
#
|
11
|
-
class Condition
|
11
|
+
class Condition # :nodoc:
|
12
12
|
@@operators = {
|
13
13
|
'==' => ->(cond, left, right) { cond.send(:equal_variables, left, right) },
|
14
14
|
'!=' => ->(cond, left, right) { !cond.send(:equal_variables, left, right) },
|
@@ -61,7 +61,7 @@ module Liquid
|
|
61
61
|
@child_condition = nil
|
62
62
|
end
|
63
63
|
|
64
|
-
def evaluate(context =
|
64
|
+
def evaluate(context = deprecated_default_context)
|
65
65
|
condition = self
|
66
66
|
result = nil
|
67
67
|
loop do
|
@@ -134,8 +134,8 @@ module Liquid
|
|
134
134
|
# return this as the result.
|
135
135
|
return context.evaluate(left) if op.nil?
|
136
136
|
|
137
|
-
left = context.evaluate(left)
|
138
|
-
right = context.evaluate(right)
|
137
|
+
left = Liquid::Utils.to_liquid_value(context.evaluate(left))
|
138
|
+
right = Liquid::Utils.to_liquid_value(context.evaluate(right))
|
139
139
|
|
140
140
|
operation = self.class.operators[op] || raise(Liquid::ArgumentError, "Unknown operator #{op}")
|
141
141
|
|
@@ -150,6 +150,12 @@ module Liquid
|
|
150
150
|
end
|
151
151
|
end
|
152
152
|
|
153
|
+
def deprecated_default_context
|
154
|
+
warn("DEPRECATION WARNING: Condition#evaluate without a context argument is deprecated" \
|
155
|
+
" and will be removed from Liquid 6.0.0.")
|
156
|
+
Context.new
|
157
|
+
end
|
158
|
+
|
153
159
|
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
154
160
|
def children
|
155
161
|
[
|
data/lib/liquid/context.rb
CHANGED
data/lib/liquid/expression.rb
CHANGED
@@ -10,21 +10,23 @@ module Liquid
|
|
10
10
|
'empty' => ''
|
11
11
|
}.freeze
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
INTEGERS_REGEX = /\A\s*(-?\d+)\s*\z/
|
16
|
-
FLOATS_REGEX = /\A\s*(-?\d[\d\.]+)\s*\z/
|
13
|
+
INTEGERS_REGEX = /\A(-?\d+)\z/
|
14
|
+
FLOATS_REGEX = /\A(-?\d[\d\.]+)\z/
|
17
15
|
|
18
16
|
# Use an atomic group (?>...) to avoid pathological backtracing from
|
19
17
|
# malicious input as described in https://github.com/Shopify/liquid/issues/1357
|
20
|
-
RANGES_REGEX = /\A\
|
18
|
+
RANGES_REGEX = /\A\(\s*(?>(\S+)\s*\.\.)\s*(\S+)\s*\)\z/
|
21
19
|
|
22
20
|
def self.parse(markup)
|
21
|
+
return nil unless markup
|
22
|
+
|
23
|
+
markup = markup.strip
|
24
|
+
if (markup.start_with?('"') && markup.end_with?('"')) ||
|
25
|
+
(markup.start_with?("'") && markup.end_with?("'"))
|
26
|
+
return markup[1..-2]
|
27
|
+
end
|
28
|
+
|
23
29
|
case markup
|
24
|
-
when nil
|
25
|
-
nil
|
26
|
-
when SINGLE_QUOTED_STRING, DOUBLE_QUOTED_STRING
|
27
|
-
Regexp.last_match(1)
|
28
30
|
when INTEGERS_REGEX
|
29
31
|
Regexp.last_match(1).to_i
|
30
32
|
when RANGES_REGEX
|
@@ -32,7 +34,6 @@ module Liquid
|
|
32
34
|
when FLOATS_REGEX
|
33
35
|
Regexp.last_match(1).to_f
|
34
36
|
else
|
35
|
-
markup = markup.strip
|
36
37
|
if LITERALS.key?(markup)
|
37
38
|
LITERALS[markup]
|
38
39
|
else
|
data/lib/liquid/range_lookup.rb
CHANGED
@@ -12,6 +12,8 @@ module Liquid
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
+
attr_reader :start_obj, :end_obj
|
16
|
+
|
15
17
|
def initialize(start_obj, end_obj)
|
16
18
|
@start_obj = start_obj
|
17
19
|
@end_obj = end_obj
|
@@ -35,5 +37,11 @@ module Liquid
|
|
35
37
|
Utils.to_integer(input)
|
36
38
|
end
|
37
39
|
end
|
40
|
+
|
41
|
+
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
42
|
+
def children
|
43
|
+
[@node.start_obj, @node.end_obj]
|
44
|
+
end
|
45
|
+
end
|
38
46
|
end
|
39
47
|
end
|
@@ -1,10 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'cgi'
|
4
|
+
require 'base64'
|
4
5
|
require 'bigdecimal'
|
5
6
|
|
6
7
|
module Liquid
|
7
8
|
module StandardFilters
|
9
|
+
MAX_INT = (1 << 31) - 1
|
8
10
|
HTML_ESCAPE = {
|
9
11
|
'&' => '&',
|
10
12
|
'>' => '>',
|
@@ -62,6 +64,26 @@ module Liquid
|
|
62
64
|
result
|
63
65
|
end
|
64
66
|
|
67
|
+
def base64_encode(input)
|
68
|
+
Base64.strict_encode64(input.to_s)
|
69
|
+
end
|
70
|
+
|
71
|
+
def base64_decode(input)
|
72
|
+
Base64.strict_decode64(input.to_s)
|
73
|
+
rescue ::ArgumentError
|
74
|
+
raise Liquid::ArgumentError, "invalid base64 provided to base64_decode"
|
75
|
+
end
|
76
|
+
|
77
|
+
def base64_url_safe_encode(input)
|
78
|
+
Base64.urlsafe_encode64(input.to_s)
|
79
|
+
end
|
80
|
+
|
81
|
+
def base64_url_safe_decode(input)
|
82
|
+
Base64.urlsafe_decode64(input.to_s)
|
83
|
+
rescue ::ArgumentError
|
84
|
+
raise Liquid::ArgumentError, "invalid base64 provided to base64_url_safe_decode"
|
85
|
+
end
|
86
|
+
|
65
87
|
def slice(input, offset, length = nil)
|
66
88
|
offset = Utils.to_integer(offset)
|
67
89
|
length = length ? Utils.to_integer(length) : 1
|
@@ -93,7 +115,13 @@ module Liquid
|
|
93
115
|
words = Utils.to_integer(words)
|
94
116
|
words = 1 if words <= 0
|
95
117
|
|
96
|
-
wordlist =
|
118
|
+
wordlist = begin
|
119
|
+
input.split(" ", words + 1)
|
120
|
+
rescue RangeError
|
121
|
+
raise if words + 1 < MAX_INT
|
122
|
+
# e.g. integer #{words} too big to convert to `int'
|
123
|
+
raise Liquid::ArgumentError, "integer #{words} too big for truncatewords"
|
124
|
+
end
|
97
125
|
return input if wordlist.length <= words
|
98
126
|
|
99
127
|
wordlist.pop
|
@@ -185,17 +213,23 @@ module Liquid
|
|
185
213
|
|
186
214
|
if ary.empty?
|
187
215
|
[]
|
188
|
-
elsif
|
189
|
-
|
190
|
-
|
216
|
+
elsif target_value.nil?
|
217
|
+
ary.select do |item|
|
218
|
+
item[property]
|
191
219
|
rescue TypeError
|
192
220
|
raise_property_error(property)
|
221
|
+
rescue NoMethodError
|
222
|
+
return nil unless item.respond_to?(:[])
|
223
|
+
raise
|
193
224
|
end
|
194
|
-
|
195
|
-
|
196
|
-
|
225
|
+
else
|
226
|
+
ary.select do |item|
|
227
|
+
item[property] == target_value
|
197
228
|
rescue TypeError
|
198
229
|
raise_property_error(property)
|
230
|
+
rescue NoMethodError
|
231
|
+
return nil unless item.respond_to?(:[])
|
232
|
+
raise
|
199
233
|
end
|
200
234
|
end
|
201
235
|
end
|
@@ -209,11 +243,14 @@ module Liquid
|
|
209
243
|
ary.uniq
|
210
244
|
elsif ary.empty? # The next two cases assume a non-empty array.
|
211
245
|
[]
|
212
|
-
|
213
|
-
|
214
|
-
|
246
|
+
else
|
247
|
+
ary.uniq do |item|
|
248
|
+
item[property]
|
215
249
|
rescue TypeError
|
216
250
|
raise_property_error(property)
|
251
|
+
rescue NoMethodError
|
252
|
+
return nil unless item.respond_to?(:[])
|
253
|
+
raise
|
217
254
|
end
|
218
255
|
end
|
219
256
|
end
|
@@ -249,11 +286,14 @@ module Liquid
|
|
249
286
|
ary.compact
|
250
287
|
elsif ary.empty? # The next two cases assume a non-empty array.
|
251
288
|
[]
|
252
|
-
|
253
|
-
|
254
|
-
|
289
|
+
else
|
290
|
+
ary.reject do |item|
|
291
|
+
item[property].nil?
|
255
292
|
rescue TypeError
|
256
293
|
raise_property_error(property)
|
294
|
+
rescue NoMethodError
|
295
|
+
return nil unless item.respond_to?(:[])
|
296
|
+
raise
|
257
297
|
end
|
258
298
|
end
|
259
299
|
end
|
@@ -268,14 +308,34 @@ module Liquid
|
|
268
308
|
input.to_s.sub(string.to_s, replacement.to_s)
|
269
309
|
end
|
270
310
|
|
311
|
+
# Replace the last occurrences of a string with another
|
312
|
+
def replace_last(input, string, replacement)
|
313
|
+
input = input.to_s
|
314
|
+
string = string.to_s
|
315
|
+
replacement = replacement.to_s
|
316
|
+
|
317
|
+
start_index = input.rindex(string)
|
318
|
+
|
319
|
+
return input unless start_index
|
320
|
+
|
321
|
+
output = input.dup
|
322
|
+
output[start_index, string.length] = replacement
|
323
|
+
output
|
324
|
+
end
|
325
|
+
|
271
326
|
# remove a substring
|
272
327
|
def remove(input, string)
|
273
|
-
input
|
328
|
+
replace(input, string, '')
|
274
329
|
end
|
275
330
|
|
276
331
|
# remove the first occurrences of a substring
|
277
332
|
def remove_first(input, string)
|
278
|
-
input
|
333
|
+
replace_first(input, string, '')
|
334
|
+
end
|
335
|
+
|
336
|
+
# remove the last occurences of a substring
|
337
|
+
def remove_last(input, string)
|
338
|
+
replace_last(input, string, '')
|
279
339
|
end
|
280
340
|
|
281
341
|
# add one string to another
|
@@ -440,7 +500,7 @@ module Liquid
|
|
440
500
|
#
|
441
501
|
def default(input, default_value = '', options = {})
|
442
502
|
options = {} unless options.is_a?(Hash)
|
443
|
-
false_check = options['allow_false'] ? input.nil? : !input
|
503
|
+
false_check = options['allow_false'] ? input.nil? : !Liquid::Utils.to_liquid_value(input)
|
444
504
|
false_check || (input.respond_to?(:empty?) && input.empty?) ? default_value : input
|
445
505
|
end
|
446
506
|
|
@@ -458,10 +518,16 @@ module Liquid
|
|
458
518
|
end
|
459
519
|
|
460
520
|
def nil_safe_compare(a, b)
|
461
|
-
|
462
|
-
|
521
|
+
result = a <=> b
|
522
|
+
|
523
|
+
if result
|
524
|
+
result
|
525
|
+
elsif a.nil?
|
526
|
+
1
|
527
|
+
elsif b.nil?
|
528
|
+
-1
|
463
529
|
else
|
464
|
-
|
530
|
+
raise Liquid::ArgumentError, "cannot sort values of incompatible types"
|
465
531
|
end
|
466
532
|
end
|
467
533
|
|
@@ -516,8 +582,9 @@ module Liquid
|
|
516
582
|
|
517
583
|
def each
|
518
584
|
@input.each do |e|
|
585
|
+
e = e.respond_to?(:to_liquid) ? e.to_liquid : e
|
519
586
|
e.context = @context if e.respond_to?(:context=)
|
520
|
-
yield(e
|
587
|
+
yield(e)
|
521
588
|
end
|
522
589
|
end
|
523
590
|
end
|
@@ -31,7 +31,11 @@ module Liquid
|
|
31
31
|
if @registers.key?(key)
|
32
32
|
@registers.fetch(key)
|
33
33
|
elsif default != UNDEFINED
|
34
|
-
|
34
|
+
if block_given?
|
35
|
+
@static.fetch(key, &block)
|
36
|
+
else
|
37
|
+
@static.fetch(key, default)
|
38
|
+
end
|
35
39
|
else
|
36
40
|
@static.fetch(key, &block)
|
37
41
|
end
|
@@ -7,25 +7,26 @@ module Liquid
|
|
7
7
|
|
8
8
|
def add_global_filter(filter)
|
9
9
|
strainer_class_cache.clear
|
10
|
-
|
10
|
+
GlobalCache.add_filter(filter)
|
11
11
|
end
|
12
12
|
|
13
13
|
def create(context, filters = [])
|
14
14
|
strainer_from_cache(filters).new(context)
|
15
15
|
end
|
16
16
|
|
17
|
-
|
17
|
+
GlobalCache = Class.new(StrainerTemplate)
|
18
18
|
|
19
|
-
|
20
|
-
@global_filters ||= []
|
21
|
-
end
|
19
|
+
private
|
22
20
|
|
23
21
|
def strainer_from_cache(filters)
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
filters
|
28
|
-
|
22
|
+
if filters.empty?
|
23
|
+
GlobalCache
|
24
|
+
else
|
25
|
+
strainer_class_cache[filters] ||= begin
|
26
|
+
klass = Class.new(GlobalCache)
|
27
|
+
filters.each { |f| klass.add_filter(f) }
|
28
|
+
klass
|
29
|
+
end
|
29
30
|
end
|
30
31
|
end
|
31
32
|
|
data/lib/liquid/tags/case.rb
CHANGED
@@ -52,7 +52,14 @@ module Liquid
|
|
52
52
|
@blocks.each do |block|
|
53
53
|
if block.else?
|
54
54
|
block.attachment.render_to_output_buffer(context, output) if execute_else_block
|
55
|
-
|
55
|
+
next
|
56
|
+
end
|
57
|
+
|
58
|
+
result = Liquid::Utils.to_liquid_value(
|
59
|
+
block.evaluate(context)
|
60
|
+
)
|
61
|
+
|
62
|
+
if result
|
56
63
|
execute_else_block = false
|
57
64
|
block.attachment.render_to_output_buffer(context, output)
|
58
65
|
end
|
data/lib/liquid/tags/if.rb
CHANGED
@@ -50,7 +50,11 @@ module Liquid
|
|
50
50
|
|
51
51
|
def render_to_output_buffer(context, output)
|
52
52
|
@blocks.each do |block|
|
53
|
-
|
53
|
+
result = Liquid::Utils.to_liquid_value(
|
54
|
+
block.evaluate(context)
|
55
|
+
)
|
56
|
+
|
57
|
+
if result
|
54
58
|
return block.attachment.render_to_output_buffer(context, output)
|
55
59
|
end
|
56
60
|
end
|
data/lib/liquid/tags/unless.rb
CHANGED
@@ -11,13 +11,21 @@ module Liquid
|
|
11
11
|
def render_to_output_buffer(context, output)
|
12
12
|
# First condition is interpreted backwards ( if not )
|
13
13
|
first_block = @blocks.first
|
14
|
-
|
14
|
+
result = Liquid::Utils.to_liquid_value(
|
15
|
+
first_block.evaluate(context)
|
16
|
+
)
|
17
|
+
|
18
|
+
unless result
|
15
19
|
return first_block.attachment.render_to_output_buffer(context, output)
|
16
20
|
end
|
17
21
|
|
18
22
|
# After the first condition unless works just like if
|
19
23
|
@blocks[1..-1].each do |block|
|
20
|
-
|
24
|
+
result = Liquid::Utils.to_liquid_value(
|
25
|
+
block.evaluate(context)
|
26
|
+
)
|
27
|
+
|
28
|
+
if result
|
21
29
|
return block.attachment.render_to_output_buffer(context, output)
|
22
30
|
end
|
23
31
|
end
|
data/lib/liquid/tokenizer.rb
CHANGED
@@ -5,7 +5,7 @@ module Liquid
|
|
5
5
|
attr_reader :line_number, :for_liquid_tag
|
6
6
|
|
7
7
|
def initialize(source, line_numbers = false, line_number: nil, for_liquid_tag: false)
|
8
|
-
@source = source
|
8
|
+
@source = source.to_s.to_str
|
9
9
|
@line_number = line_number || (line_numbers ? 1 : nil)
|
10
10
|
@for_liquid_tag = for_liquid_tag
|
11
11
|
@tokens = tokenize
|
@@ -24,7 +24,7 @@ module Liquid
|
|
24
24
|
private
|
25
25
|
|
26
26
|
def tokenize
|
27
|
-
return [] if @source.
|
27
|
+
return [] if @source.empty?
|
28
28
|
|
29
29
|
return @source.split("\n") if @for_liquid_tag
|
30
30
|
|
data/lib/liquid/utils.rb
CHANGED
@@ -81,5 +81,13 @@ module Liquid
|
|
81
81
|
rescue ::ArgumentError
|
82
82
|
nil
|
83
83
|
end
|
84
|
+
|
85
|
+
def self.to_liquid_value(obj)
|
86
|
+
# Enable "obj" to represent itself as a primitive value like integer, string, or boolean
|
87
|
+
return obj.to_liquid_value if obj.respond_to?(:to_liquid_value)
|
88
|
+
|
89
|
+
# Otherwise return the object itself
|
90
|
+
obj
|
91
|
+
end
|
84
92
|
end
|
85
93
|
end
|
@@ -40,6 +40,9 @@ module Liquid
|
|
40
40
|
@lookups.each_index do |i|
|
41
41
|
key = context.evaluate(@lookups[i])
|
42
42
|
|
43
|
+
# Cast "key" to its liquid value to enable it to act as a primitive value
|
44
|
+
key = Liquid::Utils.to_liquid_value(key)
|
45
|
+
|
43
46
|
# If object is a hash- or array-like object we look for the
|
44
47
|
# presence of the key and if its available we return it
|
45
48
|
if object.respond_to?(:[]) &&
|
data/lib/liquid/version.rb
CHANGED
data/lib/liquid.rb
CHANGED
@@ -36,7 +36,7 @@ module Liquid
|
|
36
36
|
VariableIncompleteEnd = /\}\}?/
|
37
37
|
QuotedString = /"[^"]*"|'[^']*'/
|
38
38
|
QuotedFragment = /#{QuotedString}|(?:[^\s,\|'"]|#{QuotedString})+/o
|
39
|
-
TagAttributes = /(\w
|
39
|
+
TagAttributes = /(\w[\w-]*)\s*\:\s*(#{QuotedFragment})/o
|
40
40
|
AnyStartingTag = /#{TagStart}|#{VariableStart}/o
|
41
41
|
PartialTemplateParser = /#{TagStart}.*?#{TagEnd}|#{VariableStart}.*?#{VariableIncompleteEnd}/om
|
42
42
|
TemplateParser = /(#{PartialTemplateParser}|#{AnyStartingTag})/om
|
@@ -59,8 +59,8 @@ require 'liquid/forloop_drop'
|
|
59
59
|
require 'liquid/extensions'
|
60
60
|
require 'liquid/errors'
|
61
61
|
require 'liquid/interrupts'
|
62
|
-
require 'liquid/strainer_factory'
|
63
62
|
require 'liquid/strainer_template'
|
63
|
+
require 'liquid/strainer_factory'
|
64
64
|
require 'liquid/expression'
|
65
65
|
require 'liquid/context'
|
66
66
|
require 'liquid/parser_switching'
|
@@ -24,7 +24,7 @@ class ContextSensitiveDrop < Liquid::Drop
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
-
class Category
|
27
|
+
class Category
|
28
28
|
attr_accessor :name
|
29
29
|
|
30
30
|
def initialize(name)
|
@@ -36,8 +36,9 @@ class Category < Liquid::Drop
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
-
class CategoryDrop
|
39
|
+
class CategoryDrop < Liquid::Drop
|
40
40
|
attr_accessor :category, :context
|
41
|
+
|
41
42
|
def initialize(category)
|
42
43
|
@category = category
|
43
44
|
end
|
@@ -405,45 +406,42 @@ class ContextTest < Minitest::Test
|
|
405
406
|
end
|
406
407
|
|
407
408
|
def test_lambda_is_called_once
|
409
|
+
@global = 0
|
410
|
+
|
408
411
|
@context['callcount'] = proc {
|
409
|
-
@global
|
410
|
-
@global += 1
|
412
|
+
@global += 1
|
411
413
|
@global.to_s
|
412
414
|
}
|
413
415
|
|
414
416
|
assert_equal('1', @context['callcount'])
|
415
417
|
assert_equal('1', @context['callcount'])
|
416
418
|
assert_equal('1', @context['callcount'])
|
417
|
-
|
418
|
-
@global = nil
|
419
419
|
end
|
420
420
|
|
421
421
|
def test_nested_lambda_is_called_once
|
422
|
+
@global = 0
|
423
|
+
|
422
424
|
@context['callcount'] = { "lambda" => proc {
|
423
|
-
@global
|
424
|
-
@global += 1
|
425
|
+
@global += 1
|
425
426
|
@global.to_s
|
426
427
|
} }
|
427
428
|
|
428
429
|
assert_equal('1', @context['callcount.lambda'])
|
429
430
|
assert_equal('1', @context['callcount.lambda'])
|
430
431
|
assert_equal('1', @context['callcount.lambda'])
|
431
|
-
|
432
|
-
@global = nil
|
433
432
|
end
|
434
433
|
|
435
434
|
def test_lambda_in_array_is_called_once
|
435
|
+
@global = 0
|
436
|
+
|
436
437
|
@context['callcount'] = [1, 2, proc {
|
437
|
-
@global
|
438
|
-
@global += 1
|
438
|
+
@global += 1
|
439
439
|
@global.to_s
|
440
440
|
}, 4, 5]
|
441
441
|
|
442
442
|
assert_equal('1', @context['callcount[2]'])
|
443
443
|
assert_equal('1', @context['callcount[2]'])
|
444
444
|
assert_equal('1', @context['callcount[2]'])
|
445
|
-
|
446
|
-
@global = nil
|
447
445
|
end
|
448
446
|
|
449
447
|
def test_access_to_context_from_proc
|