liquid 4.0.1 → 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 +4 -4
- data/History.md +142 -0
- data/README.md +10 -4
- data/lib/liquid/block.rb +31 -14
- data/lib/liquid/block_body.rb +169 -56
- data/lib/liquid/condition.rb +59 -23
- data/lib/liquid/context.rb +111 -52
- data/lib/liquid/document.rb +47 -9
- data/lib/liquid/drop.rb +4 -2
- data/lib/liquid/errors.rb +20 -18
- data/lib/liquid/expression.rb +29 -33
- data/lib/liquid/extensions.rb +2 -0
- data/lib/liquid/file_system.rb +6 -4
- data/lib/liquid/forloop_drop.rb +54 -4
- data/lib/liquid/i18n.rb +5 -3
- data/lib/liquid/interrupts.rb +3 -1
- data/lib/liquid/lexer.rb +31 -24
- data/lib/liquid/locales/en.yml +8 -5
- data/lib/liquid/parse_context.rb +20 -4
- data/lib/liquid/parse_tree_visitor.rb +42 -0
- data/lib/liquid/parser.rb +30 -18
- data/lib/liquid/parser_switching.rb +17 -3
- data/lib/liquid/partial_cache.rb +24 -0
- data/lib/liquid/profiler/hooks.rb +26 -14
- data/lib/liquid/profiler.rb +67 -86
- data/lib/liquid/range_lookup.rb +13 -3
- data/lib/liquid/registers.rb +51 -0
- data/lib/liquid/resource_limits.rb +47 -8
- data/lib/liquid/standardfilters.rb +616 -129
- data/lib/liquid/strainer_factory.rb +41 -0
- data/lib/liquid/strainer_template.rb +62 -0
- data/lib/liquid/tablerowloop_drop.rb +64 -5
- data/lib/liquid/tag/disableable.rb +22 -0
- data/lib/liquid/tag/disabler.rb +21 -0
- data/lib/liquid/tag.rb +28 -6
- data/lib/liquid/tags/assign.rb +44 -18
- data/lib/liquid/tags/break.rb +16 -3
- data/lib/liquid/tags/capture.rb +24 -18
- data/lib/liquid/tags/case.rb +69 -27
- data/lib/liquid/tags/comment.rb +18 -3
- data/lib/liquid/tags/continue.rb +16 -12
- data/lib/liquid/tags/cycle.rb +45 -25
- data/lib/liquid/tags/decrement.rb +22 -20
- data/lib/liquid/tags/echo.rb +41 -0
- data/lib/liquid/tags/for.rb +97 -89
- data/lib/liquid/tags/if.rb +61 -35
- data/lib/liquid/tags/ifchanged.rb +11 -10
- data/lib/liquid/tags/include.rb +56 -56
- data/lib/liquid/tags/increment.rb +23 -17
- data/lib/liquid/tags/inline_comment.rb +43 -0
- data/lib/liquid/tags/raw.rb +25 -11
- data/lib/liquid/tags/render.rb +109 -0
- data/lib/liquid/tags/table_row.rb +53 -19
- data/lib/liquid/tags/unless.rb +38 -19
- data/lib/liquid/template.rb +52 -72
- data/lib/liquid/template_factory.rb +9 -0
- data/lib/liquid/tokenizer.rb +18 -10
- data/lib/liquid/usage.rb +8 -0
- data/lib/liquid/utils.rb +13 -3
- data/lib/liquid/variable.rb +52 -41
- data/lib/liquid/variable_lookup.rb +24 -10
- data/lib/liquid/version.rb +3 -1
- data/lib/liquid.rb +19 -6
- metadata +21 -104
- data/lib/liquid/strainer.rb +0 -66
- data/test/fixtures/en_locale.yml +0 -9
- data/test/integration/assign_test.rb +0 -48
- data/test/integration/blank_test.rb +0 -106
- data/test/integration/block_test.rb +0 -12
- data/test/integration/capture_test.rb +0 -50
- data/test/integration/context_test.rb +0 -32
- data/test/integration/document_test.rb +0 -19
- data/test/integration/drop_test.rb +0 -273
- data/test/integration/error_handling_test.rb +0 -260
- data/test/integration/filter_test.rb +0 -178
- data/test/integration/hash_ordering_test.rb +0 -23
- data/test/integration/output_test.rb +0 -123
- data/test/integration/parsing_quirks_test.rb +0 -122
- data/test/integration/render_profiling_test.rb +0 -154
- data/test/integration/security_test.rb +0 -80
- data/test/integration/standard_filter_test.rb +0 -626
- data/test/integration/tags/break_tag_test.rb +0 -15
- data/test/integration/tags/continue_tag_test.rb +0 -15
- data/test/integration/tags/for_tag_test.rb +0 -410
- data/test/integration/tags/if_else_tag_test.rb +0 -188
- data/test/integration/tags/include_tag_test.rb +0 -245
- data/test/integration/tags/increment_tag_test.rb +0 -23
- data/test/integration/tags/raw_tag_test.rb +0 -31
- data/test/integration/tags/standard_tag_test.rb +0 -296
- data/test/integration/tags/statements_test.rb +0 -111
- data/test/integration/tags/table_row_test.rb +0 -64
- data/test/integration/tags/unless_else_tag_test.rb +0 -26
- data/test/integration/template_test.rb +0 -332
- data/test/integration/trim_mode_test.rb +0 -529
- data/test/integration/variable_test.rb +0 -96
- data/test/test_helper.rb +0 -116
- data/test/unit/block_unit_test.rb +0 -58
- data/test/unit/condition_unit_test.rb +0 -166
- data/test/unit/context_unit_test.rb +0 -489
- data/test/unit/file_system_unit_test.rb +0 -35
- data/test/unit/i18n_unit_test.rb +0 -37
- data/test/unit/lexer_unit_test.rb +0 -51
- data/test/unit/parser_unit_test.rb +0 -82
- data/test/unit/regexp_unit_test.rb +0 -44
- data/test/unit/strainer_unit_test.rb +0 -164
- data/test/unit/tag_unit_test.rb +0 -21
- data/test/unit/tags/case_tag_unit_test.rb +0 -10
- data/test/unit/tags/for_tag_unit_test.rb +0 -13
- data/test/unit/tags/if_tag_unit_test.rb +0 -8
- data/test/unit/template_unit_test.rb +0 -78
- data/test/unit/tokenizer_unit_test.rb +0 -55
- data/test/unit/variable_unit_test.rb +0 -162
@@ -1,54 +1,187 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'cgi'
|
4
|
+
require 'base64'
|
2
5
|
require 'bigdecimal'
|
3
6
|
|
4
7
|
module Liquid
|
5
8
|
module StandardFilters
|
9
|
+
MAX_INT = (1 << 31) - 1
|
6
10
|
HTML_ESCAPE = {
|
7
|
-
'&'
|
8
|
-
'>'
|
9
|
-
'<'
|
10
|
-
'"'
|
11
|
-
"'"
|
12
|
-
}
|
11
|
+
'&' => '&',
|
12
|
+
'>' => '>',
|
13
|
+
'<' => '<',
|
14
|
+
'"' => '"',
|
15
|
+
"'" => ''',
|
16
|
+
}.freeze
|
13
17
|
HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+));)/
|
14
|
-
|
15
|
-
|
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]
|
16
35
|
def size(input)
|
17
36
|
input.respond_to?(:size) ? input.size : 0
|
18
37
|
end
|
19
38
|
|
20
|
-
#
|
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]
|
21
46
|
def downcase(input)
|
22
47
|
input.to_s.downcase
|
23
48
|
end
|
24
49
|
|
25
|
-
#
|
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]
|
26
57
|
def upcase(input)
|
27
58
|
input.to_s.upcase
|
28
59
|
end
|
29
60
|
|
30
|
-
#
|
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]
|
31
68
|
def capitalize(input)
|
32
69
|
input.to_s.capitalize
|
33
70
|
end
|
34
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]
|
35
79
|
def escape(input)
|
36
|
-
CGI.escapeHTML(input.to_s)
|
80
|
+
CGI.escapeHTML(input.to_s) unless input.nil?
|
37
81
|
end
|
38
82
|
alias_method :h, :escape
|
39
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]
|
40
91
|
def escape_once(input)
|
41
92
|
input.to_s.gsub(HTML_ESCAPE_ONCE_REGEXP, HTML_ESCAPE)
|
42
93
|
end
|
43
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]
|
44
106
|
def url_encode(input)
|
45
107
|
CGI.escape(input.to_s) unless input.nil?
|
46
108
|
end
|
47
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]
|
48
118
|
def url_decode(input)
|
49
|
-
|
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
|
50
125
|
end
|
51
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]
|
52
185
|
def slice(input, offset, length = nil)
|
53
186
|
offset = Utils.to_integer(offset)
|
54
187
|
length = length ? Utils.to_integer(length) : 1
|
@@ -60,205 +193,442 @@ module Liquid
|
|
60
193
|
end
|
61
194
|
end
|
62
195
|
|
63
|
-
#
|
64
|
-
|
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]
|
206
|
+
def truncate(input, length = 50, truncate_string = "...")
|
65
207
|
return if input.nil?
|
66
208
|
input_str = input.to_s
|
67
|
-
length
|
209
|
+
length = Utils.to_integer(length)
|
210
|
+
|
68
211
|
truncate_string_str = truncate_string.to_s
|
212
|
+
|
69
213
|
l = length - truncate_string_str.length
|
70
214
|
l = 0 if l < 0
|
71
|
-
|
215
|
+
|
216
|
+
input_str.length > length ? input_str[0...l].concat(truncate_string_str) : input_str
|
72
217
|
end
|
73
218
|
|
74
|
-
|
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.
|
227
|
+
#
|
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 = "...")
|
75
234
|
return if input.nil?
|
76
|
-
|
235
|
+
input = input.to_s
|
77
236
|
words = Utils.to_integer(words)
|
78
|
-
|
79
|
-
|
80
|
-
wordlist
|
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
|
247
|
+
|
248
|
+
wordlist.pop
|
249
|
+
wordlist.join(" ").concat(truncate_string.to_s)
|
81
250
|
end
|
82
251
|
|
83
|
-
#
|
84
|
-
#
|
85
|
-
#
|
86
|
-
#
|
87
|
-
#
|
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]]
|
88
259
|
def split(input, pattern)
|
89
260
|
input.to_s.split(pattern.to_s)
|
90
261
|
end
|
91
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]
|
92
270
|
def strip(input)
|
93
271
|
input.to_s.strip
|
94
272
|
end
|
95
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]
|
96
281
|
def lstrip(input)
|
97
282
|
input.to_s.lstrip
|
98
283
|
end
|
99
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]
|
100
292
|
def rstrip(input)
|
101
293
|
input.to_s.rstrip
|
102
294
|
end
|
103
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]
|
104
303
|
def strip_html(input)
|
105
|
-
empty
|
106
|
-
input.to_s.gsub(
|
304
|
+
empty = ''
|
305
|
+
result = input.to_s.gsub(STRIP_HTML_BLOCKS, empty)
|
306
|
+
result.gsub!(STRIP_HTML_TAGS, empty)
|
307
|
+
result
|
107
308
|
end
|
108
309
|
|
109
|
-
#
|
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]
|
110
317
|
def strip_newlines(input)
|
111
|
-
input.to_s.gsub(/\r?\n/, ''
|
112
|
-
end
|
318
|
+
input.to_s.gsub(/\r?\n/, '')
|
319
|
+
end
|
320
|
+
|
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]
|
328
|
+
def join(input, glue = ' ')
|
329
|
+
InputIterator.new(input, context).join(glue)
|
330
|
+
end
|
331
|
+
|
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]]
|
339
|
+
def sort(input, property = nil)
|
340
|
+
ary = InputIterator.new(input, context)
|
113
341
|
|
114
|
-
|
115
|
-
def join(input, glue = ' '.freeze)
|
116
|
-
InputIterator.new(input).join(glue)
|
117
|
-
end
|
342
|
+
return [] if ary.empty?
|
118
343
|
|
119
|
-
# Sort elements of the array
|
120
|
-
# provide optional property with which to sort an array of hashes or drops
|
121
|
-
def sort(input, property = nil)
|
122
|
-
ary = InputIterator.new(input)
|
123
344
|
if property.nil?
|
124
345
|
ary.sort do |a, b|
|
125
|
-
|
126
|
-
a <=> b
|
127
|
-
else
|
128
|
-
a.nil? ? 1 : -1
|
129
|
-
end
|
346
|
+
nil_safe_compare(a, b)
|
130
347
|
end
|
131
|
-
elsif ary.empty? # The next two cases assume a non-empty array.
|
132
|
-
[]
|
133
348
|
elsif ary.all? { |el| el.respond_to?(:[]) }
|
134
|
-
|
135
|
-
a
|
136
|
-
|
137
|
-
|
138
|
-
a <=> b
|
139
|
-
else
|
140
|
-
a.nil? ? 1 : -1
|
141
|
-
end
|
349
|
+
begin
|
350
|
+
ary.sort { |a, b| nil_safe_compare(a[property], b[property]) }
|
351
|
+
rescue TypeError
|
352
|
+
raise_property_error(property)
|
142
353
|
end
|
143
354
|
end
|
144
355
|
end
|
145
356
|
|
146
|
-
#
|
147
|
-
#
|
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]]
|
148
368
|
def sort_natural(input, property = nil)
|
149
|
-
ary = InputIterator.new(input)
|
369
|
+
ary = InputIterator.new(input, context)
|
370
|
+
|
371
|
+
return [] if ary.empty?
|
150
372
|
|
151
373
|
if property.nil?
|
152
374
|
ary.sort do |a, b|
|
153
|
-
|
154
|
-
a.to_s.casecmp(b.to_s)
|
155
|
-
else
|
156
|
-
a.nil? ? 1 : -1
|
157
|
-
end
|
375
|
+
nil_safe_casecmp(a, b)
|
158
376
|
end
|
159
|
-
elsif ary.empty? # The next two cases assume a non-empty array.
|
160
|
-
[]
|
161
377
|
elsif ary.all? { |el| el.respond_to?(:[]) }
|
162
|
-
|
163
|
-
a
|
164
|
-
|
165
|
-
|
166
|
-
a.to_s.casecmp(b.to_s)
|
167
|
-
else
|
168
|
-
a.nil? ? 1 : -1
|
169
|
-
end
|
378
|
+
begin
|
379
|
+
ary.sort { |a, b| nil_safe_casecmp(a[property], b[property]) }
|
380
|
+
rescue TypeError
|
381
|
+
raise_property_error(property)
|
170
382
|
end
|
171
383
|
end
|
172
384
|
end
|
173
385
|
|
174
|
-
#
|
175
|
-
#
|
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]]
|
176
428
|
def uniq(input, property = nil)
|
177
|
-
ary = InputIterator.new(input)
|
429
|
+
ary = InputIterator.new(input, context)
|
178
430
|
|
179
431
|
if property.nil?
|
180
432
|
ary.uniq
|
181
433
|
elsif ary.empty? # The next two cases assume a non-empty array.
|
182
434
|
[]
|
183
|
-
|
184
|
-
ary.uniq
|
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
|
185
444
|
end
|
186
445
|
end
|
187
446
|
|
188
|
-
#
|
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]]
|
189
454
|
def reverse(input)
|
190
|
-
ary = InputIterator.new(input)
|
455
|
+
ary = InputIterator.new(input, context)
|
191
456
|
ary.reverse
|
192
457
|
end
|
193
458
|
|
194
|
-
#
|
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]]
|
195
466
|
def map(input, property)
|
196
|
-
InputIterator.new(input).map do |e|
|
467
|
+
InputIterator.new(input, context).map do |e|
|
197
468
|
e = e.call if e.is_a?(Proc)
|
198
469
|
|
199
|
-
if property == "to_liquid"
|
470
|
+
if property == "to_liquid"
|
200
471
|
e
|
201
472
|
elsif e.respond_to?(:[])
|
202
473
|
r = e[property]
|
203
474
|
r.is_a?(Proc) ? r.call : r
|
204
475
|
end
|
205
476
|
end
|
477
|
+
rescue TypeError
|
478
|
+
raise_property_error(property)
|
206
479
|
end
|
207
480
|
|
208
|
-
#
|
209
|
-
#
|
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]]
|
210
488
|
def compact(input, property = nil)
|
211
|
-
ary = InputIterator.new(input)
|
489
|
+
ary = InputIterator.new(input, context)
|
212
490
|
|
213
491
|
if property.nil?
|
214
492
|
ary.compact
|
215
493
|
elsif ary.empty? # The next two cases assume a non-empty array.
|
216
494
|
[]
|
217
|
-
|
218
|
-
ary.reject
|
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
|
219
504
|
end
|
220
505
|
end
|
221
506
|
|
222
|
-
#
|
223
|
-
|
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]
|
514
|
+
def replace(input, string, replacement = '')
|
224
515
|
input.to_s.gsub(string.to_s, replacement.to_s)
|
225
516
|
end
|
226
517
|
|
227
|
-
#
|
228
|
-
|
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]
|
525
|
+
def replace_first(input, string, replacement = '')
|
229
526
|
input.to_s.sub(string.to_s, replacement.to_s)
|
230
527
|
end
|
231
528
|
|
232
|
-
#
|
233
|
-
|
234
|
-
|
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
|
235
548
|
end
|
236
549
|
|
237
|
-
#
|
238
|
-
|
239
|
-
|
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, '')
|
240
559
|
end
|
241
560
|
|
242
|
-
#
|
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]
|
243
590
|
def append(input, string)
|
244
591
|
input.to_s + string.to_s
|
245
592
|
end
|
246
593
|
|
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]]
|
247
605
|
def concat(input, array)
|
248
606
|
unless array.respond_to?(:to_ary)
|
249
|
-
raise ArgumentError
|
607
|
+
raise ArgumentError, "concat filter requires an array argument"
|
250
608
|
end
|
251
|
-
InputIterator.new(input).concat(array)
|
609
|
+
InputIterator.new(input, context).concat(array)
|
252
610
|
end
|
253
611
|
|
254
|
-
#
|
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]
|
255
619
|
def prepend(input, string)
|
256
620
|
string.to_s + input.to_s
|
257
621
|
end
|
258
622
|
|
259
|
-
#
|
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]
|
260
630
|
def newline_to_br(input)
|
261
|
-
input.to_s.gsub(/\n/, "<br />\n"
|
631
|
+
input.to_s.gsub(/\r?\n/, "<br />\n")
|
262
632
|
end
|
263
633
|
|
264
634
|
# Reformat a date using Ruby's core Time#strftime( string ) -> string
|
@@ -295,63 +665,111 @@ module Liquid
|
|
295
665
|
def date(input, format)
|
296
666
|
return input if format.to_s.empty?
|
297
667
|
|
298
|
-
return input unless date = Utils.to_date(input)
|
668
|
+
return input unless (date = Utils.to_date(input))
|
299
669
|
|
300
670
|
date.strftime(format.to_s)
|
301
671
|
end
|
302
672
|
|
303
|
-
#
|
304
|
-
#
|
305
|
-
#
|
306
|
-
#
|
307
|
-
#
|
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]
|
308
680
|
def first(array)
|
309
681
|
array.first if array.respond_to?(:first)
|
310
682
|
end
|
311
683
|
|
312
|
-
#
|
313
|
-
#
|
314
|
-
#
|
315
|
-
#
|
316
|
-
#
|
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]
|
317
691
|
def last(array)
|
318
692
|
array.last if array.respond_to?(:last)
|
319
693
|
end
|
320
694
|
|
321
|
-
#
|
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]
|
322
702
|
def abs(input)
|
323
703
|
result = Utils.to_number(input).abs
|
324
704
|
result.is_a?(BigDecimal) ? result.to_f : result
|
325
705
|
end
|
326
706
|
|
327
|
-
#
|
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]
|
328
714
|
def plus(input, operand)
|
329
715
|
apply_operation(input, operand, :+)
|
330
716
|
end
|
331
717
|
|
332
|
-
#
|
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]
|
333
725
|
def minus(input, operand)
|
334
726
|
apply_operation(input, operand, :-)
|
335
727
|
end
|
336
728
|
|
337
|
-
#
|
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]
|
338
736
|
def times(input, operand)
|
339
737
|
apply_operation(input, operand, :*)
|
340
738
|
end
|
341
739
|
|
342
|
-
#
|
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]
|
343
747
|
def divided_by(input, operand)
|
344
748
|
apply_operation(input, operand, :/)
|
345
749
|
rescue ::ZeroDivisionError => e
|
346
750
|
raise Liquid::ZeroDivisionError, e.message
|
347
751
|
end
|
348
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]
|
349
760
|
def modulo(input, operand)
|
350
761
|
apply_operation(input, operand, :%)
|
351
762
|
rescue ::ZeroDivisionError => e
|
352
763
|
raise Liquid::ZeroDivisionError, e.message
|
353
764
|
end
|
354
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]
|
355
773
|
def round(input, n = 0)
|
356
774
|
result = Utils.to_number(input).round(Utils.to_number(n))
|
357
775
|
result = result.to_f if result.is_a?(BigDecimal)
|
@@ -361,18 +779,39 @@ module Liquid
|
|
361
779
|
raise Liquid::FloatDomainError, e.message
|
362
780
|
end
|
363
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]
|
364
789
|
def ceil(input)
|
365
790
|
Utils.to_number(input).ceil.to_i
|
366
791
|
rescue ::FloatDomainError => e
|
367
792
|
raise Liquid::FloatDomainError, e.message
|
368
793
|
end
|
369
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]
|
370
802
|
def floor(input)
|
371
803
|
Utils.to_number(input).floor.to_i
|
372
804
|
rescue ::FloatDomainError => e
|
373
805
|
raise Liquid::FloatDomainError, e.message
|
374
806
|
end
|
375
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]
|
376
815
|
def at_least(input, n)
|
377
816
|
min_value = Utils.to_number(n)
|
378
817
|
|
@@ -381,6 +820,13 @@ module Liquid
|
|
381
820
|
result.is_a?(BigDecimal) ? result.to_f : result
|
382
821
|
end
|
383
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]
|
384
830
|
def at_most(input, n)
|
385
831
|
max_value = Utils.to_number(n)
|
386
832
|
|
@@ -389,26 +835,65 @@ module Liquid
|
|
389
835
|
result.is_a?(BigDecimal) ? result.to_f : result
|
390
836
|
end
|
391
837
|
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
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
|
398
854
|
end
|
399
855
|
|
400
856
|
private
|
401
857
|
|
858
|
+
attr_reader :context
|
859
|
+
|
860
|
+
def raise_property_error(property)
|
861
|
+
raise Liquid::ArgumentError, "cannot select the property '#{property}'"
|
862
|
+
end
|
863
|
+
|
402
864
|
def apply_operation(input, operand, operation)
|
403
865
|
result = Utils.to_number(input).send(operation, Utils.to_number(operand))
|
404
866
|
result.is_a?(BigDecimal) ? result.to_f : result
|
405
867
|
end
|
406
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
|
+
|
407
891
|
class InputIterator
|
408
892
|
include Enumerable
|
409
893
|
|
410
|
-
def initialize(input)
|
411
|
-
@
|
894
|
+
def initialize(input, context)
|
895
|
+
@context = context
|
896
|
+
@input = if input.is_a?(Array)
|
412
897
|
input.flatten
|
413
898
|
elsif input.is_a?(Hash)
|
414
899
|
[input]
|
@@ -446,7 +931,9 @@ module Liquid
|
|
446
931
|
|
447
932
|
def each
|
448
933
|
@input.each do |e|
|
449
|
-
|
934
|
+
e = e.respond_to?(:to_liquid) ? e.to_liquid : e
|
935
|
+
e.context = @context if e.respond_to?(:context=)
|
936
|
+
yield(e)
|
450
937
|
end
|
451
938
|
end
|
452
939
|
end
|