liquid 5.3.0 → 5.5.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 +20 -1
- data/README.md +5 -5
- data/lib/liquid/block.rb +8 -4
- data/lib/liquid/block_body.rb +21 -6
- data/lib/liquid/condition.rb +9 -4
- data/lib/liquid/context.rb +13 -5
- data/lib/liquid/drop.rb +4 -0
- data/lib/liquid/errors.rb +16 -15
- data/lib/liquid/expression.rb +4 -1
- data/lib/liquid/forloop_drop.rb +45 -5
- data/lib/liquid/lexer.rb +2 -3
- data/lib/liquid/locales/en.yml +7 -5
- data/lib/liquid/partial_cache.rb +15 -6
- data/lib/liquid/range_lookup.rb +11 -1
- data/lib/liquid/{static_registers.rb → registers.rb} +13 -10
- data/lib/liquid/standardfilters.rb +480 -68
- data/lib/liquid/strainer_factory.rb +4 -0
- data/lib/liquid/strainer_template.rb +4 -0
- data/lib/liquid/tablerowloop_drop.rb +58 -1
- data/lib/liquid/tag/disabler.rb +0 -8
- data/lib/liquid/tag.rb +10 -3
- data/lib/liquid/tags/assign.rb +12 -8
- data/lib/liquid/tags/break.rb +8 -0
- data/lib/liquid/tags/capture.rb +13 -10
- data/lib/liquid/tags/case.rb +22 -1
- data/lib/liquid/tags/comment.rb +72 -0
- data/lib/liquid/tags/continue.rb +8 -9
- data/lib/liquid/tags/cycle.rb +12 -11
- data/lib/liquid/tags/decrement.rb +22 -20
- data/lib/liquid/tags/echo.rb +16 -9
- data/lib/liquid/tags/for.rb +25 -46
- data/lib/liquid/tags/if.rb +12 -10
- data/lib/liquid/tags/include.rb +21 -17
- data/lib/liquid/tags/increment.rb +22 -17
- data/lib/liquid/tags/inline_comment.rb +30 -0
- data/lib/liquid/tags/raw.rb +13 -2
- data/lib/liquid/tags/render.rb +37 -8
- data/lib/liquid/tags/table_row.rb +33 -3
- data/lib/liquid/tags/unless.rb +17 -6
- data/lib/liquid/template.rb +11 -4
- data/lib/liquid/tokenizer.rb +9 -3
- data/lib/liquid/variable.rb +4 -4
- data/lib/liquid/variable_lookup.rb +10 -7
- data/lib/liquid/version.rb +1 -1
- data/lib/liquid.rb +3 -3
- metadata +7 -123
- data/lib/liquid/register.rb +0 -6
- data/test/fixtures/en_locale.yml +0 -9
- data/test/integration/assign_test.rb +0 -117
- data/test/integration/blank_test.rb +0 -109
- data/test/integration/block_test.rb +0 -58
- data/test/integration/capture_test.rb +0 -58
- data/test/integration/context_test.rb +0 -634
- data/test/integration/document_test.rb +0 -21
- data/test/integration/drop_test.rb +0 -257
- data/test/integration/error_handling_test.rb +0 -272
- data/test/integration/expression_test.rb +0 -46
- data/test/integration/filter_kwarg_test.rb +0 -24
- data/test/integration/filter_test.rb +0 -189
- data/test/integration/hash_ordering_test.rb +0 -25
- data/test/integration/output_test.rb +0 -125
- data/test/integration/parsing_quirks_test.rb +0 -134
- data/test/integration/profiler_test.rb +0 -240
- data/test/integration/security_test.rb +0 -89
- data/test/integration/standard_filter_test.rb +0 -925
- data/test/integration/tag/disableable_test.rb +0 -59
- data/test/integration/tag_test.rb +0 -45
- data/test/integration/tags/break_tag_test.rb +0 -17
- data/test/integration/tags/continue_tag_test.rb +0 -17
- data/test/integration/tags/echo_test.rb +0 -13
- data/test/integration/tags/for_tag_test.rb +0 -466
- data/test/integration/tags/if_else_tag_test.rb +0 -190
- data/test/integration/tags/include_tag_test.rb +0 -269
- data/test/integration/tags/increment_tag_test.rb +0 -25
- data/test/integration/tags/liquid_tag_test.rb +0 -116
- data/test/integration/tags/raw_tag_test.rb +0 -34
- data/test/integration/tags/render_tag_test.rb +0 -213
- data/test/integration/tags/standard_tag_test.rb +0 -303
- data/test/integration/tags/statements_test.rb +0 -113
- data/test/integration/tags/table_row_test.rb +0 -66
- data/test/integration/tags/unless_else_tag_test.rb +0 -28
- data/test/integration/template_test.rb +0 -340
- data/test/integration/trim_mode_test.rb +0 -563
- data/test/integration/variable_test.rb +0 -138
- data/test/test_helper.rb +0 -207
- data/test/unit/block_unit_test.rb +0 -53
- data/test/unit/condition_unit_test.rb +0 -181
- data/test/unit/file_system_unit_test.rb +0 -37
- data/test/unit/i18n_unit_test.rb +0 -39
- data/test/unit/lexer_unit_test.rb +0 -53
- data/test/unit/parse_tree_visitor_test.rb +0 -261
- data/test/unit/parser_unit_test.rb +0 -84
- data/test/unit/partial_cache_unit_test.rb +0 -128
- data/test/unit/regexp_unit_test.rb +0 -46
- data/test/unit/static_registers_unit_test.rb +0 -156
- data/test/unit/strainer_factory_unit_test.rb +0 -101
- data/test/unit/strainer_template_unit_test.rb +0 -82
- data/test/unit/tag_unit_test.rb +0 -23
- data/test/unit/tags/case_tag_unit_test.rb +0 -12
- data/test/unit/tags/for_tag_unit_test.rb +0 -15
- data/test/unit/tags/if_tag_unit_test.rb +0 -10
- data/test/unit/template_factory_unit_test.rb +0 -12
- data/test/unit/template_unit_test.rb +0 -87
- data/test/unit/tokenizer_unit_test.rb +0 -62
- data/test/unit/variable_unit_test.rb +0 -164
@@ -6,7 +6,14 @@ require 'bigdecimal'
|
|
6
6
|
|
7
7
|
module Liquid
|
8
8
|
module StandardFilters
|
9
|
-
|
9
|
+
MAX_I32 = (1 << 31) - 1
|
10
|
+
private_constant :MAX_I32
|
11
|
+
|
12
|
+
MIN_I64 = -(1 << 63)
|
13
|
+
MAX_I64 = (1 << 63) - 1
|
14
|
+
I64_RANGE = MIN_I64..MAX_I64
|
15
|
+
private_constant :MIN_I64, :MAX_I64, :I64_RANGE
|
16
|
+
|
10
17
|
HTML_ESCAPE = {
|
11
18
|
'&' => '&',
|
12
19
|
'>' => '>',
|
@@ -18,43 +25,116 @@ module Liquid
|
|
18
25
|
STRIP_HTML_BLOCKS = Regexp.union(
|
19
26
|
%r{<script.*?</script>}m,
|
20
27
|
/<!--.*?-->/m,
|
21
|
-
%r{<style.*?</style>}m
|
28
|
+
%r{<style.*?</style>}m,
|
22
29
|
)
|
23
30
|
STRIP_HTML_TAGS = /<.*?>/m
|
24
31
|
|
25
|
-
|
32
|
+
class << self
|
33
|
+
def try_coerce_encoding(input, encoding:)
|
34
|
+
original_encoding = input.encoding
|
35
|
+
if input.encoding != encoding
|
36
|
+
input.force_encoding(encoding)
|
37
|
+
unless input.valid_encoding?
|
38
|
+
input.force_encoding(original_encoding)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
input
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# @liquid_public_docs
|
46
|
+
# @liquid_type filter
|
47
|
+
# @liquid_category array
|
48
|
+
# @liquid_summary
|
49
|
+
# Returns the size of a string or array.
|
50
|
+
# @liquid_description
|
51
|
+
# The size of a string is the number of characters that the string includes. The size of an array is the number of items
|
52
|
+
# in the array.
|
53
|
+
# @liquid_syntax variable | size
|
54
|
+
# @liquid_return [number]
|
26
55
|
def size(input)
|
27
56
|
input.respond_to?(:size) ? input.size : 0
|
28
57
|
end
|
29
58
|
|
30
|
-
#
|
59
|
+
# @liquid_public_docs
|
60
|
+
# @liquid_type filter
|
61
|
+
# @liquid_category string
|
62
|
+
# @liquid_summary
|
63
|
+
# Converts a string to all lowercase characters.
|
64
|
+
# @liquid_syntax string | downcase
|
65
|
+
# @liquid_return [string]
|
31
66
|
def downcase(input)
|
32
67
|
input.to_s.downcase
|
33
68
|
end
|
34
69
|
|
35
|
-
#
|
70
|
+
# @liquid_public_docs
|
71
|
+
# @liquid_type filter
|
72
|
+
# @liquid_category string
|
73
|
+
# @liquid_summary
|
74
|
+
# Converts a string to all uppercase characters.
|
75
|
+
# @liquid_syntax string | upcase
|
76
|
+
# @liquid_return [string]
|
36
77
|
def upcase(input)
|
37
78
|
input.to_s.upcase
|
38
79
|
end
|
39
80
|
|
40
|
-
#
|
81
|
+
# @liquid_public_docs
|
82
|
+
# @liquid_type filter
|
83
|
+
# @liquid_category string
|
84
|
+
# @liquid_summary
|
85
|
+
# Capitalizes the first word in a string and downcases the remaining characters.
|
86
|
+
# @liquid_syntax string | capitalize
|
87
|
+
# @liquid_return [string]
|
41
88
|
def capitalize(input)
|
42
89
|
input.to_s.capitalize
|
43
90
|
end
|
44
91
|
|
92
|
+
# @liquid_public_docs
|
93
|
+
# @liquid_type filter
|
94
|
+
# @liquid_category string
|
95
|
+
# @liquid_summary
|
96
|
+
# Escapes special characters in HTML, such as `<>`, `'`, and `&`, and converts characters into escape sequences. The filter doesn't effect characters within the string that don’t have a corresponding escape sequence.".
|
97
|
+
# @liquid_syntax string | escape
|
98
|
+
# @liquid_return [string]
|
45
99
|
def escape(input)
|
46
100
|
CGI.escapeHTML(input.to_s) unless input.nil?
|
47
101
|
end
|
48
102
|
alias_method :h, :escape
|
49
103
|
|
104
|
+
# @liquid_public_docs
|
105
|
+
# @liquid_type filter
|
106
|
+
# @liquid_category string
|
107
|
+
# @liquid_summary
|
108
|
+
# Escapes a string without changing characters that have already been escaped.
|
109
|
+
# @liquid_syntax string | escape_once
|
110
|
+
# @liquid_return [string]
|
50
111
|
def escape_once(input)
|
51
112
|
input.to_s.gsub(HTML_ESCAPE_ONCE_REGEXP, HTML_ESCAPE)
|
52
113
|
end
|
53
114
|
|
115
|
+
# @liquid_public_docs
|
116
|
+
# @liquid_type filter
|
117
|
+
# @liquid_category string
|
118
|
+
# @liquid_summary
|
119
|
+
# Converts any URL-unsafe characters in a string to the
|
120
|
+
# [percent-encoded](https://developer.mozilla.org/en-US/docs/Glossary/percent-encoding) equivalent.
|
121
|
+
# @liquid_description
|
122
|
+
# > Note:
|
123
|
+
# > Spaces are converted to a `+` character, instead of a percent-encoded character.
|
124
|
+
# @liquid_syntax string | url_encode
|
125
|
+
# @liquid_return [string]
|
54
126
|
def url_encode(input)
|
55
127
|
CGI.escape(input.to_s) unless input.nil?
|
56
128
|
end
|
57
129
|
|
130
|
+
# @liquid_public_docs
|
131
|
+
# @liquid_type filter
|
132
|
+
# @liquid_category string
|
133
|
+
# @liquid_summary
|
134
|
+
# Decodes any [percent-encoded](https://developer.mozilla.org/en-US/docs/Glossary/percent-encoding) characters
|
135
|
+
# in a string.
|
136
|
+
# @liquid_syntax string | url_decode
|
137
|
+
# @liquid_return [string]
|
58
138
|
def url_decode(input)
|
59
139
|
return if input.nil?
|
60
140
|
|
@@ -64,38 +144,96 @@ module Liquid
|
|
64
144
|
result
|
65
145
|
end
|
66
146
|
|
147
|
+
# @liquid_public_docs
|
148
|
+
# @liquid_type filter
|
149
|
+
# @liquid_category string
|
150
|
+
# @liquid_summary
|
151
|
+
# Encodes a string to [Base64 format](https://developer.mozilla.org/en-US/docs/Glossary/Base64).
|
152
|
+
# @liquid_syntax string | base64_encode
|
153
|
+
# @liquid_return [string]
|
67
154
|
def base64_encode(input)
|
68
155
|
Base64.strict_encode64(input.to_s)
|
69
156
|
end
|
70
157
|
|
158
|
+
# @liquid_public_docs
|
159
|
+
# @liquid_type filter
|
160
|
+
# @liquid_category string
|
161
|
+
# @liquid_summary
|
162
|
+
# Decodes a string in [Base64 format](https://developer.mozilla.org/en-US/docs/Glossary/Base64).
|
163
|
+
# @liquid_syntax string | base64_decode
|
164
|
+
# @liquid_return [string]
|
71
165
|
def base64_decode(input)
|
72
|
-
|
166
|
+
input = input.to_s
|
167
|
+
StandardFilters.try_coerce_encoding(Base64.strict_decode64(input), encoding: input.encoding)
|
73
168
|
rescue ::ArgumentError
|
74
169
|
raise Liquid::ArgumentError, "invalid base64 provided to base64_decode"
|
75
170
|
end
|
76
171
|
|
172
|
+
# @liquid_public_docs
|
173
|
+
# @liquid_type filter
|
174
|
+
# @liquid_category string
|
175
|
+
# @liquid_summary
|
176
|
+
# Encodes a string to URL-safe [Base64 format](https://developer.mozilla.org/en-US/docs/Glossary/Base64).
|
177
|
+
# @liquid_syntax string | base64_url_safe_encode
|
178
|
+
# @liquid_return [string]
|
77
179
|
def base64_url_safe_encode(input)
|
78
180
|
Base64.urlsafe_encode64(input.to_s)
|
79
181
|
end
|
80
182
|
|
183
|
+
# @liquid_public_docs
|
184
|
+
# @liquid_type filter
|
185
|
+
# @liquid_category string
|
186
|
+
# @liquid_summary
|
187
|
+
# Decodes a string in URL-safe [Base64 format](https://developer.mozilla.org/en-US/docs/Glossary/Base64).
|
188
|
+
# @liquid_syntax string | base64_url_safe_decode
|
189
|
+
# @liquid_return [string]
|
81
190
|
def base64_url_safe_decode(input)
|
82
|
-
|
191
|
+
input = input.to_s
|
192
|
+
StandardFilters.try_coerce_encoding(Base64.urlsafe_decode64(input), encoding: input.encoding)
|
83
193
|
rescue ::ArgumentError
|
84
194
|
raise Liquid::ArgumentError, "invalid base64 provided to base64_url_safe_decode"
|
85
195
|
end
|
86
196
|
|
197
|
+
# @liquid_public_docs
|
198
|
+
# @liquid_type filter
|
199
|
+
# @liquid_category string
|
200
|
+
# @liquid_summary
|
201
|
+
# Returns a substring or series of array items, starting at a given 0-based index.
|
202
|
+
# @liquid_description
|
203
|
+
# By default, the substring has a length of one character, and the array series has one array item. However, you can
|
204
|
+
# provide a second parameter to specify the number of characters or array items.
|
205
|
+
# @liquid_syntax string | slice
|
206
|
+
# @liquid_return [string]
|
87
207
|
def slice(input, offset, length = nil)
|
88
208
|
offset = Utils.to_integer(offset)
|
89
209
|
length = length ? Utils.to_integer(length) : 1
|
90
210
|
|
91
|
-
|
92
|
-
input.
|
93
|
-
|
94
|
-
|
211
|
+
begin
|
212
|
+
if input.is_a?(Array)
|
213
|
+
input.slice(offset, length) || []
|
214
|
+
else
|
215
|
+
input.to_s.slice(offset, length) || ''
|
216
|
+
end
|
217
|
+
rescue RangeError
|
218
|
+
if I64_RANGE.cover?(length) && I64_RANGE.cover?(offset)
|
219
|
+
raise # unexpected error
|
220
|
+
end
|
221
|
+
offset = offset.clamp(I64_RANGE)
|
222
|
+
length = length.clamp(I64_RANGE)
|
223
|
+
retry
|
95
224
|
end
|
96
225
|
end
|
97
226
|
|
98
|
-
#
|
227
|
+
# @liquid_public_docs
|
228
|
+
# @liquid_type filter
|
229
|
+
# @liquid_category string
|
230
|
+
# @liquid_summary
|
231
|
+
# Truncates a string down to a given number of characters.
|
232
|
+
# @liquid_description
|
233
|
+
# If the specified number of characters is less than the length of the string, then an ellipsis (`...`) is appended to
|
234
|
+
# the truncated string. The ellipsis is included in the character count of the truncated string.
|
235
|
+
# @liquid_syntax string | truncate: number
|
236
|
+
# @liquid_return [string]
|
99
237
|
def truncate(input, length = 50, truncate_string = "...")
|
100
238
|
return if input.nil?
|
101
239
|
input_str = input.to_s
|
@@ -109,6 +247,20 @@ module Liquid
|
|
109
247
|
input_str.length > length ? input_str[0...l].concat(truncate_string_str) : input_str
|
110
248
|
end
|
111
249
|
|
250
|
+
# @liquid_public_docs
|
251
|
+
# @liquid_type filter
|
252
|
+
# @liquid_category string
|
253
|
+
# @liquid_summary
|
254
|
+
# Truncates a string down to a given number of words.
|
255
|
+
# @liquid_description
|
256
|
+
# If the specified number of words is less than the number of words in the string, then an ellipsis (`...`) is appended to
|
257
|
+
# the truncated string.
|
258
|
+
#
|
259
|
+
# > Caution:
|
260
|
+
# > HTML tags are treated as words, so you should strip any HTML from truncated content. If you don't strip HTML, then
|
261
|
+
# > closing HTML tags can be removed, which can result in unexpected behavior.
|
262
|
+
# @liquid_syntax string | truncatewords: number
|
263
|
+
# @liquid_return [string]
|
112
264
|
def truncatewords(input, words = 15, truncate_string = "...")
|
113
265
|
return if input.nil?
|
114
266
|
input = input.to_s
|
@@ -118,9 +270,9 @@ module Liquid
|
|
118
270
|
wordlist = begin
|
119
271
|
input.split(" ", words + 1)
|
120
272
|
rescue RangeError
|
121
|
-
|
122
|
-
|
123
|
-
raise
|
273
|
+
# integer too big for String#split, but we can semantically assume no truncation is needed
|
274
|
+
return input if words + 1 > MAX_I32
|
275
|
+
raise # unexpected error
|
124
276
|
end
|
125
277
|
return input if wordlist.length <= words
|
126
278
|
|
@@ -128,27 +280,57 @@ module Liquid
|
|
128
280
|
wordlist.join(" ").concat(truncate_string.to_s)
|
129
281
|
end
|
130
282
|
|
131
|
-
#
|
132
|
-
#
|
133
|
-
#
|
134
|
-
#
|
135
|
-
#
|
283
|
+
# @liquid_public_docs
|
284
|
+
# @liquid_type filter
|
285
|
+
# @liquid_category string
|
286
|
+
# @liquid_summary
|
287
|
+
# Splits a string into an array of substrings based on a given separator.
|
288
|
+
# @liquid_syntax string | split: string
|
289
|
+
# @liquid_return [array[string]]
|
136
290
|
def split(input, pattern)
|
137
291
|
input.to_s.split(pattern.to_s)
|
138
292
|
end
|
139
293
|
|
294
|
+
# @liquid_public_docs
|
295
|
+
# @liquid_type filter
|
296
|
+
# @liquid_category string
|
297
|
+
# @liquid_summary
|
298
|
+
# Strips all whitespace from the left and right of a string.
|
299
|
+
# @liquid_syntax string | strip
|
300
|
+
# @liquid_return [string]
|
140
301
|
def strip(input)
|
141
302
|
input.to_s.strip
|
142
303
|
end
|
143
304
|
|
305
|
+
# @liquid_public_docs
|
306
|
+
# @liquid_type filter
|
307
|
+
# @liquid_category string
|
308
|
+
# @liquid_summary
|
309
|
+
# Strips all whitespace from the left of a string.
|
310
|
+
# @liquid_syntax string | lstrip
|
311
|
+
# @liquid_return [string]
|
144
312
|
def lstrip(input)
|
145
313
|
input.to_s.lstrip
|
146
314
|
end
|
147
315
|
|
316
|
+
# @liquid_public_docs
|
317
|
+
# @liquid_type filter
|
318
|
+
# @liquid_category string
|
319
|
+
# @liquid_summary
|
320
|
+
# Strips all whitespace from the right of a string.
|
321
|
+
# @liquid_syntax string | rstrip
|
322
|
+
# @liquid_return [string]
|
148
323
|
def rstrip(input)
|
149
324
|
input.to_s.rstrip
|
150
325
|
end
|
151
326
|
|
327
|
+
# @liquid_public_docs
|
328
|
+
# @liquid_type filter
|
329
|
+
# @liquid_category string
|
330
|
+
# @liquid_summary
|
331
|
+
# Strips all HTML tags from a string.
|
332
|
+
# @liquid_syntax string | strip_html
|
333
|
+
# @liquid_return [string]
|
152
334
|
def strip_html(input)
|
153
335
|
empty = ''
|
154
336
|
result = input.to_s.gsub(STRIP_HTML_BLOCKS, empty)
|
@@ -156,18 +338,35 @@ module Liquid
|
|
156
338
|
result
|
157
339
|
end
|
158
340
|
|
159
|
-
#
|
341
|
+
# @liquid_public_docs
|
342
|
+
# @liquid_type filter
|
343
|
+
# @liquid_category string
|
344
|
+
# @liquid_summary
|
345
|
+
# Strips all newline characters (line breaks) from a string.
|
346
|
+
# @liquid_syntax string | strip_newlines
|
347
|
+
# @liquid_return [string]
|
160
348
|
def strip_newlines(input)
|
161
349
|
input.to_s.gsub(/\r?\n/, '')
|
162
350
|
end
|
163
351
|
|
164
|
-
#
|
352
|
+
# @liquid_public_docs
|
353
|
+
# @liquid_type filter
|
354
|
+
# @liquid_category array
|
355
|
+
# @liquid_summary
|
356
|
+
# Combines all of the items in an array into a single string, separated by a space.
|
357
|
+
# @liquid_syntax array | join
|
358
|
+
# @liquid_return [string]
|
165
359
|
def join(input, glue = ' ')
|
166
360
|
InputIterator.new(input, context).join(glue)
|
167
361
|
end
|
168
362
|
|
169
|
-
#
|
170
|
-
#
|
363
|
+
# @liquid_public_docs
|
364
|
+
# @liquid_type filter
|
365
|
+
# @liquid_category array
|
366
|
+
# @liquid_summary
|
367
|
+
# Sorts the items in an array in case-sensitive alphabetical, or numerical, order.
|
368
|
+
# @liquid_syntax array | sort
|
369
|
+
# @liquid_return [array[untyped]]
|
171
370
|
def sort(input, property = nil)
|
172
371
|
ary = InputIterator.new(input, context)
|
173
372
|
|
@@ -186,8 +385,17 @@ module Liquid
|
|
186
385
|
end
|
187
386
|
end
|
188
387
|
|
189
|
-
#
|
190
|
-
#
|
388
|
+
# @liquid_public_docs
|
389
|
+
# @liquid_type filter
|
390
|
+
# @liquid_category array
|
391
|
+
# @liquid_summary
|
392
|
+
# Sorts the items in an array in case-insensitive alphabetical order.
|
393
|
+
# @liquid_description
|
394
|
+
# > Caution:
|
395
|
+
# > You shouldn't use the `sort_natural` filter to sort numerical values. When comparing items an array, each item is converted to a
|
396
|
+
# > string, so sorting on numerical values can lead to unexpected results.
|
397
|
+
# @liquid_syntax array | sort_natural
|
398
|
+
# @liquid_return [array[untyped]]
|
191
399
|
def sort_natural(input, property = nil)
|
192
400
|
ary = InputIterator.new(input, context)
|
193
401
|
|
@@ -206,8 +414,15 @@ module Liquid
|
|
206
414
|
end
|
207
415
|
end
|
208
416
|
|
209
|
-
#
|
210
|
-
#
|
417
|
+
# @liquid_public_docs
|
418
|
+
# @liquid_type filter
|
419
|
+
# @liquid_category array
|
420
|
+
# @liquid_summary
|
421
|
+
# Filters an array to include only items with a specific property value.
|
422
|
+
# @liquid_description
|
423
|
+
# This requires you to provide both the property name and the associated value.
|
424
|
+
# @liquid_syntax array | where: string, string
|
425
|
+
# @liquid_return [array[untyped]]
|
211
426
|
def where(input, property, target_value = nil)
|
212
427
|
ary = InputIterator.new(input, context)
|
213
428
|
|
@@ -234,8 +449,13 @@ module Liquid
|
|
234
449
|
end
|
235
450
|
end
|
236
451
|
|
237
|
-
#
|
238
|
-
#
|
452
|
+
# @liquid_public_docs
|
453
|
+
# @liquid_type filter
|
454
|
+
# @liquid_category array
|
455
|
+
# @liquid_summary
|
456
|
+
# Removes any duplicate items in an array.
|
457
|
+
# @liquid_syntax array | uniq
|
458
|
+
# @liquid_return [array[untyped]]
|
239
459
|
def uniq(input, property = nil)
|
240
460
|
ary = InputIterator.new(input, context)
|
241
461
|
|
@@ -255,13 +475,25 @@ module Liquid
|
|
255
475
|
end
|
256
476
|
end
|
257
477
|
|
258
|
-
#
|
478
|
+
# @liquid_public_docs
|
479
|
+
# @liquid_type filter
|
480
|
+
# @liquid_category array
|
481
|
+
# @liquid_summary
|
482
|
+
# Reverses the order of the items in an array.
|
483
|
+
# @liquid_syntax array | reverse
|
484
|
+
# @liquid_return [array[untyped]]
|
259
485
|
def reverse(input)
|
260
486
|
ary = InputIterator.new(input, context)
|
261
487
|
ary.reverse
|
262
488
|
end
|
263
489
|
|
264
|
-
#
|
490
|
+
# @liquid_public_docs
|
491
|
+
# @liquid_type filter
|
492
|
+
# @liquid_category array
|
493
|
+
# @liquid_summary
|
494
|
+
# Creates an array of values from a specific property of the items in an array.
|
495
|
+
# @liquid_syntax array | map: string
|
496
|
+
# @liquid_return [array[untyped]]
|
265
497
|
def map(input, property)
|
266
498
|
InputIterator.new(input, context).map do |e|
|
267
499
|
e = e.call if e.is_a?(Proc)
|
@@ -277,8 +509,13 @@ module Liquid
|
|
277
509
|
raise_property_error(property)
|
278
510
|
end
|
279
511
|
|
280
|
-
#
|
281
|
-
#
|
512
|
+
# @liquid_public_docs
|
513
|
+
# @liquid_type filter
|
514
|
+
# @liquid_category array
|
515
|
+
# @liquid_summary
|
516
|
+
# Removes any `nil` items from an array.
|
517
|
+
# @liquid_syntax array | compact
|
518
|
+
# @liquid_return [array[untyped]]
|
282
519
|
def compact(input, property = nil)
|
283
520
|
ary = InputIterator.new(input, context)
|
284
521
|
|
@@ -298,17 +535,35 @@ module Liquid
|
|
298
535
|
end
|
299
536
|
end
|
300
537
|
|
301
|
-
#
|
538
|
+
# @liquid_public_docs
|
539
|
+
# @liquid_type filter
|
540
|
+
# @liquid_category string
|
541
|
+
# @liquid_summary
|
542
|
+
# Replaces any instance of a substring inside a string with a given string.
|
543
|
+
# @liquid_syntax string | replace: string, string
|
544
|
+
# @liquid_return [string]
|
302
545
|
def replace(input, string, replacement = '')
|
303
546
|
input.to_s.gsub(string.to_s, replacement.to_s)
|
304
547
|
end
|
305
548
|
|
306
|
-
#
|
549
|
+
# @liquid_public_docs
|
550
|
+
# @liquid_type filter
|
551
|
+
# @liquid_category string
|
552
|
+
# @liquid_summary
|
553
|
+
# Replaces the first instance of a substring inside a string with a given string.
|
554
|
+
# @liquid_syntax string | replace_first: string, string
|
555
|
+
# @liquid_return [string]
|
307
556
|
def replace_first(input, string, replacement = '')
|
308
557
|
input.to_s.sub(string.to_s, replacement.to_s)
|
309
558
|
end
|
310
559
|
|
311
|
-
#
|
560
|
+
# @liquid_public_docs
|
561
|
+
# @liquid_type filter
|
562
|
+
# @liquid_category string
|
563
|
+
# @liquid_summary
|
564
|
+
# Replaces the last instance of a substring inside a string with a given string.
|
565
|
+
# @liquid_syntax string | replace_last: string, string
|
566
|
+
# @liquid_return [string]
|
312
567
|
def replace_last(input, string, replacement)
|
313
568
|
input = input.to_s
|
314
569
|
string = string.to_s
|
@@ -323,26 +578,61 @@ module Liquid
|
|
323
578
|
output
|
324
579
|
end
|
325
580
|
|
326
|
-
#
|
581
|
+
# @liquid_public_docs
|
582
|
+
# @liquid_type filter
|
583
|
+
# @liquid_category string
|
584
|
+
# @liquid_summary
|
585
|
+
# Removes any instance of a substring inside a string.
|
586
|
+
# @liquid_syntax string | remove: string
|
587
|
+
# @liquid_return [string]
|
327
588
|
def remove(input, string)
|
328
589
|
replace(input, string, '')
|
329
590
|
end
|
330
591
|
|
331
|
-
#
|
592
|
+
# @liquid_public_docs
|
593
|
+
# @liquid_type filter
|
594
|
+
# @liquid_category string
|
595
|
+
# @liquid_summary
|
596
|
+
# Removes the first instance of a substring inside a string.
|
597
|
+
# @liquid_syntax string | remove_first: string
|
598
|
+
# @liquid_return [string]
|
332
599
|
def remove_first(input, string)
|
333
600
|
replace_first(input, string, '')
|
334
601
|
end
|
335
602
|
|
336
|
-
#
|
603
|
+
# @liquid_public_docs
|
604
|
+
# @liquid_type filter
|
605
|
+
# @liquid_category string
|
606
|
+
# @liquid_summary
|
607
|
+
# Removes the last instance of a substring inside a string.
|
608
|
+
# @liquid_syntax string | remove_last: string
|
609
|
+
# @liquid_return [string]
|
337
610
|
def remove_last(input, string)
|
338
611
|
replace_last(input, string, '')
|
339
612
|
end
|
340
613
|
|
341
|
-
#
|
614
|
+
# @liquid_public_docs
|
615
|
+
# @liquid_type filter
|
616
|
+
# @liquid_category string
|
617
|
+
# @liquid_summary
|
618
|
+
# Adds a given string to the end of a string.
|
619
|
+
# @liquid_syntax string | append: string
|
620
|
+
# @liquid_return [string]
|
342
621
|
def append(input, string)
|
343
622
|
input.to_s + string.to_s
|
344
623
|
end
|
345
624
|
|
625
|
+
# @liquid_public_docs
|
626
|
+
# @liquid_type filter
|
627
|
+
# @liquid_category array
|
628
|
+
# @liquid_summary
|
629
|
+
# Concatenates (combines) two arrays.
|
630
|
+
# @liquid_description
|
631
|
+
# > Note:
|
632
|
+
# > The `concat` filter won't filter out duplicates. If you want to remove duplicates, then you need to use the
|
633
|
+
# > [`uniq` filter](/docs/api/liquid/filters/uniq).
|
634
|
+
# @liquid_syntax array | concat: array
|
635
|
+
# @liquid_return [array[untyped]]
|
346
636
|
def concat(input, array)
|
347
637
|
unless array.respond_to?(:to_ary)
|
348
638
|
raise ArgumentError, "concat filter requires an array argument"
|
@@ -350,12 +640,24 @@ module Liquid
|
|
350
640
|
InputIterator.new(input, context).concat(array)
|
351
641
|
end
|
352
642
|
|
353
|
-
#
|
643
|
+
# @liquid_public_docs
|
644
|
+
# @liquid_type filter
|
645
|
+
# @liquid_category string
|
646
|
+
# @liquid_summary
|
647
|
+
# Adds a given string to the beginning of a string.
|
648
|
+
# @liquid_syntax string | prepend: string
|
649
|
+
# @liquid_return [string]
|
354
650
|
def prepend(input, string)
|
355
651
|
string.to_s + input.to_s
|
356
652
|
end
|
357
653
|
|
358
|
-
#
|
654
|
+
# @liquid_public_docs
|
655
|
+
# @liquid_type filter
|
656
|
+
# @liquid_category string
|
657
|
+
# @liquid_summary
|
658
|
+
# Converts newlines (`\n`) in a string to HTML line breaks (`<br>`).
|
659
|
+
# @liquid_syntax string | newline_to_br
|
660
|
+
# @liquid_return [string]
|
359
661
|
def newline_to_br(input)
|
360
662
|
input.to_s.gsub(/\r?\n/, "<br />\n")
|
361
663
|
end
|
@@ -399,58 +701,106 @@ module Liquid
|
|
399
701
|
date.strftime(format.to_s)
|
400
702
|
end
|
401
703
|
|
402
|
-
#
|
403
|
-
#
|
404
|
-
#
|
405
|
-
#
|
406
|
-
#
|
704
|
+
# @liquid_public_docs
|
705
|
+
# @liquid_type filter
|
706
|
+
# @liquid_category array
|
707
|
+
# @liquid_summary
|
708
|
+
# Returns the first item in an array.
|
709
|
+
# @liquid_syntax array | first
|
710
|
+
# @liquid_return [untyped]
|
407
711
|
def first(array)
|
408
712
|
array.first if array.respond_to?(:first)
|
409
713
|
end
|
410
714
|
|
411
|
-
#
|
412
|
-
#
|
413
|
-
#
|
414
|
-
#
|
415
|
-
#
|
715
|
+
# @liquid_public_docs
|
716
|
+
# @liquid_type filter
|
717
|
+
# @liquid_category array
|
718
|
+
# @liquid_summary
|
719
|
+
# Returns the last item in an array.
|
720
|
+
# @liquid_syntax array | last
|
721
|
+
# @liquid_return [untyped]
|
416
722
|
def last(array)
|
417
723
|
array.last if array.respond_to?(:last)
|
418
724
|
end
|
419
725
|
|
420
|
-
#
|
726
|
+
# @liquid_public_docs
|
727
|
+
# @liquid_type filter
|
728
|
+
# @liquid_category math
|
729
|
+
# @liquid_summary
|
730
|
+
# Returns the absolute value of a number.
|
731
|
+
# @liquid_syntax number | abs
|
732
|
+
# @liquid_return [number]
|
421
733
|
def abs(input)
|
422
734
|
result = Utils.to_number(input).abs
|
423
735
|
result.is_a?(BigDecimal) ? result.to_f : result
|
424
736
|
end
|
425
737
|
|
426
|
-
#
|
738
|
+
# @liquid_public_docs
|
739
|
+
# @liquid_type filter
|
740
|
+
# @liquid_category math
|
741
|
+
# @liquid_summary
|
742
|
+
# Adds two numbers.
|
743
|
+
# @liquid_syntax number | plus: number
|
744
|
+
# @liquid_return [number]
|
427
745
|
def plus(input, operand)
|
428
746
|
apply_operation(input, operand, :+)
|
429
747
|
end
|
430
748
|
|
431
|
-
#
|
749
|
+
# @liquid_public_docs
|
750
|
+
# @liquid_type filter
|
751
|
+
# @liquid_category math
|
752
|
+
# @liquid_summary
|
753
|
+
# Subtracts a given number from another number.
|
754
|
+
# @liquid_syntax number | minus: number
|
755
|
+
# @liquid_return [number]
|
432
756
|
def minus(input, operand)
|
433
757
|
apply_operation(input, operand, :-)
|
434
758
|
end
|
435
759
|
|
436
|
-
#
|
760
|
+
# @liquid_public_docs
|
761
|
+
# @liquid_type filter
|
762
|
+
# @liquid_category math
|
763
|
+
# @liquid_summary
|
764
|
+
# Multiplies a number by a given number.
|
765
|
+
# @liquid_syntax number | times: number
|
766
|
+
# @liquid_return [number]
|
437
767
|
def times(input, operand)
|
438
768
|
apply_operation(input, operand, :*)
|
439
769
|
end
|
440
770
|
|
441
|
-
#
|
771
|
+
# @liquid_public_docs
|
772
|
+
# @liquid_type filter
|
773
|
+
# @liquid_category math
|
774
|
+
# @liquid_summary
|
775
|
+
# Divides a number by a given number. The `divided_by` filter produces a result of the same type as the divisor. This means if you divide by an integer, the result will be an integer, and if you divide by a float, the result will be a float.
|
776
|
+
# @liquid_syntax number | divided_by: number
|
777
|
+
# @liquid_return [number]
|
442
778
|
def divided_by(input, operand)
|
443
779
|
apply_operation(input, operand, :/)
|
444
780
|
rescue ::ZeroDivisionError => e
|
445
781
|
raise Liquid::ZeroDivisionError, e.message
|
446
782
|
end
|
447
783
|
|
784
|
+
# @liquid_public_docs
|
785
|
+
# @liquid_type filter
|
786
|
+
# @liquid_category math
|
787
|
+
# @liquid_summary
|
788
|
+
# Returns the remainder of dividing a number by a given number.
|
789
|
+
# @liquid_syntax number | modulo: number
|
790
|
+
# @liquid_return [number]
|
448
791
|
def modulo(input, operand)
|
449
792
|
apply_operation(input, operand, :%)
|
450
793
|
rescue ::ZeroDivisionError => e
|
451
794
|
raise Liquid::ZeroDivisionError, e.message
|
452
795
|
end
|
453
796
|
|
797
|
+
# @liquid_public_docs
|
798
|
+
# @liquid_type filter
|
799
|
+
# @liquid_category math
|
800
|
+
# @liquid_summary
|
801
|
+
# Rounds a number to the nearest integer.
|
802
|
+
# @liquid_syntax number | round
|
803
|
+
# @liquid_return [number]
|
454
804
|
def round(input, n = 0)
|
455
805
|
result = Utils.to_number(input).round(Utils.to_number(n))
|
456
806
|
result = result.to_f if result.is_a?(BigDecimal)
|
@@ -460,18 +810,39 @@ module Liquid
|
|
460
810
|
raise Liquid::FloatDomainError, e.message
|
461
811
|
end
|
462
812
|
|
813
|
+
# @liquid_public_docs
|
814
|
+
# @liquid_type filter
|
815
|
+
# @liquid_category math
|
816
|
+
# @liquid_summary
|
817
|
+
# Rounds a number up to the nearest integer.
|
818
|
+
# @liquid_syntax number | ceil
|
819
|
+
# @liquid_return [number]
|
463
820
|
def ceil(input)
|
464
821
|
Utils.to_number(input).ceil.to_i
|
465
822
|
rescue ::FloatDomainError => e
|
466
823
|
raise Liquid::FloatDomainError, e.message
|
467
824
|
end
|
468
825
|
|
826
|
+
# @liquid_public_docs
|
827
|
+
# @liquid_type filter
|
828
|
+
# @liquid_category math
|
829
|
+
# @liquid_summary
|
830
|
+
# Rounds a number down to the nearest integer.
|
831
|
+
# @liquid_syntax number | floor
|
832
|
+
# @liquid_return [number]
|
469
833
|
def floor(input)
|
470
834
|
Utils.to_number(input).floor.to_i
|
471
835
|
rescue ::FloatDomainError => e
|
472
836
|
raise Liquid::FloatDomainError, e.message
|
473
837
|
end
|
474
838
|
|
839
|
+
# @liquid_public_docs
|
840
|
+
# @liquid_type filter
|
841
|
+
# @liquid_category math
|
842
|
+
# @liquid_summary
|
843
|
+
# Limits a number to a minimum value.
|
844
|
+
# @liquid_syntax number | at_least
|
845
|
+
# @liquid_return [number]
|
475
846
|
def at_least(input, n)
|
476
847
|
min_value = Utils.to_number(n)
|
477
848
|
|
@@ -480,6 +851,13 @@ module Liquid
|
|
480
851
|
result.is_a?(BigDecimal) ? result.to_f : result
|
481
852
|
end
|
482
853
|
|
854
|
+
# @liquid_public_docs
|
855
|
+
# @liquid_type filter
|
856
|
+
# @liquid_category math
|
857
|
+
# @liquid_summary
|
858
|
+
# Limits a number to a maximum value.
|
859
|
+
# @liquid_syntax number | at_most
|
860
|
+
# @liquid_return [number]
|
483
861
|
def at_most(input, n)
|
484
862
|
max_value = Utils.to_number(n)
|
485
863
|
|
@@ -488,22 +866,54 @@ module Liquid
|
|
488
866
|
result.is_a?(BigDecimal) ? result.to_f : result
|
489
867
|
end
|
490
868
|
|
491
|
-
#
|
492
|
-
#
|
493
|
-
#
|
494
|
-
#
|
495
|
-
#
|
496
|
-
# Use `allow_false` when an input should only be tested against nil or empty and not false.
|
497
|
-
#
|
498
|
-
# Example:
|
499
|
-
# {{ product.title | default: "No Title", allow_false: true }}
|
869
|
+
# @liquid_public_docs
|
870
|
+
# @liquid_type filter
|
871
|
+
# @liquid_category default
|
872
|
+
# @liquid_summary
|
873
|
+
# Sets a default value for any variable whose value is one of the following:
|
500
874
|
#
|
875
|
+
# - [`empty`](/docs/api/liquid/basics#empty)
|
876
|
+
# - [`false`](/docs/api/liquid/basics#truthy-and-falsy)
|
877
|
+
# - [`nil`](/docs/api/liquid/basics#nil)
|
878
|
+
# @liquid_syntax variable | default: variable
|
879
|
+
# @liquid_return [untyped]
|
880
|
+
# @liquid_optional_param allow_false [boolean] Whether to use false values instead of the default.
|
501
881
|
def default(input, default_value = '', options = {})
|
502
882
|
options = {} unless options.is_a?(Hash)
|
503
883
|
false_check = options['allow_false'] ? input.nil? : !Liquid::Utils.to_liquid_value(input)
|
504
884
|
false_check || (input.respond_to?(:empty?) && input.empty?) ? default_value : input
|
505
885
|
end
|
506
886
|
|
887
|
+
# @liquid_public_docs
|
888
|
+
# @liquid_type filter
|
889
|
+
# @liquid_category array
|
890
|
+
# @liquid_summary
|
891
|
+
# Returns the sum of all elements in an array.
|
892
|
+
# @liquid_syntax array | sum
|
893
|
+
# @liquid_return [number]
|
894
|
+
def sum(input, property = nil)
|
895
|
+
ary = InputIterator.new(input, context)
|
896
|
+
return 0 if ary.empty?
|
897
|
+
|
898
|
+
values_for_sum = ary.map do |item|
|
899
|
+
if property.nil?
|
900
|
+
item
|
901
|
+
elsif item.respond_to?(:[])
|
902
|
+
item[property]
|
903
|
+
else
|
904
|
+
0
|
905
|
+
end
|
906
|
+
rescue TypeError
|
907
|
+
raise_property_error(property)
|
908
|
+
end
|
909
|
+
|
910
|
+
result = InputIterator.new(values_for_sum, context).sum do |item|
|
911
|
+
Utils.to_number(item)
|
912
|
+
end
|
913
|
+
|
914
|
+
result.is_a?(BigDecimal) ? result.to_f : result
|
915
|
+
end
|
916
|
+
|
507
917
|
private
|
508
918
|
|
509
919
|
attr_reader :context
|
@@ -534,6 +944,8 @@ module Liquid
|
|
534
944
|
def nil_safe_casecmp(a, b)
|
535
945
|
if !a.nil? && !b.nil?
|
536
946
|
a.to_s.casecmp(b.to_s)
|
947
|
+
elsif a.nil? && b.nil?
|
948
|
+
0
|
537
949
|
else
|
538
950
|
a.nil? ? 1 : -1
|
539
951
|
end
|