liquid 3.0.0 → 4.0.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 +130 -62
- data/README.md +31 -0
- data/lib/liquid/block.rb +31 -124
- data/lib/liquid/block_body.rb +75 -59
- data/lib/liquid/condition.rb +23 -22
- data/lib/liquid/context.rb +51 -60
- data/lib/liquid/document.rb +19 -9
- data/lib/liquid/drop.rb +17 -16
- data/lib/liquid/errors.rb +20 -24
- data/lib/liquid/expression.rb +15 -3
- data/lib/liquid/extensions.rb +13 -7
- data/lib/liquid/file_system.rb +11 -11
- data/lib/liquid/forloop_drop.rb +42 -0
- data/lib/liquid/i18n.rb +5 -5
- data/lib/liquid/interrupts.rb +1 -2
- data/lib/liquid/lexer.rb +6 -4
- data/lib/liquid/locales/en.yml +5 -1
- data/lib/liquid/parse_context.rb +37 -0
- data/lib/liquid/parser.rb +1 -1
- data/lib/liquid/parser_switching.rb +4 -4
- data/lib/liquid/profiler/hooks.rb +7 -7
- data/lib/liquid/profiler.rb +18 -19
- data/lib/liquid/range_lookup.rb +16 -1
- data/lib/liquid/resource_limits.rb +23 -0
- data/lib/liquid/standardfilters.rb +121 -61
- data/lib/liquid/strainer.rb +32 -29
- data/lib/liquid/tablerowloop_drop.rb +62 -0
- data/lib/liquid/tag.rb +9 -8
- data/lib/liquid/tags/assign.rb +17 -4
- data/lib/liquid/tags/break.rb +0 -3
- data/lib/liquid/tags/capture.rb +2 -2
- data/lib/liquid/tags/case.rb +19 -12
- data/lib/liquid/tags/comment.rb +2 -2
- data/lib/liquid/tags/cycle.rb +6 -6
- data/lib/liquid/tags/decrement.rb +1 -4
- data/lib/liquid/tags/for.rb +95 -75
- data/lib/liquid/tags/if.rb +48 -43
- data/lib/liquid/tags/ifchanged.rb +0 -2
- data/lib/liquid/tags/include.rb +61 -52
- data/lib/liquid/tags/raw.rb +32 -4
- data/lib/liquid/tags/table_row.rb +12 -31
- data/lib/liquid/tags/unless.rb +4 -5
- data/lib/liquid/template.rb +42 -54
- data/lib/liquid/tokenizer.rb +31 -0
- data/lib/liquid/utils.rb +52 -8
- data/lib/liquid/variable.rb +46 -45
- data/lib/liquid/variable_lookup.rb +9 -5
- data/lib/liquid/version.rb +1 -1
- data/lib/liquid.rb +9 -7
- data/test/integration/assign_test.rb +18 -8
- data/test/integration/blank_test.rb +14 -14
- data/test/integration/capture_test.rb +10 -0
- data/test/integration/context_test.rb +2 -2
- data/test/integration/document_test.rb +19 -0
- data/test/integration/drop_test.rb +42 -40
- data/test/integration/error_handling_test.rb +99 -46
- data/test/integration/filter_test.rb +72 -19
- data/test/integration/hash_ordering_test.rb +9 -9
- data/test/integration/output_test.rb +34 -27
- data/test/integration/parsing_quirks_test.rb +15 -13
- data/test/integration/render_profiling_test.rb +20 -20
- data/test/integration/security_test.rb +9 -7
- data/test/integration/standard_filter_test.rb +198 -42
- data/test/integration/tags/break_tag_test.rb +1 -2
- data/test/integration/tags/continue_tag_test.rb +0 -1
- data/test/integration/tags/for_tag_test.rb +133 -98
- data/test/integration/tags/if_else_tag_test.rb +96 -77
- data/test/integration/tags/include_tag_test.rb +34 -30
- data/test/integration/tags/increment_tag_test.rb +10 -11
- data/test/integration/tags/raw_tag_test.rb +7 -1
- data/test/integration/tags/standard_tag_test.rb +121 -122
- data/test/integration/tags/statements_test.rb +3 -5
- data/test/integration/tags/table_row_test.rb +20 -19
- data/test/integration/tags/unless_else_tag_test.rb +6 -6
- data/test/integration/template_test.rb +190 -49
- data/test/integration/trim_mode_test.rb +525 -0
- data/test/integration/variable_test.rb +23 -13
- data/test/test_helper.rb +44 -9
- data/test/unit/block_unit_test.rb +8 -5
- data/test/unit/condition_unit_test.rb +86 -77
- data/test/unit/context_unit_test.rb +48 -57
- data/test/unit/file_system_unit_test.rb +3 -3
- data/test/unit/i18n_unit_test.rb +2 -2
- data/test/unit/lexer_unit_test.rb +11 -8
- data/test/unit/parser_unit_test.rb +2 -2
- data/test/unit/regexp_unit_test.rb +1 -1
- data/test/unit/strainer_unit_test.rb +85 -8
- data/test/unit/tag_unit_test.rb +7 -2
- data/test/unit/tags/case_tag_unit_test.rb +1 -1
- data/test/unit/tags/for_tag_unit_test.rb +2 -2
- data/test/unit/tags/if_tag_unit_test.rb +1 -1
- data/test/unit/template_unit_test.rb +14 -5
- data/test/unit/tokenizer_unit_test.rb +24 -7
- data/test/unit/variable_unit_test.rb +66 -43
- metadata +55 -50
- data/lib/liquid/module_ex.rb +0 -62
- data/lib/liquid/token.rb +0 -18
- data/test/unit/module_ex_unit_test.rb +0 -87
- /data/{MIT-LICENSE → LICENSE} +0 -0
@@ -2,7 +2,6 @@ require 'cgi'
|
|
2
2
|
require 'bigdecimal'
|
3
3
|
|
4
4
|
module Liquid
|
5
|
-
|
6
5
|
module StandardFilters
|
7
6
|
HTML_ESCAPE = {
|
8
7
|
'&'.freeze => '&'.freeze,
|
@@ -34,7 +33,7 @@ module Liquid
|
|
34
33
|
end
|
35
34
|
|
36
35
|
def escape(input)
|
37
|
-
CGI.escapeHTML(input).untaint
|
36
|
+
CGI.escapeHTML(input).untaint unless input.nil?
|
38
37
|
end
|
39
38
|
alias_method :h, :escape
|
40
39
|
|
@@ -43,12 +42,16 @@ module Liquid
|
|
43
42
|
end
|
44
43
|
|
45
44
|
def url_encode(input)
|
46
|
-
CGI.escape(input)
|
45
|
+
CGI.escape(input) unless input.nil?
|
46
|
+
end
|
47
|
+
|
48
|
+
def url_decode(input)
|
49
|
+
CGI.unescape(input) unless input.nil?
|
47
50
|
end
|
48
51
|
|
49
|
-
def slice(input, offset, length=nil)
|
50
|
-
offset =
|
51
|
-
length = length ?
|
52
|
+
def slice(input, offset, length = nil)
|
53
|
+
offset = Utils.to_integer(offset)
|
54
|
+
length = length ? Utils.to_integer(length) : 1
|
52
55
|
|
53
56
|
if input.is_a?(Array)
|
54
57
|
input.slice(offset, length) || []
|
@@ -59,18 +62,22 @@ module Liquid
|
|
59
62
|
|
60
63
|
# Truncate a string down to x characters
|
61
64
|
def truncate(input, length = 50, truncate_string = "...".freeze)
|
62
|
-
if input.nil?
|
63
|
-
|
65
|
+
return if input.nil?
|
66
|
+
input_str = input.to_s
|
67
|
+
length = Utils.to_integer(length)
|
68
|
+
truncate_string_str = truncate_string.to_s
|
69
|
+
l = length - truncate_string_str.length
|
64
70
|
l = 0 if l < 0
|
65
|
-
|
71
|
+
input_str.length > length ? input_str[0...l] + truncate_string_str : input_str
|
66
72
|
end
|
67
73
|
|
68
74
|
def truncatewords(input, words = 15, truncate_string = "...".freeze)
|
69
|
-
if input.nil?
|
75
|
+
return if input.nil?
|
70
76
|
wordlist = input.to_s.split
|
71
|
-
|
77
|
+
words = Utils.to_integer(words)
|
78
|
+
l = words - 1
|
72
79
|
l = 0 if l < 0
|
73
|
-
wordlist.length > l ? wordlist[0..l].join(" ".freeze) + truncate_string : input
|
80
|
+
wordlist.length > l ? wordlist[0..l].join(" ".freeze) + truncate_string.to_s : input
|
74
81
|
end
|
75
82
|
|
76
83
|
# Split input string into an array of substrings separated by given pattern.
|
@@ -79,7 +86,7 @@ module Liquid
|
|
79
86
|
# <div class="summary">{{ post | split '//' | first }}</div>
|
80
87
|
#
|
81
88
|
def split(input, pattern)
|
82
|
-
input.to_s.split(pattern)
|
89
|
+
input.to_s.split(pattern.to_s)
|
83
90
|
end
|
84
91
|
|
85
92
|
def strip(input)
|
@@ -115,10 +122,32 @@ module Liquid
|
|
115
122
|
ary = InputIterator.new(input)
|
116
123
|
if property.nil?
|
117
124
|
ary.sort
|
125
|
+
elsif ary.empty? # The next two cases assume a non-empty array.
|
126
|
+
[]
|
118
127
|
elsif ary.first.respond_to?(:[]) && !ary.first[property].nil?
|
119
|
-
ary.sort
|
120
|
-
|
121
|
-
|
128
|
+
ary.sort do |a, b|
|
129
|
+
a = a[property]
|
130
|
+
b = b[property]
|
131
|
+
if a && b
|
132
|
+
a <=> b
|
133
|
+
else
|
134
|
+
a ? -1 : 1
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Sort elements of an array ignoring case if strings
|
141
|
+
# provide optional property with which to sort an array of hashes or drops
|
142
|
+
def sort_natural(input, property = nil)
|
143
|
+
ary = InputIterator.new(input)
|
144
|
+
|
145
|
+
if property.nil?
|
146
|
+
ary.sort { |a, b| a.casecmp(b) }
|
147
|
+
elsif ary.empty? # The next two cases assume a non-empty array.
|
148
|
+
[]
|
149
|
+
elsif ary.first.respond_to?(:[]) && !ary.first[property].nil?
|
150
|
+
ary.sort { |a, b| a[property].casecmp(b[property]) }
|
122
151
|
end
|
123
152
|
end
|
124
153
|
|
@@ -126,10 +155,13 @@ module Liquid
|
|
126
155
|
# provide optional property with which to determine uniqueness
|
127
156
|
def uniq(input, property = nil)
|
128
157
|
ary = InputIterator.new(input)
|
158
|
+
|
129
159
|
if property.nil?
|
130
|
-
|
131
|
-
elsif
|
132
|
-
|
160
|
+
ary.uniq
|
161
|
+
elsif ary.empty? # The next two cases assume a non-empty array.
|
162
|
+
[]
|
163
|
+
elsif ary.first.respond_to?(:[])
|
164
|
+
ary.uniq{ |a| a[property] }
|
133
165
|
end
|
134
166
|
end
|
135
167
|
|
@@ -147,29 +179,44 @@ module Liquid
|
|
147
179
|
if property == "to_liquid".freeze
|
148
180
|
e
|
149
181
|
elsif e.respond_to?(:[])
|
150
|
-
e[property]
|
182
|
+
r = e[property]
|
183
|
+
r.is_a?(Proc) ? r.call : r
|
151
184
|
end
|
152
185
|
end
|
153
186
|
end
|
154
187
|
|
188
|
+
# Remove nils within an array
|
189
|
+
# provide optional property with which to check for nil
|
190
|
+
def compact(input, property = nil)
|
191
|
+
ary = InputIterator.new(input)
|
192
|
+
|
193
|
+
if property.nil?
|
194
|
+
ary.compact
|
195
|
+
elsif ary.empty? # The next two cases assume a non-empty array.
|
196
|
+
[]
|
197
|
+
elsif ary.first.respond_to?(:[])
|
198
|
+
ary.reject{ |a| a[property].nil? }
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
155
202
|
# Replace occurrences of a string with another
|
156
203
|
def replace(input, string, replacement = ''.freeze)
|
157
|
-
input.to_s.gsub(string, replacement.to_s)
|
204
|
+
input.to_s.gsub(string.to_s, replacement.to_s)
|
158
205
|
end
|
159
206
|
|
160
207
|
# Replace the first occurrences of a string with another
|
161
208
|
def replace_first(input, string, replacement = ''.freeze)
|
162
|
-
input.to_s.sub(string, replacement.to_s)
|
209
|
+
input.to_s.sub(string.to_s, replacement.to_s)
|
163
210
|
end
|
164
211
|
|
165
212
|
# remove a substring
|
166
213
|
def remove(input, string)
|
167
|
-
input.to_s.gsub(string, ''.freeze)
|
214
|
+
input.to_s.gsub(string.to_s, ''.freeze)
|
168
215
|
end
|
169
216
|
|
170
217
|
# remove the first occurrences of a substring
|
171
218
|
def remove_first(input, string)
|
172
|
-
input.to_s.sub(string, ''.freeze)
|
219
|
+
input.to_s.sub(string.to_s, ''.freeze)
|
173
220
|
end
|
174
221
|
|
175
222
|
# add one string to another
|
@@ -177,6 +224,13 @@ module Liquid
|
|
177
224
|
input.to_s + string.to_s
|
178
225
|
end
|
179
226
|
|
227
|
+
def concat(input, array)
|
228
|
+
unless array.respond_to?(:to_ary)
|
229
|
+
raise ArgumentError.new("concat filter requires an array argument")
|
230
|
+
end
|
231
|
+
InputIterator.new(input).concat(array)
|
232
|
+
end
|
233
|
+
|
180
234
|
# prepend a string to another
|
181
235
|
def prepend(input, string)
|
182
236
|
string.to_s + input.to_s
|
@@ -221,7 +275,7 @@ module Liquid
|
|
221
275
|
def date(input, format)
|
222
276
|
return input if format.to_s.empty?
|
223
277
|
|
224
|
-
return input unless date = to_date(input)
|
278
|
+
return input unless date = Utils.to_date(input)
|
225
279
|
|
226
280
|
date.strftime(format.to_s)
|
227
281
|
end
|
@@ -244,6 +298,12 @@ module Liquid
|
|
244
298
|
array.last if array.respond_to?(:last)
|
245
299
|
end
|
246
300
|
|
301
|
+
# absolute value
|
302
|
+
def abs(input)
|
303
|
+
result = Utils.to_number(input).abs
|
304
|
+
result.is_a?(BigDecimal) ? result.to_f : result
|
305
|
+
end
|
306
|
+
|
247
307
|
# addition
|
248
308
|
def plus(input, operand)
|
249
309
|
apply_operation(input, operand, :+)
|
@@ -262,66 +322,49 @@ module Liquid
|
|
262
322
|
# division
|
263
323
|
def divided_by(input, operand)
|
264
324
|
apply_operation(input, operand, :/)
|
325
|
+
rescue ::ZeroDivisionError => e
|
326
|
+
raise Liquid::ZeroDivisionError, e.message
|
265
327
|
end
|
266
328
|
|
267
329
|
def modulo(input, operand)
|
268
330
|
apply_operation(input, operand, :%)
|
331
|
+
rescue ::ZeroDivisionError => e
|
332
|
+
raise Liquid::ZeroDivisionError, e.message
|
269
333
|
end
|
270
334
|
|
271
335
|
def round(input, n = 0)
|
272
|
-
result = to_number(input).round(to_number(n))
|
336
|
+
result = Utils.to_number(input).round(Utils.to_number(n))
|
273
337
|
result = result.to_f if result.is_a?(BigDecimal)
|
274
338
|
result = result.to_i if n == 0
|
275
339
|
result
|
340
|
+
rescue ::FloatDomainError => e
|
341
|
+
raise Liquid::FloatDomainError, e.message
|
276
342
|
end
|
277
343
|
|
278
344
|
def ceil(input)
|
279
|
-
to_number(input).ceil.to_i
|
345
|
+
Utils.to_number(input).ceil.to_i
|
346
|
+
rescue ::FloatDomainError => e
|
347
|
+
raise Liquid::FloatDomainError, e.message
|
280
348
|
end
|
281
349
|
|
282
350
|
def floor(input)
|
283
|
-
to_number(input).floor.to_i
|
284
|
-
|
285
|
-
|
286
|
-
def default(input, default_value = "".freeze)
|
287
|
-
is_blank = input.respond_to?(:empty?) ? input.empty? : !input
|
288
|
-
is_blank ? default_value : input
|
351
|
+
Utils.to_number(input).floor.to_i
|
352
|
+
rescue ::FloatDomainError => e
|
353
|
+
raise Liquid::FloatDomainError, e.message
|
289
354
|
end
|
290
355
|
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
case obj
|
295
|
-
when Float
|
296
|
-
BigDecimal.new(obj.to_s)
|
297
|
-
when Numeric
|
298
|
-
obj
|
299
|
-
when String
|
300
|
-
(obj.strip =~ /\A\d+\.\d+\z/) ? BigDecimal.new(obj) : obj.to_i
|
356
|
+
def default(input, default_value = ''.freeze)
|
357
|
+
if !input || input.respond_to?(:empty?) && input.empty?
|
358
|
+
default_value
|
301
359
|
else
|
302
|
-
|
360
|
+
input
|
303
361
|
end
|
304
362
|
end
|
305
363
|
|
306
|
-
|
307
|
-
return obj if obj.respond_to?(:strftime)
|
308
|
-
|
309
|
-
case obj
|
310
|
-
when 'now'.freeze, 'today'.freeze
|
311
|
-
Time.now
|
312
|
-
when /\A\d+\z/, Integer
|
313
|
-
Time.at(obj.to_i)
|
314
|
-
when String
|
315
|
-
Time.parse(obj)
|
316
|
-
else
|
317
|
-
nil
|
318
|
-
end
|
319
|
-
rescue ArgumentError
|
320
|
-
nil
|
321
|
-
end
|
364
|
+
private
|
322
365
|
|
323
366
|
def apply_operation(input, operand, operation)
|
324
|
-
result = to_number(input).send(operation, to_number(operand))
|
367
|
+
result = Utils.to_number(input).send(operation, Utils.to_number(operand))
|
325
368
|
result.is_a?(BigDecimal) ? result.to_f : result
|
326
369
|
end
|
327
370
|
|
@@ -344,10 +387,27 @@ module Liquid
|
|
344
387
|
to_a.join(glue)
|
345
388
|
end
|
346
389
|
|
390
|
+
def concat(args)
|
391
|
+
to_a.concat(args)
|
392
|
+
end
|
393
|
+
|
347
394
|
def reverse
|
348
395
|
reverse_each.to_a
|
349
396
|
end
|
350
397
|
|
398
|
+
def uniq(&block)
|
399
|
+
to_a.uniq(&block)
|
400
|
+
end
|
401
|
+
|
402
|
+
def compact
|
403
|
+
to_a.compact
|
404
|
+
end
|
405
|
+
|
406
|
+
def empty?
|
407
|
+
@input.each { return false }
|
408
|
+
true
|
409
|
+
end
|
410
|
+
|
351
411
|
def each
|
352
412
|
@input.each do |e|
|
353
413
|
yield(e.respond_to?(:to_liquid) ? e.to_liquid : e)
|
data/lib/liquid/strainer.rb
CHANGED
@@ -1,19 +1,19 @@
|
|
1
1
|
require 'set'
|
2
2
|
|
3
3
|
module Liquid
|
4
|
-
|
5
4
|
# Strainer is the parent class for the filters system.
|
6
5
|
# New filters are mixed into the strainer class which is then instantiated for each liquid template render run.
|
7
6
|
#
|
8
7
|
# The Strainer only allows method calls defined in filters given to it via Strainer.global_filter,
|
9
8
|
# Context#add_filters or Template.register_filter
|
10
9
|
class Strainer #:nodoc:
|
11
|
-
@@
|
12
|
-
|
13
|
-
|
10
|
+
@@global_strainer = Class.new(Strainer) do
|
11
|
+
@filter_methods = Set.new
|
12
|
+
end
|
14
13
|
@@strainer_class_cache = Hash.new do |hash, filters|
|
15
|
-
hash[filters] = Class.new(
|
16
|
-
|
14
|
+
hash[filters] = Class.new(@@global_strainer) do
|
15
|
+
@filter_methods = @@global_strainer.filter_methods.dup
|
16
|
+
filters.each { |f| add_filter(f) }
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
@@ -21,43 +21,46 @@ module Liquid
|
|
21
21
|
@context = context
|
22
22
|
end
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
24
|
+
class << self
|
25
|
+
attr_reader :filter_methods
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.add_filter(filter)
|
29
|
+
raise ArgumentError, "Expected module but got: #{filter.class}" unless filter.is_a?(Module)
|
30
|
+
unless self.class.include?(filter)
|
31
|
+
invokable_non_public_methods = (filter.private_instance_methods + filter.protected_instance_methods).select { |m| invokable?(m) }
|
32
|
+
if invokable_non_public_methods.any?
|
33
|
+
raise MethodOverrideError, "Filter overrides registered public methods as non public: #{invokable_non_public_methods.join(', ')}"
|
34
|
+
else
|
35
|
+
send(:include, filter)
|
36
|
+
@filter_methods.merge(filter.public_instance_methods.map(&:to_s))
|
37
|
+
end
|
37
38
|
end
|
38
39
|
end
|
39
40
|
|
40
|
-
def self.
|
41
|
-
@@strainer_class_cache
|
41
|
+
def self.global_filter(filter)
|
42
|
+
@@strainer_class_cache.clear
|
43
|
+
@@global_strainer.add_filter(filter)
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.invokable?(method)
|
47
|
+
@filter_methods.include?(method.to_s)
|
42
48
|
end
|
43
49
|
|
44
50
|
def self.create(context, filters = [])
|
45
|
-
|
46
|
-
strainer_class_cache[filters].new(context)
|
51
|
+
@@strainer_class_cache[filters].new(context)
|
47
52
|
end
|
48
53
|
|
49
54
|
def invoke(method, *args)
|
50
|
-
if invokable?(method)
|
55
|
+
if self.class.invokable?(method)
|
51
56
|
send(method, *args)
|
57
|
+
elsif @context && @context.strict_filters
|
58
|
+
raise Liquid::UndefinedFilter, "undefined filter #{method}"
|
52
59
|
else
|
53
60
|
args.first
|
54
61
|
end
|
55
62
|
rescue ::ArgumentError => e
|
56
|
-
raise Liquid::ArgumentError.
|
57
|
-
end
|
58
|
-
|
59
|
-
def invokable?(method)
|
60
|
-
@@known_methods.include?(method.to_s) && respond_to?(method)
|
63
|
+
raise Liquid::ArgumentError, e.message, e.backtrace
|
61
64
|
end
|
62
65
|
end
|
63
66
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Liquid
|
2
|
+
class TablerowloopDrop < Drop
|
3
|
+
def initialize(length, cols)
|
4
|
+
@length = length
|
5
|
+
@row = 1
|
6
|
+
@col = 1
|
7
|
+
@cols = cols
|
8
|
+
@index = 0
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :length, :col, :row
|
12
|
+
|
13
|
+
def index
|
14
|
+
@index + 1
|
15
|
+
end
|
16
|
+
|
17
|
+
def index0
|
18
|
+
@index
|
19
|
+
end
|
20
|
+
|
21
|
+
def col0
|
22
|
+
@col - 1
|
23
|
+
end
|
24
|
+
|
25
|
+
def rindex
|
26
|
+
@length - @index
|
27
|
+
end
|
28
|
+
|
29
|
+
def rindex0
|
30
|
+
@length - @index - 1
|
31
|
+
end
|
32
|
+
|
33
|
+
def first
|
34
|
+
@index == 0
|
35
|
+
end
|
36
|
+
|
37
|
+
def last
|
38
|
+
@index == @length - 1
|
39
|
+
end
|
40
|
+
|
41
|
+
def col_first
|
42
|
+
@col == 1
|
43
|
+
end
|
44
|
+
|
45
|
+
def col_last
|
46
|
+
@col == @cols
|
47
|
+
end
|
48
|
+
|
49
|
+
protected
|
50
|
+
|
51
|
+
def increment!
|
52
|
+
@index += 1
|
53
|
+
|
54
|
+
if @col == @cols
|
55
|
+
@col = 1
|
56
|
+
@row += 1
|
57
|
+
else
|
58
|
+
@col += 1
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/liquid/tag.rb
CHANGED
@@ -1,26 +1,27 @@
|
|
1
1
|
module Liquid
|
2
2
|
class Tag
|
3
|
-
|
4
|
-
|
3
|
+
attr_reader :nodelist, :tag_name, :line_number, :parse_context
|
4
|
+
alias_method :options, :parse_context
|
5
5
|
include ParserSwitching
|
6
6
|
|
7
7
|
class << self
|
8
|
-
def parse(tag_name, markup,
|
8
|
+
def parse(tag_name, markup, tokenizer, options)
|
9
9
|
tag = new(tag_name, markup, options)
|
10
|
-
tag.parse(
|
10
|
+
tag.parse(tokenizer)
|
11
11
|
tag
|
12
12
|
end
|
13
13
|
|
14
14
|
private :new
|
15
15
|
end
|
16
16
|
|
17
|
-
def initialize(tag_name, markup,
|
17
|
+
def initialize(tag_name, markup, parse_context)
|
18
18
|
@tag_name = tag_name
|
19
19
|
@markup = markup
|
20
|
-
@
|
20
|
+
@parse_context = parse_context
|
21
|
+
@line_number = parse_context.line_number
|
21
22
|
end
|
22
23
|
|
23
|
-
def parse(
|
24
|
+
def parse(_tokens)
|
24
25
|
end
|
25
26
|
|
26
27
|
def raw
|
@@ -31,7 +32,7 @@ module Liquid
|
|
31
32
|
self.class.name.downcase
|
32
33
|
end
|
33
34
|
|
34
|
-
def render(
|
35
|
+
def render(_context)
|
35
36
|
''.freeze
|
36
37
|
end
|
37
38
|
|
data/lib/liquid/tags/assign.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
module Liquid
|
2
|
-
|
3
2
|
# Assign sets a variable in your template.
|
4
3
|
#
|
5
4
|
# {% assign foo = 'monkey' %}
|
@@ -15,8 +14,7 @@ module Liquid
|
|
15
14
|
super
|
16
15
|
if markup =~ Syntax
|
17
16
|
@to = $1
|
18
|
-
@from = Variable.new($2,options)
|
19
|
-
@from.line_number = line_number
|
17
|
+
@from = Variable.new($2, options)
|
20
18
|
else
|
21
19
|
raise SyntaxError.new options[:locale].t("errors.syntax.assign".freeze)
|
22
20
|
end
|
@@ -25,13 +23,28 @@ module Liquid
|
|
25
23
|
def render(context)
|
26
24
|
val = @from.render(context)
|
27
25
|
context.scopes.last[@to] = val
|
28
|
-
context.
|
26
|
+
context.resource_limits.assign_score += assign_score_of(val)
|
29
27
|
''.freeze
|
30
28
|
end
|
31
29
|
|
32
30
|
def blank?
|
33
31
|
true
|
34
32
|
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def assign_score_of(val)
|
37
|
+
if val.instance_of?(String)
|
38
|
+
val.length
|
39
|
+
elsif val.instance_of?(Array) || val.instance_of?(Hash)
|
40
|
+
sum = 1
|
41
|
+
# Uses #each to avoid extra allocations.
|
42
|
+
val.each { |child| sum += assign_score_of(child) }
|
43
|
+
sum
|
44
|
+
else
|
45
|
+
1
|
46
|
+
end
|
47
|
+
end
|
35
48
|
end
|
36
49
|
|
37
50
|
Template.register_tag('assign'.freeze, Assign)
|
data/lib/liquid/tags/break.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
module Liquid
|
2
|
-
|
3
2
|
# Break tag to be used to break out of a for loop.
|
4
3
|
#
|
5
4
|
# == Basic Usage:
|
@@ -10,11 +9,9 @@ module Liquid
|
|
10
9
|
# {% endfor %}
|
11
10
|
#
|
12
11
|
class Break < Tag
|
13
|
-
|
14
12
|
def interrupt
|
15
13
|
BreakInterrupt.new
|
16
14
|
end
|
17
|
-
|
18
15
|
end
|
19
16
|
|
20
17
|
Template.register_tag('break'.freeze, Break)
|
data/lib/liquid/tags/capture.rb
CHANGED
@@ -11,7 +11,7 @@ module Liquid
|
|
11
11
|
# in a sidebar or footer.
|
12
12
|
#
|
13
13
|
class Capture < Block
|
14
|
-
Syntax = /(
|
14
|
+
Syntax = /(#{VariableSignature}+)/o
|
15
15
|
|
16
16
|
def initialize(tag_name, markup, options)
|
17
17
|
super
|
@@ -25,7 +25,7 @@ module Liquid
|
|
25
25
|
def render(context)
|
26
26
|
output = super
|
27
27
|
context.scopes.last[@to] = output
|
28
|
-
context.
|
28
|
+
context.resource_limits.assign_score += output.length
|
29
29
|
''.freeze
|
30
30
|
end
|
31
31
|
|
data/lib/liquid/tags/case.rb
CHANGED
@@ -8,18 +8,24 @@ module Liquid
|
|
8
8
|
@blocks = []
|
9
9
|
|
10
10
|
if markup =~ Syntax
|
11
|
-
@left = $1
|
11
|
+
@left = Expression.parse($1)
|
12
12
|
else
|
13
13
|
raise SyntaxError.new(options[:locale].t("errors.syntax.case".freeze))
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
+
def parse(tokens)
|
18
|
+
body = BlockBody.new
|
19
|
+
while parse_body(body, tokens)
|
20
|
+
body = @blocks.last.attachment
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
17
24
|
def nodelist
|
18
|
-
@blocks.
|
25
|
+
@blocks.map(&:attachment)
|
19
26
|
end
|
20
27
|
|
21
28
|
def unknown_tag(tag, markup, tokens)
|
22
|
-
@nodelist = []
|
23
29
|
case tag
|
24
30
|
when 'when'.freeze
|
25
31
|
record_when_condition(markup)
|
@@ -37,10 +43,10 @@ module Liquid
|
|
37
43
|
output = ''
|
38
44
|
@blocks.each do |block|
|
39
45
|
if block.else?
|
40
|
-
return
|
46
|
+
return block.attachment.render(context) if execute_else_block
|
41
47
|
elsif block.evaluate(context)
|
42
48
|
execute_else_block = false
|
43
|
-
output <<
|
49
|
+
output << block.attachment.render(context)
|
44
50
|
end
|
45
51
|
end
|
46
52
|
output
|
@@ -50,27 +56,28 @@ module Liquid
|
|
50
56
|
private
|
51
57
|
|
52
58
|
def record_when_condition(markup)
|
59
|
+
body = BlockBody.new
|
60
|
+
|
53
61
|
while markup
|
54
|
-
|
55
|
-
if not markup =~ WhenSyntax
|
62
|
+
unless markup =~ WhenSyntax
|
56
63
|
raise SyntaxError.new(options[:locale].t("errors.syntax.case_invalid_when".freeze))
|
57
64
|
end
|
58
65
|
|
59
66
|
markup = $2
|
60
67
|
|
61
|
-
block = Condition.new(@left, '=='.freeze, $1)
|
62
|
-
block.attach(
|
63
|
-
@blocks
|
68
|
+
block = Condition.new(@left, '=='.freeze, Expression.parse($1))
|
69
|
+
block.attach(body)
|
70
|
+
@blocks << block
|
64
71
|
end
|
65
72
|
end
|
66
73
|
|
67
74
|
def record_else_condition(markup)
|
68
|
-
|
75
|
+
unless markup.strip.empty?
|
69
76
|
raise SyntaxError.new(options[:locale].t("errors.syntax.case_invalid_else".freeze))
|
70
77
|
end
|
71
78
|
|
72
79
|
block = ElseCondition.new
|
73
|
-
block.attach(
|
80
|
+
block.attach(BlockBody.new)
|
74
81
|
@blocks << block
|
75
82
|
end
|
76
83
|
end
|