liquid 2.6.3 → 5.4.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 +5 -5
- data/History.md +272 -26
- data/README.md +67 -3
- data/lib/liquid/block.rb +62 -94
- data/lib/liquid/block_body.rb +255 -0
- data/lib/liquid/condition.rb +96 -38
- data/lib/liquid/context.rb +172 -154
- data/lib/liquid/document.rb +57 -9
- data/lib/liquid/drop.rb +33 -14
- data/lib/liquid/errors.rb +56 -10
- data/lib/liquid/expression.rb +45 -0
- data/lib/liquid/extensions.rb +21 -7
- data/lib/liquid/file_system.rb +27 -14
- data/lib/liquid/forloop_drop.rb +92 -0
- data/lib/liquid/i18n.rb +41 -0
- data/lib/liquid/interrupts.rb +3 -2
- data/lib/liquid/lexer.rb +62 -0
- data/lib/liquid/locales/en.yml +29 -0
- data/lib/liquid/parse_context.rb +54 -0
- data/lib/liquid/parse_tree_visitor.rb +42 -0
- data/lib/liquid/parser.rb +102 -0
- data/lib/liquid/parser_switching.rb +45 -0
- data/lib/liquid/partial_cache.rb +24 -0
- data/lib/liquid/profiler/hooks.rb +35 -0
- data/lib/liquid/profiler.rb +139 -0
- data/lib/liquid/range_lookup.rb +47 -0
- data/lib/liquid/registers.rb +51 -0
- data/lib/liquid/resource_limits.rb +62 -0
- data/lib/liquid/standardfilters.rb +789 -118
- data/lib/liquid/strainer_factory.rb +41 -0
- data/lib/liquid/strainer_template.rb +62 -0
- data/lib/liquid/tablerowloop_drop.rb +121 -0
- data/lib/liquid/tag/disableable.rb +22 -0
- data/lib/liquid/tag/disabler.rb +21 -0
- data/lib/liquid/tag.rb +49 -10
- data/lib/liquid/tags/assign.rb +61 -19
- data/lib/liquid/tags/break.rb +14 -4
- data/lib/liquid/tags/capture.rb +29 -21
- data/lib/liquid/tags/case.rb +80 -31
- data/lib/liquid/tags/comment.rb +24 -2
- data/lib/liquid/tags/continue.rb +14 -13
- data/lib/liquid/tags/cycle.rb +50 -32
- data/lib/liquid/tags/decrement.rb +24 -26
- data/lib/liquid/tags/echo.rb +41 -0
- data/lib/liquid/tags/for.rb +164 -100
- data/lib/liquid/tags/if.rb +105 -44
- data/lib/liquid/tags/ifchanged.rb +10 -11
- data/lib/liquid/tags/include.rb +85 -65
- data/lib/liquid/tags/increment.rb +24 -22
- data/lib/liquid/tags/inline_comment.rb +43 -0
- data/lib/liquid/tags/raw.rb +50 -11
- data/lib/liquid/tags/render.rb +109 -0
- data/lib/liquid/tags/table_row.rb +88 -0
- data/lib/liquid/tags/unless.rb +37 -21
- data/lib/liquid/template.rb +124 -46
- data/lib/liquid/template_factory.rb +9 -0
- data/lib/liquid/tokenizer.rb +39 -0
- data/lib/liquid/usage.rb +8 -0
- data/lib/liquid/utils.rb +68 -5
- data/lib/liquid/variable.rb +128 -32
- data/lib/liquid/variable_lookup.rb +96 -0
- data/lib/liquid/version.rb +3 -1
- data/lib/liquid.rb +36 -13
- metadata +69 -77
- data/lib/extras/liquid_view.rb +0 -51
- data/lib/liquid/htmltags.rb +0 -73
- data/lib/liquid/module_ex.rb +0 -62
- data/lib/liquid/strainer.rb +0 -53
- data/test/liquid/assign_test.rb +0 -21
- data/test/liquid/block_test.rb +0 -58
- data/test/liquid/capture_test.rb +0 -40
- data/test/liquid/condition_test.rb +0 -127
- data/test/liquid/context_test.rb +0 -478
- data/test/liquid/drop_test.rb +0 -180
- data/test/liquid/error_handling_test.rb +0 -81
- data/test/liquid/file_system_test.rb +0 -29
- data/test/liquid/filter_test.rb +0 -125
- data/test/liquid/hash_ordering_test.rb +0 -25
- data/test/liquid/module_ex_test.rb +0 -87
- data/test/liquid/output_test.rb +0 -116
- data/test/liquid/parsing_quirks_test.rb +0 -52
- data/test/liquid/regexp_test.rb +0 -44
- data/test/liquid/security_test.rb +0 -64
- data/test/liquid/standard_filter_test.rb +0 -263
- data/test/liquid/strainer_test.rb +0 -52
- data/test/liquid/tags/break_tag_test.rb +0 -16
- data/test/liquid/tags/continue_tag_test.rb +0 -16
- data/test/liquid/tags/for_tag_test.rb +0 -297
- data/test/liquid/tags/html_tag_test.rb +0 -63
- data/test/liquid/tags/if_else_tag_test.rb +0 -166
- data/test/liquid/tags/include_tag_test.rb +0 -166
- data/test/liquid/tags/increment_tag_test.rb +0 -24
- data/test/liquid/tags/raw_tag_test.rb +0 -24
- data/test/liquid/tags/standard_tag_test.rb +0 -295
- data/test/liquid/tags/statements_test.rb +0 -134
- data/test/liquid/tags/unless_else_tag_test.rb +0 -26
- data/test/liquid/template_test.rb +0 -146
- data/test/liquid/variable_test.rb +0 -186
- data/test/test_helper.rb +0 -29
- /data/{MIT-LICENSE → LICENSE} +0 -0
@@ -1,153 +1,637 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'cgi'
|
4
|
+
require 'base64'
|
2
5
|
require 'bigdecimal'
|
3
6
|
|
4
7
|
module Liquid
|
5
|
-
|
6
8
|
module StandardFilters
|
7
|
-
|
8
|
-
|
9
|
+
MAX_INT = (1 << 31) - 1
|
10
|
+
HTML_ESCAPE = {
|
11
|
+
'&' => '&',
|
12
|
+
'>' => '>',
|
13
|
+
'<' => '<',
|
14
|
+
'"' => '"',
|
15
|
+
"'" => ''',
|
16
|
+
}.freeze
|
17
|
+
HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+));)/
|
18
|
+
STRIP_HTML_BLOCKS = Regexp.union(
|
19
|
+
%r{<script.*?</script>}m,
|
20
|
+
/<!--.*?-->/m,
|
21
|
+
%r{<style.*?</style>}m
|
22
|
+
)
|
23
|
+
STRIP_HTML_TAGS = /<.*?>/m
|
24
|
+
|
25
|
+
# @liquid_public_docs
|
26
|
+
# @liquid_type filter
|
27
|
+
# @liquid_category array
|
28
|
+
# @liquid_summary
|
29
|
+
# Returns the size of a string or array.
|
30
|
+
# @liquid_description
|
31
|
+
# The size of a string is the number of characters that the string includes. The size of an array is the number of items
|
32
|
+
# in the array.
|
33
|
+
# @liquid_syntax variable | size
|
34
|
+
# @liquid_return [number]
|
9
35
|
def size(input)
|
10
|
-
|
11
36
|
input.respond_to?(:size) ? input.size : 0
|
12
37
|
end
|
13
38
|
|
14
|
-
#
|
39
|
+
# @liquid_public_docs
|
40
|
+
# @liquid_type filter
|
41
|
+
# @liquid_category string
|
42
|
+
# @liquid_summary
|
43
|
+
# Converts a string to all lowercase characters.
|
44
|
+
# @liquid_syntax string | downcase
|
45
|
+
# @liquid_return [string]
|
15
46
|
def downcase(input)
|
16
47
|
input.to_s.downcase
|
17
48
|
end
|
18
49
|
|
19
|
-
#
|
50
|
+
# @liquid_public_docs
|
51
|
+
# @liquid_type filter
|
52
|
+
# @liquid_category string
|
53
|
+
# @liquid_summary
|
54
|
+
# Converts a string to all uppercase characters.
|
55
|
+
# @liquid_syntax string | upcase
|
56
|
+
# @liquid_return [string]
|
20
57
|
def upcase(input)
|
21
58
|
input.to_s.upcase
|
22
59
|
end
|
23
60
|
|
24
|
-
#
|
61
|
+
# @liquid_public_docs
|
62
|
+
# @liquid_type filter
|
63
|
+
# @liquid_category string
|
64
|
+
# @liquid_summary
|
65
|
+
# Capitalizes the first word in a string.
|
66
|
+
# @liquid_syntax string | capitalize
|
67
|
+
# @liquid_return [string]
|
25
68
|
def capitalize(input)
|
26
69
|
input.to_s.capitalize
|
27
70
|
end
|
28
71
|
|
72
|
+
# @liquid_public_docs
|
73
|
+
# @liquid_type filter
|
74
|
+
# @liquid_category string
|
75
|
+
# @liquid_summary
|
76
|
+
# Escapes a string.
|
77
|
+
# @liquid_syntax string | escape
|
78
|
+
# @liquid_return [string]
|
29
79
|
def escape(input)
|
30
|
-
CGI.escapeHTML(input)
|
80
|
+
CGI.escapeHTML(input.to_s) unless input.nil?
|
31
81
|
end
|
82
|
+
alias_method :h, :escape
|
32
83
|
|
84
|
+
# @liquid_public_docs
|
85
|
+
# @liquid_type filter
|
86
|
+
# @liquid_category string
|
87
|
+
# @liquid_summary
|
88
|
+
# Escapes a string without changing characters that have already been escaped.
|
89
|
+
# @liquid_syntax string | escape_once
|
90
|
+
# @liquid_return [string]
|
33
91
|
def escape_once(input)
|
34
|
-
|
35
|
-
|
36
|
-
|
92
|
+
input.to_s.gsub(HTML_ESCAPE_ONCE_REGEXP, HTML_ESCAPE)
|
93
|
+
end
|
94
|
+
|
95
|
+
# @liquid_public_docs
|
96
|
+
# @liquid_type filter
|
97
|
+
# @liquid_category string
|
98
|
+
# @liquid_summary
|
99
|
+
# Converts any URL-unsafe characters in a string to the
|
100
|
+
# [percent-encoded](https://developer.mozilla.org/en-US/docs/Glossary/percent-encoding) equivalent.
|
101
|
+
# @liquid_description
|
102
|
+
# > Note:
|
103
|
+
# > Spaces are converted to a `+` character, instead of a percent-encoded character.
|
104
|
+
# @liquid_syntax string | url_encode
|
105
|
+
# @liquid_return [string]
|
106
|
+
def url_encode(input)
|
107
|
+
CGI.escape(input.to_s) unless input.nil?
|
108
|
+
end
|
109
|
+
|
110
|
+
# @liquid_public_docs
|
111
|
+
# @liquid_type filter
|
112
|
+
# @liquid_category string
|
113
|
+
# @liquid_summary
|
114
|
+
# Decodes any [percent-encoded](https://developer.mozilla.org/en-US/docs/Glossary/percent-encoding) characters
|
115
|
+
# in a string.
|
116
|
+
# @liquid_syntax string | url_decode
|
117
|
+
# @liquid_return [string]
|
118
|
+
def url_decode(input)
|
119
|
+
return if input.nil?
|
120
|
+
|
121
|
+
result = CGI.unescape(input.to_s)
|
122
|
+
raise Liquid::ArgumentError, "invalid byte sequence in #{result.encoding}" unless result.valid_encoding?
|
123
|
+
|
124
|
+
result
|
125
|
+
end
|
126
|
+
|
127
|
+
# @liquid_public_docs
|
128
|
+
# @liquid_type filter
|
129
|
+
# @liquid_category string
|
130
|
+
# @liquid_summary
|
131
|
+
# Encodes a string to [Base64 format](https://developer.mozilla.org/en-US/docs/Glossary/Base64).
|
132
|
+
# @liquid_syntax string | base64_encode
|
133
|
+
# @liquid_return [string]
|
134
|
+
def base64_encode(input)
|
135
|
+
Base64.strict_encode64(input.to_s)
|
136
|
+
end
|
137
|
+
|
138
|
+
# @liquid_public_docs
|
139
|
+
# @liquid_type filter
|
140
|
+
# @liquid_category string
|
141
|
+
# @liquid_summary
|
142
|
+
# Decodes a string in [Base64 format](https://developer.mozilla.org/en-US/docs/Glossary/Base64).
|
143
|
+
# @liquid_syntax string | base64_decode
|
144
|
+
# @liquid_return [string]
|
145
|
+
def base64_decode(input)
|
146
|
+
Base64.strict_decode64(input.to_s)
|
147
|
+
rescue ::ArgumentError
|
148
|
+
raise Liquid::ArgumentError, "invalid base64 provided to base64_decode"
|
149
|
+
end
|
150
|
+
|
151
|
+
# @liquid_public_docs
|
152
|
+
# @liquid_type filter
|
153
|
+
# @liquid_category string
|
154
|
+
# @liquid_summary
|
155
|
+
# Encodes a string to URL-safe [Base64 format](https://developer.mozilla.org/en-US/docs/Glossary/Base64).
|
156
|
+
# @liquid_syntax string | base64_url_safe_encode
|
157
|
+
# @liquid_return [string]
|
158
|
+
def base64_url_safe_encode(input)
|
159
|
+
Base64.urlsafe_encode64(input.to_s)
|
160
|
+
end
|
161
|
+
|
162
|
+
# @liquid_public_docs
|
163
|
+
# @liquid_type filter
|
164
|
+
# @liquid_category string
|
165
|
+
# @liquid_summary
|
166
|
+
# Decodes a string in URL-safe [Base64 format](https://developer.mozilla.org/en-US/docs/Glossary/Base64).
|
167
|
+
# @liquid_syntax string | base64_url_safe_decode
|
168
|
+
# @liquid_return [string]
|
169
|
+
def base64_url_safe_decode(input)
|
170
|
+
Base64.urlsafe_decode64(input.to_s)
|
171
|
+
rescue ::ArgumentError
|
172
|
+
raise Liquid::ArgumentError, "invalid base64 provided to base64_url_safe_decode"
|
173
|
+
end
|
174
|
+
|
175
|
+
# @liquid_public_docs
|
176
|
+
# @liquid_type filter
|
177
|
+
# @liquid_category string
|
178
|
+
# @liquid_summary
|
179
|
+
# Returns a substring or series of array items, starting at a given 0-based index.
|
180
|
+
# @liquid_description
|
181
|
+
# By default, the substring has a length of one character, and the array series has one array item. However, you can
|
182
|
+
# provide a second parameter to specify the number of characters or array items.
|
183
|
+
# @liquid_syntax string | slice
|
184
|
+
# @liquid_return [string]
|
185
|
+
def slice(input, offset, length = nil)
|
186
|
+
offset = Utils.to_integer(offset)
|
187
|
+
length = length ? Utils.to_integer(length) : 1
|
188
|
+
|
189
|
+
if input.is_a?(Array)
|
190
|
+
input.slice(offset, length) || []
|
191
|
+
else
|
192
|
+
input.to_s.slice(offset, length) || ''
|
193
|
+
end
|
37
194
|
end
|
38
195
|
|
39
|
-
|
40
|
-
|
41
|
-
#
|
196
|
+
# @liquid_public_docs
|
197
|
+
# @liquid_type filter
|
198
|
+
# @liquid_category string
|
199
|
+
# @liquid_summary
|
200
|
+
# Truncates a string down to a given number of characters.
|
201
|
+
# @liquid_description
|
202
|
+
# If the specified number of characters is less than the length of the string, then an ellipsis (`...`) is appended to
|
203
|
+
# the truncated string. The ellipsis is included in the character count of the truncated string.
|
204
|
+
# @liquid_syntax string | truncate: number
|
205
|
+
# @liquid_return [string]
|
42
206
|
def truncate(input, length = 50, truncate_string = "...")
|
43
|
-
if input.nil?
|
44
|
-
|
45
|
-
|
46
|
-
truncated = RUBY_VERSION[0,3] == "1.8" ? input.scan(/./mu)[0...l].to_s : input[0...l]
|
47
|
-
input.length > length.to_i ? truncated + truncate_string : input
|
48
|
-
end
|
207
|
+
return if input.nil?
|
208
|
+
input_str = input.to_s
|
209
|
+
length = Utils.to_integer(length)
|
49
210
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
l = words.to_i - 1
|
211
|
+
truncate_string_str = truncate_string.to_s
|
212
|
+
|
213
|
+
l = length - truncate_string_str.length
|
54
214
|
l = 0 if l < 0
|
55
|
-
|
215
|
+
|
216
|
+
input_str.length > length ? input_str[0...l].concat(truncate_string_str) : input_str
|
56
217
|
end
|
57
218
|
|
58
|
-
#
|
219
|
+
# @liquid_public_docs
|
220
|
+
# @liquid_type filter
|
221
|
+
# @liquid_category string
|
222
|
+
# @liquid_summary
|
223
|
+
# Truncates a string down to a given number of words.
|
224
|
+
# @liquid_description
|
225
|
+
# If the specified number of words is less than the number of words in the string, then an ellipsis (`...`) is appended to
|
226
|
+
# the truncated string.
|
59
227
|
#
|
60
|
-
#
|
61
|
-
#
|
62
|
-
#
|
63
|
-
|
64
|
-
|
65
|
-
|
228
|
+
# > Caution:
|
229
|
+
# > HTML tags are treated as words, so you should strip any HTML from truncated content. If you don't strip HTML, then
|
230
|
+
# > closing HTML tags can be removed, which can result in unexpected behavior.
|
231
|
+
# @liquid_syntax string | truncatewords: number
|
232
|
+
# @liquid_return [string]
|
233
|
+
def truncatewords(input, words = 15, truncate_string = "...")
|
234
|
+
return if input.nil?
|
235
|
+
input = input.to_s
|
236
|
+
words = Utils.to_integer(words)
|
237
|
+
words = 1 if words <= 0
|
238
|
+
|
239
|
+
wordlist = begin
|
240
|
+
input.split(" ", words + 1)
|
241
|
+
rescue RangeError
|
242
|
+
raise if words + 1 < MAX_INT
|
243
|
+
# e.g. integer #{words} too big to convert to `int'
|
244
|
+
raise Liquid::ArgumentError, "integer #{words} too big for truncatewords"
|
245
|
+
end
|
246
|
+
return input if wordlist.length <= words
|
66
247
|
|
67
|
-
|
68
|
-
|
248
|
+
wordlist.pop
|
249
|
+
wordlist.join(" ").concat(truncate_string.to_s)
|
69
250
|
end
|
70
251
|
|
71
|
-
#
|
252
|
+
# @liquid_public_docs
|
253
|
+
# @liquid_type filter
|
254
|
+
# @liquid_category string
|
255
|
+
# @liquid_summary
|
256
|
+
# Splits a string into an array of substrings based on a given separator.
|
257
|
+
# @liquid_syntax string | split: string
|
258
|
+
# @liquid_return [array[string]]
|
259
|
+
def split(input, pattern)
|
260
|
+
input.to_s.split(pattern.to_s)
|
261
|
+
end
|
262
|
+
|
263
|
+
# @liquid_public_docs
|
264
|
+
# @liquid_type filter
|
265
|
+
# @liquid_category string
|
266
|
+
# @liquid_summary
|
267
|
+
# Strips all whitespace from the left and right of a string.
|
268
|
+
# @liquid_syntax string | strip
|
269
|
+
# @liquid_return [string]
|
270
|
+
def strip(input)
|
271
|
+
input.to_s.strip
|
272
|
+
end
|
273
|
+
|
274
|
+
# @liquid_public_docs
|
275
|
+
# @liquid_type filter
|
276
|
+
# @liquid_category string
|
277
|
+
# @liquid_summary
|
278
|
+
# Strips all whitespace from the left of a string.
|
279
|
+
# @liquid_syntax string | lstrip
|
280
|
+
# @liquid_return [string]
|
281
|
+
def lstrip(input)
|
282
|
+
input.to_s.lstrip
|
283
|
+
end
|
284
|
+
|
285
|
+
# @liquid_public_docs
|
286
|
+
# @liquid_type filter
|
287
|
+
# @liquid_category string
|
288
|
+
# @liquid_summary
|
289
|
+
# Strips all whitespace from the right of a string.
|
290
|
+
# @liquid_syntax string | rstrip
|
291
|
+
# @liquid_return [string]
|
292
|
+
def rstrip(input)
|
293
|
+
input.to_s.rstrip
|
294
|
+
end
|
295
|
+
|
296
|
+
# @liquid_public_docs
|
297
|
+
# @liquid_type filter
|
298
|
+
# @liquid_category string
|
299
|
+
# @liquid_summary
|
300
|
+
# Strips all HTML tags from a string.
|
301
|
+
# @liquid_syntax string | strip_html
|
302
|
+
# @liquid_return [string]
|
303
|
+
def strip_html(input)
|
304
|
+
empty = ''
|
305
|
+
result = input.to_s.gsub(STRIP_HTML_BLOCKS, empty)
|
306
|
+
result.gsub!(STRIP_HTML_TAGS, empty)
|
307
|
+
result
|
308
|
+
end
|
309
|
+
|
310
|
+
# @liquid_public_docs
|
311
|
+
# @liquid_type filter
|
312
|
+
# @liquid_category string
|
313
|
+
# @liquid_summary
|
314
|
+
# Strips all newline characters (line breaks) from a string.
|
315
|
+
# @liquid_syntax string | strip_newlines
|
316
|
+
# @liquid_return [string]
|
72
317
|
def strip_newlines(input)
|
73
318
|
input.to_s.gsub(/\r?\n/, '')
|
74
319
|
end
|
75
320
|
|
76
|
-
#
|
321
|
+
# @liquid_public_docs
|
322
|
+
# @liquid_type filter
|
323
|
+
# @liquid_category array
|
324
|
+
# @liquid_summary
|
325
|
+
# Combines all of the items in an array into a single string, separated by a space.
|
326
|
+
# @liquid_syntax array | join
|
327
|
+
# @liquid_return [string]
|
77
328
|
def join(input, glue = ' ')
|
78
|
-
|
329
|
+
InputIterator.new(input, context).join(glue)
|
79
330
|
end
|
80
331
|
|
81
|
-
#
|
82
|
-
#
|
332
|
+
# @liquid_public_docs
|
333
|
+
# @liquid_type filter
|
334
|
+
# @liquid_category array
|
335
|
+
# @liquid_summary
|
336
|
+
# Sorts the items in an array in case-sensitive alphabetical, or numerical, order.
|
337
|
+
# @liquid_syntax array | sort
|
338
|
+
# @liquid_return [array[untyped]]
|
83
339
|
def sort(input, property = nil)
|
84
|
-
ary =
|
340
|
+
ary = InputIterator.new(input, context)
|
341
|
+
|
342
|
+
return [] if ary.empty?
|
343
|
+
|
344
|
+
if property.nil?
|
345
|
+
ary.sort do |a, b|
|
346
|
+
nil_safe_compare(a, b)
|
347
|
+
end
|
348
|
+
elsif ary.all? { |el| el.respond_to?(:[]) }
|
349
|
+
begin
|
350
|
+
ary.sort { |a, b| nil_safe_compare(a[property], b[property]) }
|
351
|
+
rescue TypeError
|
352
|
+
raise_property_error(property)
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
# @liquid_public_docs
|
358
|
+
# @liquid_type filter
|
359
|
+
# @liquid_category array
|
360
|
+
# @liquid_summary
|
361
|
+
# Sorts the items in an array in case-insensitive alphabetical order.
|
362
|
+
# @liquid_description
|
363
|
+
# > Caution:
|
364
|
+
# > You shouldn't use the `sort_natural` filter to sort numerical values. When comparing items an array, each item is converted to a
|
365
|
+
# > string, so sorting on numerical values can lead to unexpected results.
|
366
|
+
# @liquid_syntax array | sort_natural
|
367
|
+
# @liquid_return [array[untyped]]
|
368
|
+
def sort_natural(input, property = nil)
|
369
|
+
ary = InputIterator.new(input, context)
|
370
|
+
|
371
|
+
return [] if ary.empty?
|
372
|
+
|
373
|
+
if property.nil?
|
374
|
+
ary.sort do |a, b|
|
375
|
+
nil_safe_casecmp(a, b)
|
376
|
+
end
|
377
|
+
elsif ary.all? { |el| el.respond_to?(:[]) }
|
378
|
+
begin
|
379
|
+
ary.sort { |a, b| nil_safe_casecmp(a[property], b[property]) }
|
380
|
+
rescue TypeError
|
381
|
+
raise_property_error(property)
|
382
|
+
end
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
# @liquid_public_docs
|
387
|
+
# @liquid_type filter
|
388
|
+
# @liquid_category array
|
389
|
+
# @liquid_summary
|
390
|
+
# Filters an array to include only items with a specific property value.
|
391
|
+
# @liquid_description
|
392
|
+
# This requires you to provide both the property name and the associated value.
|
393
|
+
# @liquid_syntax array | where: string, string
|
394
|
+
# @liquid_return [array[untyped]]
|
395
|
+
def where(input, property, target_value = nil)
|
396
|
+
ary = InputIterator.new(input, context)
|
397
|
+
|
398
|
+
if ary.empty?
|
399
|
+
[]
|
400
|
+
elsif target_value.nil?
|
401
|
+
ary.select do |item|
|
402
|
+
item[property]
|
403
|
+
rescue TypeError
|
404
|
+
raise_property_error(property)
|
405
|
+
rescue NoMethodError
|
406
|
+
return nil unless item.respond_to?(:[])
|
407
|
+
raise
|
408
|
+
end
|
409
|
+
else
|
410
|
+
ary.select do |item|
|
411
|
+
item[property] == target_value
|
412
|
+
rescue TypeError
|
413
|
+
raise_property_error(property)
|
414
|
+
rescue NoMethodError
|
415
|
+
return nil unless item.respond_to?(:[])
|
416
|
+
raise
|
417
|
+
end
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
421
|
+
# @liquid_public_docs
|
422
|
+
# @liquid_type filter
|
423
|
+
# @liquid_category array
|
424
|
+
# @liquid_summary
|
425
|
+
# Removes any duplicate items in an array.
|
426
|
+
# @liquid_syntax array | uniq
|
427
|
+
# @liquid_return [array[untyped]]
|
428
|
+
def uniq(input, property = nil)
|
429
|
+
ary = InputIterator.new(input, context)
|
430
|
+
|
85
431
|
if property.nil?
|
86
|
-
ary.
|
87
|
-
elsif ary.
|
88
|
-
|
89
|
-
|
90
|
-
ary.
|
432
|
+
ary.uniq
|
433
|
+
elsif ary.empty? # The next two cases assume a non-empty array.
|
434
|
+
[]
|
435
|
+
else
|
436
|
+
ary.uniq do |item|
|
437
|
+
item[property]
|
438
|
+
rescue TypeError
|
439
|
+
raise_property_error(property)
|
440
|
+
rescue NoMethodError
|
441
|
+
return nil unless item.respond_to?(:[])
|
442
|
+
raise
|
443
|
+
end
|
91
444
|
end
|
92
445
|
end
|
93
446
|
|
94
|
-
#
|
447
|
+
# @liquid_public_docs
|
448
|
+
# @liquid_type filter
|
449
|
+
# @liquid_category array
|
450
|
+
# @liquid_summary
|
451
|
+
# Reverses the order of the items in an array.
|
452
|
+
# @liquid_syntax array | reverse
|
453
|
+
# @liquid_return [array[untyped]]
|
95
454
|
def reverse(input)
|
96
|
-
ary =
|
455
|
+
ary = InputIterator.new(input, context)
|
97
456
|
ary.reverse
|
98
457
|
end
|
99
458
|
|
100
|
-
#
|
459
|
+
# @liquid_public_docs
|
460
|
+
# @liquid_type filter
|
461
|
+
# @liquid_category array
|
462
|
+
# @liquid_summary
|
463
|
+
# Creates an array of values from a specific property of the items in an array.
|
464
|
+
# @liquid_syntax array | map: string
|
465
|
+
# @liquid_return [array[untyped]]
|
101
466
|
def map(input, property)
|
102
|
-
|
103
|
-
ary.map do |e|
|
467
|
+
InputIterator.new(input, context).map do |e|
|
104
468
|
e = e.call if e.is_a?(Proc)
|
105
|
-
e = e.to_liquid if e.respond_to?(:to_liquid)
|
106
469
|
|
107
470
|
if property == "to_liquid"
|
108
471
|
e
|
109
472
|
elsif e.respond_to?(:[])
|
110
|
-
e[property]
|
473
|
+
r = e[property]
|
474
|
+
r.is_a?(Proc) ? r.call : r
|
111
475
|
end
|
112
476
|
end
|
477
|
+
rescue TypeError
|
478
|
+
raise_property_error(property)
|
113
479
|
end
|
114
480
|
|
115
|
-
#
|
481
|
+
# @liquid_public_docs
|
482
|
+
# @liquid_type filter
|
483
|
+
# @liquid_category array
|
484
|
+
# @liquid_summary
|
485
|
+
# Removes any `nil` items from an array.
|
486
|
+
# @liquid_syntax array | compact
|
487
|
+
# @liquid_return [array[untyped]]
|
488
|
+
def compact(input, property = nil)
|
489
|
+
ary = InputIterator.new(input, context)
|
490
|
+
|
491
|
+
if property.nil?
|
492
|
+
ary.compact
|
493
|
+
elsif ary.empty? # The next two cases assume a non-empty array.
|
494
|
+
[]
|
495
|
+
else
|
496
|
+
ary.reject do |item|
|
497
|
+
item[property].nil?
|
498
|
+
rescue TypeError
|
499
|
+
raise_property_error(property)
|
500
|
+
rescue NoMethodError
|
501
|
+
return nil unless item.respond_to?(:[])
|
502
|
+
raise
|
503
|
+
end
|
504
|
+
end
|
505
|
+
end
|
506
|
+
|
507
|
+
# @liquid_public_docs
|
508
|
+
# @liquid_type filter
|
509
|
+
# @liquid_category string
|
510
|
+
# @liquid_summary
|
511
|
+
# Replaces any instance of a substring inside a string with a given string.
|
512
|
+
# @liquid_syntax string | replace: string, string
|
513
|
+
# @liquid_return [string]
|
116
514
|
def replace(input, string, replacement = '')
|
117
|
-
input.to_s.gsub(string, replacement.to_s)
|
515
|
+
input.to_s.gsub(string.to_s, replacement.to_s)
|
118
516
|
end
|
119
517
|
|
120
|
-
#
|
518
|
+
# @liquid_public_docs
|
519
|
+
# @liquid_type filter
|
520
|
+
# @liquid_category string
|
521
|
+
# @liquid_summary
|
522
|
+
# Replaces the first instance of a substring inside a string with a given string.
|
523
|
+
# @liquid_syntax string | replace_first: string, string
|
524
|
+
# @liquid_return [string]
|
121
525
|
def replace_first(input, string, replacement = '')
|
122
|
-
input.to_s.sub(string, replacement.to_s)
|
526
|
+
input.to_s.sub(string.to_s, replacement.to_s)
|
123
527
|
end
|
124
528
|
|
125
|
-
#
|
126
|
-
|
127
|
-
|
529
|
+
# @liquid_public_docs
|
530
|
+
# @liquid_type filter
|
531
|
+
# @liquid_category string
|
532
|
+
# @liquid_summary
|
533
|
+
# Replaces the last instance of a substring inside a string with a given string.
|
534
|
+
# @liquid_syntax string | replace_last: string, string
|
535
|
+
# @liquid_return [string]
|
536
|
+
def replace_last(input, string, replacement)
|
537
|
+
input = input.to_s
|
538
|
+
string = string.to_s
|
539
|
+
replacement = replacement.to_s
|
540
|
+
|
541
|
+
start_index = input.rindex(string)
|
542
|
+
|
543
|
+
return input unless start_index
|
544
|
+
|
545
|
+
output = input.dup
|
546
|
+
output[start_index, string.length] = replacement
|
547
|
+
output
|
128
548
|
end
|
129
549
|
|
130
|
-
#
|
131
|
-
|
132
|
-
|
550
|
+
# @liquid_public_docs
|
551
|
+
# @liquid_type filter
|
552
|
+
# @liquid_category string
|
553
|
+
# @liquid_summary
|
554
|
+
# Removes any instance of a substring inside a string.
|
555
|
+
# @liquid_syntax string | remove: string
|
556
|
+
# @liquid_return [string]
|
557
|
+
def remove(input, string)
|
558
|
+
replace(input, string, '')
|
133
559
|
end
|
134
560
|
|
135
|
-
#
|
561
|
+
# @liquid_public_docs
|
562
|
+
# @liquid_type filter
|
563
|
+
# @liquid_category string
|
564
|
+
# @liquid_summary
|
565
|
+
# Removes the first instance of a substring inside a string.
|
566
|
+
# @liquid_syntax string | remove_first: string
|
567
|
+
# @liquid_return [string]
|
568
|
+
def remove_first(input, string)
|
569
|
+
replace_first(input, string, '')
|
570
|
+
end
|
571
|
+
|
572
|
+
# @liquid_public_docs
|
573
|
+
# @liquid_type filter
|
574
|
+
# @liquid_category string
|
575
|
+
# @liquid_summary
|
576
|
+
# Removes the last instance of a substring inside a string.
|
577
|
+
# @liquid_syntax string | remove_last: string
|
578
|
+
# @liquid_return [string]
|
579
|
+
def remove_last(input, string)
|
580
|
+
replace_last(input, string, '')
|
581
|
+
end
|
582
|
+
|
583
|
+
# @liquid_public_docs
|
584
|
+
# @liquid_type filter
|
585
|
+
# @liquid_category string
|
586
|
+
# @liquid_summary
|
587
|
+
# Adds a given string to the end of a string.
|
588
|
+
# @liquid_syntax string | append: string
|
589
|
+
# @liquid_return [string]
|
136
590
|
def append(input, string)
|
137
591
|
input.to_s + string.to_s
|
138
592
|
end
|
139
593
|
|
140
|
-
#
|
594
|
+
# @liquid_public_docs
|
595
|
+
# @liquid_type filter
|
596
|
+
# @liquid_category array
|
597
|
+
# @liquid_summary
|
598
|
+
# Concatenates (combines) two arrays.
|
599
|
+
# @liquid_description
|
600
|
+
# > Note:
|
601
|
+
# > The `concat` filter won't filter out duplicates. If you want to remove duplicates, then you need to use the
|
602
|
+
# > [`uniq` filter](/api/liquid/filters#uniq).
|
603
|
+
# @liquid_syntax array | concat: array
|
604
|
+
# @liquid_return [array[untyped]]
|
605
|
+
def concat(input, array)
|
606
|
+
unless array.respond_to?(:to_ary)
|
607
|
+
raise ArgumentError, "concat filter requires an array argument"
|
608
|
+
end
|
609
|
+
InputIterator.new(input, context).concat(array)
|
610
|
+
end
|
611
|
+
|
612
|
+
# @liquid_public_docs
|
613
|
+
# @liquid_type filter
|
614
|
+
# @liquid_category string
|
615
|
+
# @liquid_summary
|
616
|
+
# Adds a given string to the beginning of a string.
|
617
|
+
# @liquid_syntax string | prepend: string
|
618
|
+
# @liquid_return [string]
|
141
619
|
def prepend(input, string)
|
142
620
|
string.to_s + input.to_s
|
143
621
|
end
|
144
622
|
|
145
|
-
#
|
623
|
+
# @liquid_public_docs
|
624
|
+
# @liquid_type filter
|
625
|
+
# @liquid_category string
|
626
|
+
# @liquid_summary
|
627
|
+
# Converts newlines (`\n`) in a string to HTML line breaks (`<br>`).
|
628
|
+
# @liquid_syntax string | newline_to_br
|
629
|
+
# @liquid_return [string]
|
146
630
|
def newline_to_br(input)
|
147
|
-
input.to_s.gsub(/\n/, "<br />\n")
|
631
|
+
input.to_s.gsub(/\r?\n/, "<br />\n")
|
148
632
|
end
|
149
633
|
|
150
|
-
# Reformat a date
|
634
|
+
# Reformat a date using Ruby's core Time#strftime( string ) -> string
|
151
635
|
#
|
152
636
|
# %a - The abbreviated weekday name (``Sun'')
|
153
637
|
# %A - The full weekday name (``Sunday'')
|
@@ -161,6 +645,7 @@ module Liquid
|
|
161
645
|
# %m - Month of the year (01..12)
|
162
646
|
# %M - Minute of the hour (00..59)
|
163
647
|
# %p - Meridian indicator (``AM'' or ``PM'')
|
648
|
+
# %s - Number of seconds since 1970-01-01 00:00:00 UTC.
|
164
649
|
# %S - Second of the minute (00..60)
|
165
650
|
# %U - Week number of the current year,
|
166
651
|
# starting with the first Sunday as the first
|
@@ -175,97 +660,283 @@ module Liquid
|
|
175
660
|
# %Y - Year with century
|
176
661
|
# %Z - Time zone name
|
177
662
|
# %% - Literal ``%'' character
|
663
|
+
#
|
664
|
+
# See also: http://www.ruby-doc.org/core/Time.html#method-i-strftime
|
178
665
|
def date(input, format)
|
666
|
+
return input if format.to_s.empty?
|
179
667
|
|
180
|
-
|
181
|
-
return input.to_s
|
182
|
-
end
|
183
|
-
|
184
|
-
if ((input.is_a?(String) && !/^\d+$/.match(input.to_s).nil?) || input.is_a?(Integer)) && input.to_i > 0
|
185
|
-
input = Time.at(input.to_i)
|
186
|
-
end
|
187
|
-
|
188
|
-
date = if input.is_a?(String)
|
189
|
-
case input.downcase
|
190
|
-
when 'now', 'today'
|
191
|
-
Time.now
|
192
|
-
else
|
193
|
-
Time.parse(input)
|
194
|
-
end
|
195
|
-
else
|
196
|
-
input
|
197
|
-
end
|
668
|
+
return input unless (date = Utils.to_date(input))
|
198
669
|
|
199
|
-
|
200
|
-
date.strftime(format.to_s)
|
201
|
-
else
|
202
|
-
input
|
203
|
-
end
|
204
|
-
rescue
|
205
|
-
input
|
670
|
+
date.strftime(format.to_s)
|
206
671
|
end
|
207
672
|
|
208
|
-
#
|
209
|
-
#
|
210
|
-
#
|
211
|
-
#
|
212
|
-
#
|
673
|
+
# @liquid_public_docs
|
674
|
+
# @liquid_type filter
|
675
|
+
# @liquid_category array
|
676
|
+
# @liquid_summary
|
677
|
+
# Returns the first item in an array.
|
678
|
+
# @liquid_syntax array | first
|
679
|
+
# @liquid_return [untyped]
|
213
680
|
def first(array)
|
214
681
|
array.first if array.respond_to?(:first)
|
215
682
|
end
|
216
683
|
|
217
|
-
#
|
218
|
-
#
|
219
|
-
#
|
220
|
-
#
|
221
|
-
#
|
684
|
+
# @liquid_public_docs
|
685
|
+
# @liquid_type filter
|
686
|
+
# @liquid_category array
|
687
|
+
# @liquid_summary
|
688
|
+
# Returns the last item in an array.
|
689
|
+
# @liquid_syntax array | last
|
690
|
+
# @liquid_return [untyped]
|
222
691
|
def last(array)
|
223
692
|
array.last if array.respond_to?(:last)
|
224
693
|
end
|
225
694
|
|
226
|
-
#
|
695
|
+
# @liquid_public_docs
|
696
|
+
# @liquid_type filter
|
697
|
+
# @liquid_category math
|
698
|
+
# @liquid_summary
|
699
|
+
# Returns the absolute value of a number.
|
700
|
+
# @liquid_syntax number | abs
|
701
|
+
# @liquid_return [number]
|
702
|
+
def abs(input)
|
703
|
+
result = Utils.to_number(input).abs
|
704
|
+
result.is_a?(BigDecimal) ? result.to_f : result
|
705
|
+
end
|
706
|
+
|
707
|
+
# @liquid_public_docs
|
708
|
+
# @liquid_type filter
|
709
|
+
# @liquid_category math
|
710
|
+
# @liquid_summary
|
711
|
+
# Adds two numbers.
|
712
|
+
# @liquid_syntax number | plus: number
|
713
|
+
# @liquid_return [number]
|
227
714
|
def plus(input, operand)
|
228
715
|
apply_operation(input, operand, :+)
|
229
716
|
end
|
230
717
|
|
231
|
-
#
|
718
|
+
# @liquid_public_docs
|
719
|
+
# @liquid_type filter
|
720
|
+
# @liquid_category math
|
721
|
+
# @liquid_summary
|
722
|
+
# Subtracts a given number from another number.
|
723
|
+
# @liquid_syntax number | minus: number
|
724
|
+
# @liquid_return [number]
|
232
725
|
def minus(input, operand)
|
233
726
|
apply_operation(input, operand, :-)
|
234
727
|
end
|
235
728
|
|
236
|
-
#
|
729
|
+
# @liquid_public_docs
|
730
|
+
# @liquid_type filter
|
731
|
+
# @liquid_category math
|
732
|
+
# @liquid_summary
|
733
|
+
# Multiplies a number by a given number.
|
734
|
+
# @liquid_syntax number | times: number
|
735
|
+
# @liquid_return [number]
|
237
736
|
def times(input, operand)
|
238
737
|
apply_operation(input, operand, :*)
|
239
738
|
end
|
240
739
|
|
241
|
-
#
|
740
|
+
# @liquid_public_docs
|
741
|
+
# @liquid_type filter
|
742
|
+
# @liquid_category math
|
743
|
+
# @liquid_summary
|
744
|
+
# Divides a number by a given number.
|
745
|
+
# @liquid_syntax number | divided_by: number
|
746
|
+
# @liquid_return [number]
|
242
747
|
def divided_by(input, operand)
|
243
748
|
apply_operation(input, operand, :/)
|
749
|
+
rescue ::ZeroDivisionError => e
|
750
|
+
raise Liquid::ZeroDivisionError, e.message
|
244
751
|
end
|
245
752
|
|
753
|
+
# @liquid_public_docs
|
754
|
+
# @liquid_type filter
|
755
|
+
# @liquid_category math
|
756
|
+
# @liquid_summary
|
757
|
+
# Returns the remainder of dividing a number by a given number.
|
758
|
+
# @liquid_syntax number | modulo: number
|
759
|
+
# @liquid_return [number]
|
246
760
|
def modulo(input, operand)
|
247
761
|
apply_operation(input, operand, :%)
|
762
|
+
rescue ::ZeroDivisionError => e
|
763
|
+
raise Liquid::ZeroDivisionError, e.message
|
764
|
+
end
|
765
|
+
|
766
|
+
# @liquid_public_docs
|
767
|
+
# @liquid_type filter
|
768
|
+
# @liquid_category math
|
769
|
+
# @liquid_summary
|
770
|
+
# Rounds a number to the nearest integer.
|
771
|
+
# @liquid_syntax number | round
|
772
|
+
# @liquid_return [number]
|
773
|
+
def round(input, n = 0)
|
774
|
+
result = Utils.to_number(input).round(Utils.to_number(n))
|
775
|
+
result = result.to_f if result.is_a?(BigDecimal)
|
776
|
+
result = result.to_i if n == 0
|
777
|
+
result
|
778
|
+
rescue ::FloatDomainError => e
|
779
|
+
raise Liquid::FloatDomainError, e.message
|
780
|
+
end
|
781
|
+
|
782
|
+
# @liquid_public_docs
|
783
|
+
# @liquid_type filter
|
784
|
+
# @liquid_category math
|
785
|
+
# @liquid_summary
|
786
|
+
# Rounds a number up to the nearest integer.
|
787
|
+
# @liquid_syntax number | ceil
|
788
|
+
# @liquid_return [number]
|
789
|
+
def ceil(input)
|
790
|
+
Utils.to_number(input).ceil.to_i
|
791
|
+
rescue ::FloatDomainError => e
|
792
|
+
raise Liquid::FloatDomainError, e.message
|
793
|
+
end
|
794
|
+
|
795
|
+
# @liquid_public_docs
|
796
|
+
# @liquid_type filter
|
797
|
+
# @liquid_category math
|
798
|
+
# @liquid_summary
|
799
|
+
# Rounds a number down to the nearest integer.
|
800
|
+
# @liquid_syntax number | floor
|
801
|
+
# @liquid_return [number]
|
802
|
+
def floor(input)
|
803
|
+
Utils.to_number(input).floor.to_i
|
804
|
+
rescue ::FloatDomainError => e
|
805
|
+
raise Liquid::FloatDomainError, e.message
|
806
|
+
end
|
807
|
+
|
808
|
+
# @liquid_public_docs
|
809
|
+
# @liquid_type filter
|
810
|
+
# @liquid_category math
|
811
|
+
# @liquid_summary
|
812
|
+
# Limits a number to a minimum value.
|
813
|
+
# @liquid_syntax number | at_least
|
814
|
+
# @liquid_return [number]
|
815
|
+
def at_least(input, n)
|
816
|
+
min_value = Utils.to_number(n)
|
817
|
+
|
818
|
+
result = Utils.to_number(input)
|
819
|
+
result = min_value if min_value > result
|
820
|
+
result.is_a?(BigDecimal) ? result.to_f : result
|
821
|
+
end
|
822
|
+
|
823
|
+
# @liquid_public_docs
|
824
|
+
# @liquid_type filter
|
825
|
+
# @liquid_category math
|
826
|
+
# @liquid_summary
|
827
|
+
# Limits a number to a maximum value.
|
828
|
+
# @liquid_syntax number | at_most
|
829
|
+
# @liquid_return [number]
|
830
|
+
def at_most(input, n)
|
831
|
+
max_value = Utils.to_number(n)
|
832
|
+
|
833
|
+
result = Utils.to_number(input)
|
834
|
+
result = max_value if max_value < result
|
835
|
+
result.is_a?(BigDecimal) ? result.to_f : result
|
836
|
+
end
|
837
|
+
|
838
|
+
# @liquid_public_docs
|
839
|
+
# @liquid_type filter
|
840
|
+
# @liquid_category default
|
841
|
+
# @liquid_summary
|
842
|
+
# Sets a default value for any variable whose value is one of the following:
|
843
|
+
#
|
844
|
+
# - [`empty`](/api/liquid/basics#empty)
|
845
|
+
# - [`false`](/api/liquid/basics#truthy-and-falsy)
|
846
|
+
# - [`nil`](/api/liquid/basics#nil)
|
847
|
+
# @liquid_syntax variable | default: variable
|
848
|
+
# @liquid_return [untyped]
|
849
|
+
# @liquid_optional_param allow_false [boolean] Whether to use false values instead of the default.
|
850
|
+
def default(input, default_value = '', options = {})
|
851
|
+
options = {} unless options.is_a?(Hash)
|
852
|
+
false_check = options['allow_false'] ? input.nil? : !Liquid::Utils.to_liquid_value(input)
|
853
|
+
false_check || (input.respond_to?(:empty?) && input.empty?) ? default_value : input
|
248
854
|
end
|
249
855
|
|
250
856
|
private
|
251
857
|
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
when Numeric
|
257
|
-
obj
|
258
|
-
when String
|
259
|
-
(obj.strip =~ /^\d+\.\d+$/) ? BigDecimal.new(obj) : obj.to_i
|
260
|
-
else
|
261
|
-
0
|
262
|
-
end
|
858
|
+
attr_reader :context
|
859
|
+
|
860
|
+
def raise_property_error(property)
|
861
|
+
raise Liquid::ArgumentError, "cannot select the property '#{property}'"
|
263
862
|
end
|
264
863
|
|
265
864
|
def apply_operation(input, operand, operation)
|
266
|
-
result = to_number(input).send(operation, to_number(operand))
|
865
|
+
result = Utils.to_number(input).send(operation, Utils.to_number(operand))
|
267
866
|
result.is_a?(BigDecimal) ? result.to_f : result
|
268
867
|
end
|
868
|
+
|
869
|
+
def nil_safe_compare(a, b)
|
870
|
+
result = a <=> b
|
871
|
+
|
872
|
+
if result
|
873
|
+
result
|
874
|
+
elsif a.nil?
|
875
|
+
1
|
876
|
+
elsif b.nil?
|
877
|
+
-1
|
878
|
+
else
|
879
|
+
raise Liquid::ArgumentError, "cannot sort values of incompatible types"
|
880
|
+
end
|
881
|
+
end
|
882
|
+
|
883
|
+
def nil_safe_casecmp(a, b)
|
884
|
+
if !a.nil? && !b.nil?
|
885
|
+
a.to_s.casecmp(b.to_s)
|
886
|
+
else
|
887
|
+
a.nil? ? 1 : -1
|
888
|
+
end
|
889
|
+
end
|
890
|
+
|
891
|
+
class InputIterator
|
892
|
+
include Enumerable
|
893
|
+
|
894
|
+
def initialize(input, context)
|
895
|
+
@context = context
|
896
|
+
@input = if input.is_a?(Array)
|
897
|
+
input.flatten
|
898
|
+
elsif input.is_a?(Hash)
|
899
|
+
[input]
|
900
|
+
elsif input.is_a?(Enumerable)
|
901
|
+
input
|
902
|
+
else
|
903
|
+
Array(input)
|
904
|
+
end
|
905
|
+
end
|
906
|
+
|
907
|
+
def join(glue)
|
908
|
+
to_a.join(glue.to_s)
|
909
|
+
end
|
910
|
+
|
911
|
+
def concat(args)
|
912
|
+
to_a.concat(args)
|
913
|
+
end
|
914
|
+
|
915
|
+
def reverse
|
916
|
+
reverse_each.to_a
|
917
|
+
end
|
918
|
+
|
919
|
+
def uniq(&block)
|
920
|
+
to_a.uniq(&block)
|
921
|
+
end
|
922
|
+
|
923
|
+
def compact
|
924
|
+
to_a.compact
|
925
|
+
end
|
926
|
+
|
927
|
+
def empty?
|
928
|
+
@input.each { return false }
|
929
|
+
true
|
930
|
+
end
|
931
|
+
|
932
|
+
def each
|
933
|
+
@input.each do |e|
|
934
|
+
e = e.respond_to?(:to_liquid) ? e.to_liquid : e
|
935
|
+
e.context = @context if e.respond_to?(:context=)
|
936
|
+
yield(e)
|
937
|
+
end
|
938
|
+
end
|
939
|
+
end
|
269
940
|
end
|
270
941
|
|
271
942
|
Template.register_filter(StandardFilters)
|