liquid 5.1.0 → 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.
Files changed (98) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +35 -0
  3. data/README.md +4 -4
  4. data/lib/liquid/block_body.rb +6 -6
  5. data/lib/liquid/condition.rb +7 -1
  6. data/lib/liquid/context.rb +6 -2
  7. data/lib/liquid/expression.rb +11 -10
  8. data/lib/liquid/forloop_drop.rb +44 -1
  9. data/lib/liquid/locales/en.yml +6 -5
  10. data/lib/liquid/partial_cache.rb +3 -3
  11. data/lib/liquid/registers.rb +51 -0
  12. data/lib/liquid/standardfilters.rb +463 -75
  13. data/lib/liquid/strainer_factory.rb +15 -10
  14. data/lib/liquid/strainer_template.rb +9 -0
  15. data/lib/liquid/tablerowloop_drop.rb +58 -1
  16. data/lib/liquid/tags/assign.rb +12 -8
  17. data/lib/liquid/tags/break.rb +8 -0
  18. data/lib/liquid/tags/capture.rb +13 -10
  19. data/lib/liquid/tags/case.rb +21 -0
  20. data/lib/liquid/tags/comment.rb +13 -0
  21. data/lib/liquid/tags/continue.rb +8 -9
  22. data/lib/liquid/tags/cycle.rb +12 -11
  23. data/lib/liquid/tags/decrement.rb +16 -17
  24. data/lib/liquid/tags/echo.rb +16 -9
  25. data/lib/liquid/tags/for.rb +22 -43
  26. data/lib/liquid/tags/if.rb +11 -9
  27. data/lib/liquid/tags/include.rb +15 -13
  28. data/lib/liquid/tags/increment.rb +16 -14
  29. data/lib/liquid/tags/inline_comment.rb +43 -0
  30. data/lib/liquid/tags/raw.rb +11 -0
  31. data/lib/liquid/tags/render.rb +29 -4
  32. data/lib/liquid/tags/table_row.rb +22 -0
  33. data/lib/liquid/tags/unless.rb +15 -4
  34. data/lib/liquid/template.rb +2 -3
  35. data/lib/liquid/variable.rb +4 -4
  36. data/lib/liquid/variable_lookup.rb +10 -7
  37. data/lib/liquid/version.rb +1 -1
  38. data/lib/liquid.rb +4 -4
  39. metadata +7 -121
  40. data/lib/liquid/register.rb +0 -6
  41. data/lib/liquid/static_registers.rb +0 -44
  42. data/test/fixtures/en_locale.yml +0 -9
  43. data/test/integration/assign_test.rb +0 -117
  44. data/test/integration/blank_test.rb +0 -109
  45. data/test/integration/block_test.rb +0 -58
  46. data/test/integration/capture_test.rb +0 -58
  47. data/test/integration/context_test.rb +0 -636
  48. data/test/integration/document_test.rb +0 -21
  49. data/test/integration/drop_test.rb +0 -257
  50. data/test/integration/error_handling_test.rb +0 -272
  51. data/test/integration/expression_test.rb +0 -46
  52. data/test/integration/filter_test.rb +0 -189
  53. data/test/integration/hash_ordering_test.rb +0 -25
  54. data/test/integration/output_test.rb +0 -125
  55. data/test/integration/parsing_quirks_test.rb +0 -134
  56. data/test/integration/profiler_test.rb +0 -213
  57. data/test/integration/security_test.rb +0 -89
  58. data/test/integration/standard_filter_test.rb +0 -880
  59. data/test/integration/tag/disableable_test.rb +0 -59
  60. data/test/integration/tag_test.rb +0 -45
  61. data/test/integration/tags/break_tag_test.rb +0 -17
  62. data/test/integration/tags/continue_tag_test.rb +0 -17
  63. data/test/integration/tags/echo_test.rb +0 -13
  64. data/test/integration/tags/for_tag_test.rb +0 -466
  65. data/test/integration/tags/if_else_tag_test.rb +0 -190
  66. data/test/integration/tags/include_tag_test.rb +0 -269
  67. data/test/integration/tags/increment_tag_test.rb +0 -25
  68. data/test/integration/tags/liquid_tag_test.rb +0 -116
  69. data/test/integration/tags/raw_tag_test.rb +0 -34
  70. data/test/integration/tags/render_tag_test.rb +0 -213
  71. data/test/integration/tags/standard_tag_test.rb +0 -303
  72. data/test/integration/tags/statements_test.rb +0 -113
  73. data/test/integration/tags/table_row_test.rb +0 -66
  74. data/test/integration/tags/unless_else_tag_test.rb +0 -28
  75. data/test/integration/template_test.rb +0 -340
  76. data/test/integration/trim_mode_test.rb +0 -563
  77. data/test/integration/variable_test.rb +0 -138
  78. data/test/test_helper.rb +0 -207
  79. data/test/unit/block_unit_test.rb +0 -53
  80. data/test/unit/condition_unit_test.rb +0 -168
  81. data/test/unit/file_system_unit_test.rb +0 -37
  82. data/test/unit/i18n_unit_test.rb +0 -39
  83. data/test/unit/lexer_unit_test.rb +0 -53
  84. data/test/unit/parse_tree_visitor_test.rb +0 -261
  85. data/test/unit/parser_unit_test.rb +0 -84
  86. data/test/unit/partial_cache_unit_test.rb +0 -128
  87. data/test/unit/regexp_unit_test.rb +0 -46
  88. data/test/unit/static_registers_unit_test.rb +0 -156
  89. data/test/unit/strainer_factory_unit_test.rb +0 -100
  90. data/test/unit/strainer_template_unit_test.rb +0 -82
  91. data/test/unit/tag_unit_test.rb +0 -23
  92. data/test/unit/tags/case_tag_unit_test.rb +0 -12
  93. data/test/unit/tags/for_tag_unit_test.rb +0 -15
  94. data/test/unit/tags/if_tag_unit_test.rb +0 -10
  95. data/test/unit/template_factory_unit_test.rb +0 -12
  96. data/test/unit/template_unit_test.rb +0 -87
  97. data/test/unit/tokenizer_unit_test.rb +0 -62
  98. data/test/unit/variable_unit_test.rb +0 -164
@@ -1,880 +0,0 @@
1
- # encoding: utf-8
2
- # frozen_string_literal: true
3
-
4
- require 'test_helper'
5
-
6
- class Filters
7
- include Liquid::StandardFilters
8
- end
9
-
10
- class TestThing
11
- attr_reader :foo
12
-
13
- def initialize
14
- @foo = 0
15
- end
16
-
17
- def to_s
18
- "woot: #{@foo}"
19
- end
20
-
21
- def [](_whatever)
22
- to_s
23
- end
24
-
25
- def to_liquid
26
- @foo += 1
27
- self
28
- end
29
- end
30
-
31
- class TestDrop < Liquid::Drop
32
- def test
33
- "testfoo"
34
- end
35
- end
36
-
37
- class TestEnumerable < Liquid::Drop
38
- include Enumerable
39
-
40
- def each(&block)
41
- [{ "foo" => 1, "bar" => 2 }, { "foo" => 2, "bar" => 1 }, { "foo" => 3, "bar" => 3 }].each(&block)
42
- end
43
- end
44
-
45
- class NumberLikeThing < Liquid::Drop
46
- def initialize(amount)
47
- @amount = amount
48
- end
49
-
50
- def to_number
51
- @amount
52
- end
53
- end
54
-
55
- class StandardFiltersTest < Minitest::Test
56
- include Liquid
57
-
58
- def setup
59
- @filters = Filters.new
60
- end
61
-
62
- def test_size
63
- assert_equal(3, @filters.size([1, 2, 3]))
64
- assert_equal(0, @filters.size([]))
65
- assert_equal(0, @filters.size(nil))
66
- end
67
-
68
- def test_downcase
69
- assert_equal('testing', @filters.downcase("Testing"))
70
- assert_equal('', @filters.downcase(nil))
71
- end
72
-
73
- def test_upcase
74
- assert_equal('TESTING', @filters.upcase("Testing"))
75
- assert_equal('', @filters.upcase(nil))
76
- end
77
-
78
- def test_slice
79
- assert_equal('oob', @filters.slice('foobar', 1, 3))
80
- assert_equal('oobar', @filters.slice('foobar', 1, 1000))
81
- assert_equal('', @filters.slice('foobar', 1, 0))
82
- assert_equal('o', @filters.slice('foobar', 1, 1))
83
- assert_equal('bar', @filters.slice('foobar', 3, 3))
84
- assert_equal('ar', @filters.slice('foobar', -2, 2))
85
- assert_equal('ar', @filters.slice('foobar', -2, 1000))
86
- assert_equal('r', @filters.slice('foobar', -1))
87
- assert_equal('', @filters.slice(nil, 0))
88
- assert_equal('', @filters.slice('foobar', 100, 10))
89
- assert_equal('', @filters.slice('foobar', -100, 10))
90
- assert_equal('oob', @filters.slice('foobar', '1', '3'))
91
- assert_raises(Liquid::ArgumentError) do
92
- @filters.slice('foobar', nil)
93
- end
94
- assert_raises(Liquid::ArgumentError) do
95
- @filters.slice('foobar', 0, "")
96
- end
97
- end
98
-
99
- def test_slice_on_arrays
100
- input = 'foobar'.split(//)
101
- assert_equal(%w(o o b), @filters.slice(input, 1, 3))
102
- assert_equal(%w(o o b a r), @filters.slice(input, 1, 1000))
103
- assert_equal(%w(), @filters.slice(input, 1, 0))
104
- assert_equal(%w(o), @filters.slice(input, 1, 1))
105
- assert_equal(%w(b a r), @filters.slice(input, 3, 3))
106
- assert_equal(%w(a r), @filters.slice(input, -2, 2))
107
- assert_equal(%w(a r), @filters.slice(input, -2, 1000))
108
- assert_equal(%w(r), @filters.slice(input, -1))
109
- assert_equal(%w(), @filters.slice(input, 100, 10))
110
- assert_equal(%w(), @filters.slice(input, -100, 10))
111
- end
112
-
113
- def test_truncate
114
- assert_equal('1234...', @filters.truncate('1234567890', 7))
115
- assert_equal('1234567890', @filters.truncate('1234567890', 20))
116
- assert_equal('...', @filters.truncate('1234567890', 0))
117
- assert_equal('1234567890', @filters.truncate('1234567890'))
118
- assert_equal("测试...", @filters.truncate("测试测试测试测试", 5))
119
- assert_equal('12341', @filters.truncate("1234567890", 5, 1))
120
- end
121
-
122
- def test_split
123
- assert_equal(['12', '34'], @filters.split('12~34', '~'))
124
- assert_equal(['A? ', ' ,Z'], @filters.split('A? ~ ~ ~ ,Z', '~ ~ ~'))
125
- assert_equal(['A?Z'], @filters.split('A?Z', '~'))
126
- assert_equal([], @filters.split(nil, ' '))
127
- assert_equal(['A', 'Z'], @filters.split('A1Z', 1))
128
- end
129
-
130
- def test_escape
131
- assert_equal('&lt;strong&gt;', @filters.escape('<strong>'))
132
- assert_equal('1', @filters.escape(1))
133
- assert_equal('2001-02-03', @filters.escape(Date.new(2001, 2, 3)))
134
- assert_nil(@filters.escape(nil))
135
- end
136
-
137
- def test_h
138
- assert_equal('&lt;strong&gt;', @filters.h('<strong>'))
139
- assert_equal('1', @filters.h(1))
140
- assert_equal('2001-02-03', @filters.h(Date.new(2001, 2, 3)))
141
- assert_nil(@filters.h(nil))
142
- end
143
-
144
- def test_escape_once
145
- assert_equal('&lt;strong&gt;Hulk&lt;/strong&gt;', @filters.escape_once('&lt;strong&gt;Hulk</strong>'))
146
- end
147
-
148
- def test_base64_encode
149
- assert_equal('b25lIHR3byB0aHJlZQ==', @filters.base64_encode('one two three'))
150
- assert_equal('', @filters.base64_encode(nil))
151
- end
152
-
153
- def test_base64_decode
154
- assert_equal('one two three', @filters.base64_decode('b25lIHR3byB0aHJlZQ=='))
155
-
156
- exception = assert_raises(Liquid::ArgumentError) do
157
- @filters.base64_decode("invalidbase64")
158
- end
159
-
160
- assert_equal('Liquid error: invalid base64 provided to base64_decode', exception.message)
161
- end
162
-
163
- def test_base64_url_safe_encode
164
- assert_equal(
165
- 'YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXogQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVogMTIzNDU2Nzg5MCAhQCMkJV4mKigpLT1fKy8_Ljo7W117fVx8',
166
- @filters.base64_url_safe_encode('abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 !@#$%^&*()-=_+/?.:;[]{}\|')
167
- )
168
- assert_equal('', @filters.base64_url_safe_encode(nil))
169
- end
170
-
171
- def test_base64_url_safe_decode
172
- assert_equal(
173
- 'abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 !@#$%^&*()-=_+/?.:;[]{}\|',
174
- @filters.base64_url_safe_decode('YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXogQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVogMTIzNDU2Nzg5MCAhQCMkJV4mKigpLT1fKy8_Ljo7W117fVx8')
175
- )
176
- exception = assert_raises(Liquid::ArgumentError) do
177
- @filters.base64_url_safe_decode("invalidbase64")
178
- end
179
- assert_equal('Liquid error: invalid base64 provided to base64_url_safe_decode', exception.message)
180
- end
181
-
182
- def test_url_encode
183
- assert_equal('foo%2B1%40example.com', @filters.url_encode('foo+1@example.com'))
184
- assert_equal('1', @filters.url_encode(1))
185
- assert_equal('2001-02-03', @filters.url_encode(Date.new(2001, 2, 3)))
186
- assert_nil(@filters.url_encode(nil))
187
- end
188
-
189
- def test_url_decode
190
- assert_equal('foo bar', @filters.url_decode('foo+bar'))
191
- assert_equal('foo bar', @filters.url_decode('foo%20bar'))
192
- assert_equal('foo+1@example.com', @filters.url_decode('foo%2B1%40example.com'))
193
- assert_equal('1', @filters.url_decode(1))
194
- assert_equal('2001-02-03', @filters.url_decode(Date.new(2001, 2, 3)))
195
- assert_nil(@filters.url_decode(nil))
196
- exception = assert_raises(Liquid::ArgumentError) do
197
- @filters.url_decode('%ff')
198
- end
199
- assert_equal('Liquid error: invalid byte sequence in UTF-8', exception.message)
200
- end
201
-
202
- def test_truncatewords
203
- assert_equal('one two three', @filters.truncatewords('one two three', 4))
204
- assert_equal('one two...', @filters.truncatewords('one two three', 2))
205
- assert_equal('one two three', @filters.truncatewords('one two three'))
206
- assert_equal(
207
- 'Two small (13&#8221; x 5.5&#8221; x 10&#8221; high) baskets fit inside one large basket (13&#8221;...',
208
- @filters.truncatewords('Two small (13&#8221; x 5.5&#8221; x 10&#8221; high) baskets fit inside one large basket (13&#8221; x 16&#8221; x 10.5&#8221; high) with cover.', 15)
209
- )
210
- assert_equal("测试测试测试测试", @filters.truncatewords('测试测试测试测试', 5))
211
- assert_equal('one two1', @filters.truncatewords("one two three", 2, 1))
212
- assert_equal('one two three...', @filters.truncatewords("one two\tthree\nfour", 3))
213
- assert_equal('one two...', @filters.truncatewords("one two three four", 2))
214
- assert_equal('one...', @filters.truncatewords("one two three four", 0))
215
- exception = assert_raises(Liquid::ArgumentError) do
216
- @filters.truncatewords("one two three four", 1 << 31)
217
- end
218
- assert_equal("Liquid error: integer #{1 << 31} too big for truncatewords", exception.message)
219
- end
220
-
221
- def test_strip_html
222
- assert_equal('test', @filters.strip_html("<div>test</div>"))
223
- assert_equal('test', @filters.strip_html("<div id='test'>test</div>"))
224
- assert_equal('', @filters.strip_html("<script type='text/javascript'>document.write('some stuff');</script>"))
225
- assert_equal('', @filters.strip_html("<style type='text/css'>foo bar</style>"))
226
- assert_equal('test', @filters.strip_html("<div\nclass='multiline'>test</div>"))
227
- assert_equal('test', @filters.strip_html("<!-- foo bar \n test -->test"))
228
- assert_equal('', @filters.strip_html(nil))
229
-
230
- # Quirk of the existing implementation
231
- assert_equal('foo;', @filters.strip_html("<<<script </script>script>foo;</script>"))
232
- end
233
-
234
- def test_join
235
- assert_equal('1 2 3 4', @filters.join([1, 2, 3, 4]))
236
- assert_equal('1 - 2 - 3 - 4', @filters.join([1, 2, 3, 4], ' - '))
237
- assert_equal('1121314', @filters.join([1, 2, 3, 4], 1))
238
- end
239
-
240
- def test_sort
241
- assert_equal([1, 2, 3, 4], @filters.sort([4, 3, 2, 1]))
242
- assert_equal([{ "a" => 1 }, { "a" => 2 }, { "a" => 3 }, { "a" => 4 }], @filters.sort([{ "a" => 4 }, { "a" => 3 }, { "a" => 1 }, { "a" => 2 }], "a"))
243
- end
244
-
245
- def test_sort_with_nils
246
- assert_equal([1, 2, 3, 4, nil], @filters.sort([nil, 4, 3, 2, 1]))
247
- assert_equal([{ "a" => 1 }, { "a" => 2 }, { "a" => 3 }, { "a" => 4 }, {}], @filters.sort([{ "a" => 4 }, { "a" => 3 }, {}, { "a" => 1 }, { "a" => 2 }], "a"))
248
- end
249
-
250
- def test_sort_when_property_is_sometimes_missing_puts_nils_last
251
- input = [
252
- { "price" => 4, "handle" => "alpha" },
253
- { "handle" => "beta" },
254
- { "price" => 1, "handle" => "gamma" },
255
- { "handle" => "delta" },
256
- { "price" => 2, "handle" => "epsilon" },
257
- ]
258
- expectation = [
259
- { "price" => 1, "handle" => "gamma" },
260
- { "price" => 2, "handle" => "epsilon" },
261
- { "price" => 4, "handle" => "alpha" },
262
- { "handle" => "delta" },
263
- { "handle" => "beta" },
264
- ]
265
- assert_equal(expectation, @filters.sort(input, "price"))
266
- end
267
-
268
- def test_sort_natural
269
- assert_equal(["a", "B", "c", "D"], @filters.sort_natural(["c", "D", "a", "B"]))
270
- assert_equal([{ "a" => "a" }, { "a" => "B" }, { "a" => "c" }, { "a" => "D" }], @filters.sort_natural([{ "a" => "D" }, { "a" => "c" }, { "a" => "a" }, { "a" => "B" }], "a"))
271
- end
272
-
273
- def test_sort_natural_with_nils
274
- assert_equal(["a", "B", "c", "D", nil], @filters.sort_natural([nil, "c", "D", "a", "B"]))
275
- assert_equal([{ "a" => "a" }, { "a" => "B" }, { "a" => "c" }, { "a" => "D" }, {}], @filters.sort_natural([{ "a" => "D" }, { "a" => "c" }, {}, { "a" => "a" }, { "a" => "B" }], "a"))
276
- end
277
-
278
- def test_sort_natural_when_property_is_sometimes_missing_puts_nils_last
279
- input = [
280
- { "price" => "4", "handle" => "alpha" },
281
- { "handle" => "beta" },
282
- { "price" => "1", "handle" => "gamma" },
283
- { "handle" => "delta" },
284
- { "price" => 2, "handle" => "epsilon" },
285
- ]
286
- expectation = [
287
- { "price" => "1", "handle" => "gamma" },
288
- { "price" => 2, "handle" => "epsilon" },
289
- { "price" => "4", "handle" => "alpha" },
290
- { "handle" => "delta" },
291
- { "handle" => "beta" },
292
- ]
293
- assert_equal(expectation, @filters.sort_natural(input, "price"))
294
- end
295
-
296
- def test_sort_natural_case_check
297
- input = [
298
- { "key" => "X" },
299
- { "key" => "Y" },
300
- { "key" => "Z" },
301
- { "fake" => "t" },
302
- { "key" => "a" },
303
- { "key" => "b" },
304
- { "key" => "c" },
305
- ]
306
- expectation = [
307
- { "key" => "a" },
308
- { "key" => "b" },
309
- { "key" => "c" },
310
- { "key" => "X" },
311
- { "key" => "Y" },
312
- { "key" => "Z" },
313
- { "fake" => "t" },
314
- ]
315
- assert_equal(expectation, @filters.sort_natural(input, "key"))
316
- assert_equal(["a", "b", "c", "X", "Y", "Z"], @filters.sort_natural(["X", "Y", "Z", "a", "b", "c"]))
317
- end
318
-
319
- def test_sort_empty_array
320
- assert_equal([], @filters.sort([], "a"))
321
- end
322
-
323
- def test_sort_invalid_property
324
- foo = [
325
- [1],
326
- [2],
327
- [3],
328
- ]
329
-
330
- assert_raises(Liquid::ArgumentError) do
331
- @filters.sort(foo, "bar")
332
- end
333
- end
334
-
335
- def test_sort_natural_empty_array
336
- assert_equal([], @filters.sort_natural([], "a"))
337
- end
338
-
339
- def test_sort_natural_invalid_property
340
- foo = [
341
- [1],
342
- [2],
343
- [3],
344
- ]
345
-
346
- assert_raises(Liquid::ArgumentError) do
347
- @filters.sort_natural(foo, "bar")
348
- end
349
- end
350
-
351
- def test_legacy_sort_hash
352
- assert_equal([{ a: 1, b: 2 }], @filters.sort(a: 1, b: 2))
353
- end
354
-
355
- def test_numerical_vs_lexicographical_sort
356
- assert_equal([2, 10], @filters.sort([10, 2]))
357
- assert_equal([{ "a" => 2 }, { "a" => 10 }], @filters.sort([{ "a" => 10 }, { "a" => 2 }], "a"))
358
- assert_equal(["10", "2"], @filters.sort(["10", "2"]))
359
- assert_equal([{ "a" => "10" }, { "a" => "2" }], @filters.sort([{ "a" => "10" }, { "a" => "2" }], "a"))
360
- end
361
-
362
- def test_uniq
363
- assert_equal(["foo"], @filters.uniq("foo"))
364
- assert_equal([1, 3, 2, 4], @filters.uniq([1, 1, 3, 2, 3, 1, 4, 3, 2, 1]))
365
- assert_equal([{ "a" => 1 }, { "a" => 3 }, { "a" => 2 }], @filters.uniq([{ "a" => 1 }, { "a" => 3 }, { "a" => 1 }, { "a" => 2 }], "a"))
366
- testdrop = TestDrop.new
367
- assert_equal([testdrop], @filters.uniq([testdrop, TestDrop.new], 'test'))
368
- end
369
-
370
- def test_uniq_empty_array
371
- assert_equal([], @filters.uniq([], "a"))
372
- end
373
-
374
- def test_uniq_invalid_property
375
- foo = [
376
- [1],
377
- [2],
378
- [3],
379
- ]
380
-
381
- assert_raises(Liquid::ArgumentError) do
382
- @filters.uniq(foo, "bar")
383
- end
384
- end
385
-
386
- def test_compact_empty_array
387
- assert_equal([], @filters.compact([], "a"))
388
- end
389
-
390
- def test_compact_invalid_property
391
- foo = [
392
- [1],
393
- [2],
394
- [3],
395
- ]
396
-
397
- assert_raises(Liquid::ArgumentError) do
398
- @filters.compact(foo, "bar")
399
- end
400
- end
401
-
402
- def test_reverse
403
- assert_equal([4, 3, 2, 1], @filters.reverse([1, 2, 3, 4]))
404
- end
405
-
406
- def test_legacy_reverse_hash
407
- assert_equal([{ a: 1, b: 2 }], @filters.reverse(a: 1, b: 2))
408
- end
409
-
410
- def test_map
411
- assert_equal([1, 2, 3, 4], @filters.map([{ "a" => 1 }, { "a" => 2 }, { "a" => 3 }, { "a" => 4 }], 'a'))
412
- assert_template_result('abc', "{{ ary | map:'foo' | map:'bar' }}",
413
- 'ary' => [{ 'foo' => { 'bar' => 'a' } }, { 'foo' => { 'bar' => 'b' } }, { 'foo' => { 'bar' => 'c' } }])
414
- end
415
-
416
- def test_map_doesnt_call_arbitrary_stuff
417
- assert_template_result("", '{{ "foo" | map: "__id__" }}')
418
- assert_template_result("", '{{ "foo" | map: "inspect" }}')
419
- end
420
-
421
- def test_map_calls_to_liquid
422
- t = TestThing.new
423
- assert_template_result("woot: 1", '{{ foo | map: "whatever" }}', "foo" => [t])
424
- end
425
-
426
- def test_map_on_hashes
427
- assert_template_result("4217", '{{ thing | map: "foo" | map: "bar" }}',
428
- "thing" => { "foo" => [{ "bar" => 42 }, { "bar" => 17 }] })
429
- end
430
-
431
- def test_legacy_map_on_hashes_with_dynamic_key
432
- template = "{% assign key = 'foo' %}{{ thing | map: key | map: 'bar' }}"
433
- hash = { "foo" => { "bar" => 42 } }
434
- assert_template_result("42", template, "thing" => hash)
435
- end
436
-
437
- def test_sort_calls_to_liquid
438
- t = TestThing.new
439
- Liquid::Template.parse('{{ foo | sort: "whatever" }}').render("foo" => [t])
440
- assert(t.foo > 0)
441
- end
442
-
443
- def test_map_over_proc
444
- drop = TestDrop.new
445
- p = proc { drop }
446
- templ = '{{ procs | map: "test" }}'
447
- assert_template_result("testfoo", templ, "procs" => [p])
448
- end
449
-
450
- def test_map_over_drops_returning_procs
451
- drops = [
452
- {
453
- "proc" => -> { "foo" },
454
- },
455
- {
456
- "proc" => -> { "bar" },
457
- },
458
- ]
459
- templ = '{{ drops | map: "proc" }}'
460
- assert_template_result("foobar", templ, "drops" => drops)
461
- end
462
-
463
- def test_map_works_on_enumerables
464
- assert_template_result("123", '{{ foo | map: "foo" }}', "foo" => TestEnumerable.new)
465
- end
466
-
467
- def test_map_returns_empty_on_2d_input_array
468
- foo = [
469
- [1],
470
- [2],
471
- [3],
472
- ]
473
-
474
- assert_raises(Liquid::ArgumentError) do
475
- @filters.map(foo, "bar")
476
- end
477
- end
478
-
479
- def test_map_returns_empty_with_no_property
480
- foo = [
481
- [1],
482
- [2],
483
- [3],
484
- ]
485
- assert_raises(Liquid::ArgumentError) do
486
- @filters.map(foo, nil)
487
- end
488
- end
489
-
490
- def test_sort_works_on_enumerables
491
- assert_template_result("213", '{{ foo | sort: "bar" | map: "foo" }}', "foo" => TestEnumerable.new)
492
- end
493
-
494
- def test_first_and_last_call_to_liquid
495
- assert_template_result('foobar', '{{ foo | first }}', 'foo' => [ThingWithToLiquid.new])
496
- assert_template_result('foobar', '{{ foo | last }}', 'foo' => [ThingWithToLiquid.new])
497
- end
498
-
499
- def test_truncate_calls_to_liquid
500
- assert_template_result("wo...", '{{ foo | truncate: 5 }}', "foo" => TestThing.new)
501
- end
502
-
503
- def test_date
504
- assert_equal('May', @filters.date(Time.parse("2006-05-05 10:00:00"), "%B"))
505
- assert_equal('June', @filters.date(Time.parse("2006-06-05 10:00:00"), "%B"))
506
- assert_equal('July', @filters.date(Time.parse("2006-07-05 10:00:00"), "%B"))
507
-
508
- assert_equal('May', @filters.date("2006-05-05 10:00:00", "%B"))
509
- assert_equal('June', @filters.date("2006-06-05 10:00:00", "%B"))
510
- assert_equal('July', @filters.date("2006-07-05 10:00:00", "%B"))
511
-
512
- assert_equal('2006-07-05 10:00:00', @filters.date("2006-07-05 10:00:00", ""))
513
- assert_equal('2006-07-05 10:00:00', @filters.date("2006-07-05 10:00:00", ""))
514
- assert_equal('2006-07-05 10:00:00', @filters.date("2006-07-05 10:00:00", ""))
515
- assert_equal('2006-07-05 10:00:00', @filters.date("2006-07-05 10:00:00", nil))
516
-
517
- assert_equal('07/05/2006', @filters.date("2006-07-05 10:00:00", "%m/%d/%Y"))
518
-
519
- assert_equal("07/16/2004", @filters.date("Fri Jul 16 01:00:00 2004", "%m/%d/%Y"))
520
- assert_equal(Date.today.year.to_s, @filters.date('now', '%Y'))
521
- assert_equal(Date.today.year.to_s, @filters.date('today', '%Y'))
522
- assert_equal(Date.today.year.to_s, @filters.date('Today', '%Y'))
523
-
524
- assert_nil(@filters.date(nil, "%B"))
525
-
526
- assert_equal('', @filters.date('', "%B"))
527
-
528
- with_timezone("UTC") do
529
- assert_equal("07/05/2006", @filters.date(1152098955, "%m/%d/%Y"))
530
- assert_equal("07/05/2006", @filters.date("1152098955", "%m/%d/%Y"))
531
- end
532
- end
533
-
534
- def test_first_last
535
- assert_equal(1, @filters.first([1, 2, 3]))
536
- assert_equal(3, @filters.last([1, 2, 3]))
537
- assert_nil(@filters.first([]))
538
- assert_nil(@filters.last([]))
539
- end
540
-
541
- def test_replace
542
- assert_equal('2 2 2 2', @filters.replace('1 1 1 1', '1', 2))
543
- assert_equal('2 2 2 2', @filters.replace('1 1 1 1', 1, 2))
544
- assert_equal('2 1 1 1', @filters.replace_first('1 1 1 1', '1', 2))
545
- assert_equal('2 1 1 1', @filters.replace_first('1 1 1 1', 1, 2))
546
- assert_template_result('2 1 1 1', "{{ '1 1 1 1' | replace_first: '1', 2 }}")
547
- end
548
-
549
- def test_remove
550
- assert_equal(' ', @filters.remove("a a a a", 'a'))
551
- assert_equal(' ', @filters.remove("1 1 1 1", 1))
552
- assert_equal('a a a', @filters.remove_first("a a a a", 'a '))
553
- assert_equal(' 1 1 1', @filters.remove_first("1 1 1 1", 1))
554
- assert_template_result('a a a', "{{ 'a a a a' | remove_first: 'a ' }}")
555
- end
556
-
557
- def test_pipes_in_string_arguments
558
- assert_template_result('foobar', "{{ 'foo|bar' | remove: '|' }}")
559
- end
560
-
561
- def test_strip
562
- assert_template_result('ab c', "{{ source | strip }}", 'source' => " ab c ")
563
- assert_template_result('ab c', "{{ source | strip }}", 'source' => " \tab c \n \t")
564
- end
565
-
566
- def test_lstrip
567
- assert_template_result('ab c ', "{{ source | lstrip }}", 'source' => " ab c ")
568
- assert_template_result("ab c \n \t", "{{ source | lstrip }}", 'source' => " \tab c \n \t")
569
- end
570
-
571
- def test_rstrip
572
- assert_template_result(" ab c", "{{ source | rstrip }}", 'source' => " ab c ")
573
- assert_template_result(" \tab c", "{{ source | rstrip }}", 'source' => " \tab c \n \t")
574
- end
575
-
576
- def test_strip_newlines
577
- assert_template_result('abc', "{{ source | strip_newlines }}", 'source' => "a\nb\nc")
578
- assert_template_result('abc', "{{ source | strip_newlines }}", 'source' => "a\r\nb\nc")
579
- end
580
-
581
- def test_newlines_to_br
582
- assert_template_result("a<br />\nb<br />\nc", "{{ source | newline_to_br }}", 'source' => "a\nb\nc")
583
- assert_template_result("a<br />\nb<br />\nc", "{{ source | newline_to_br }}", 'source' => "a\r\nb\nc")
584
- end
585
-
586
- def test_plus
587
- assert_template_result("2", "{{ 1 | plus:1 }}")
588
- assert_template_result("2.0", "{{ '1' | plus:'1.0' }}")
589
-
590
- assert_template_result("5", "{{ price | plus:'2' }}", 'price' => NumberLikeThing.new(3))
591
- end
592
-
593
- def test_minus
594
- assert_template_result("4", "{{ input | minus:operand }}", 'input' => 5, 'operand' => 1)
595
- assert_template_result("2.3", "{{ '4.3' | minus:'2' }}")
596
-
597
- assert_template_result("5", "{{ price | minus:'2' }}", 'price' => NumberLikeThing.new(7))
598
- end
599
-
600
- def test_abs
601
- assert_template_result("17", "{{ 17 | abs }}")
602
- assert_template_result("17", "{{ -17 | abs }}")
603
- assert_template_result("17", "{{ '17' | abs }}")
604
- assert_template_result("17", "{{ '-17' | abs }}")
605
- assert_template_result("0", "{{ 0 | abs }}")
606
- assert_template_result("0", "{{ '0' | abs }}")
607
- assert_template_result("17.42", "{{ 17.42 | abs }}")
608
- assert_template_result("17.42", "{{ -17.42 | abs }}")
609
- assert_template_result("17.42", "{{ '17.42' | abs }}")
610
- assert_template_result("17.42", "{{ '-17.42' | abs }}")
611
- end
612
-
613
- def test_times
614
- assert_template_result("12", "{{ 3 | times:4 }}")
615
- assert_template_result("0", "{{ 'foo' | times:4 }}")
616
- assert_template_result("6", "{{ '2.1' | times:3 | replace: '.','-' | plus:0}}")
617
- assert_template_result("7.25", "{{ 0.0725 | times:100 }}")
618
- assert_template_result("-7.25", '{{ "-0.0725" | times:100 }}')
619
- assert_template_result("7.25", '{{ "-0.0725" | times: -100 }}')
620
- assert_template_result("4", "{{ price | times:2 }}", 'price' => NumberLikeThing.new(2))
621
- end
622
-
623
- def test_divided_by
624
- assert_template_result("4", "{{ 12 | divided_by:3 }}")
625
- assert_template_result("4", "{{ 14 | divided_by:3 }}")
626
-
627
- assert_template_result("5", "{{ 15 | divided_by:3 }}")
628
- assert_equal("Liquid error: divided by 0", Template.parse("{{ 5 | divided_by:0 }}").render)
629
-
630
- assert_template_result("0.5", "{{ 2.0 | divided_by:4 }}")
631
- assert_raises(Liquid::ZeroDivisionError) do
632
- assert_template_result("4", "{{ 1 | modulo: 0 }}")
633
- end
634
-
635
- assert_template_result("5", "{{ price | divided_by:2 }}", 'price' => NumberLikeThing.new(10))
636
- end
637
-
638
- def test_modulo
639
- assert_template_result("1", "{{ 3 | modulo:2 }}")
640
- assert_raises(Liquid::ZeroDivisionError) do
641
- assert_template_result("4", "{{ 1 | modulo: 0 }}")
642
- end
643
-
644
- assert_template_result("1", "{{ price | modulo:2 }}", 'price' => NumberLikeThing.new(3))
645
- end
646
-
647
- def test_round
648
- assert_template_result("5", "{{ input | round }}", 'input' => 4.6)
649
- assert_template_result("4", "{{ '4.3' | round }}")
650
- assert_template_result("4.56", "{{ input | round: 2 }}", 'input' => 4.5612)
651
- assert_raises(Liquid::FloatDomainError) do
652
- assert_template_result("4", "{{ 1.0 | divided_by: 0.0 | round }}")
653
- end
654
-
655
- assert_template_result("5", "{{ price | round }}", 'price' => NumberLikeThing.new(4.6))
656
- assert_template_result("4", "{{ price | round }}", 'price' => NumberLikeThing.new(4.3))
657
- end
658
-
659
- def test_ceil
660
- assert_template_result("5", "{{ input | ceil }}", 'input' => 4.6)
661
- assert_template_result("5", "{{ '4.3' | ceil }}")
662
- assert_raises(Liquid::FloatDomainError) do
663
- assert_template_result("4", "{{ 1.0 | divided_by: 0.0 | ceil }}")
664
- end
665
-
666
- assert_template_result("5", "{{ price | ceil }}", 'price' => NumberLikeThing.new(4.6))
667
- end
668
-
669
- def test_floor
670
- assert_template_result("4", "{{ input | floor }}", 'input' => 4.6)
671
- assert_template_result("4", "{{ '4.3' | floor }}")
672
- assert_raises(Liquid::FloatDomainError) do
673
- assert_template_result("4", "{{ 1.0 | divided_by: 0.0 | floor }}")
674
- end
675
-
676
- assert_template_result("5", "{{ price | floor }}", 'price' => NumberLikeThing.new(5.4))
677
- end
678
-
679
- def test_at_most
680
- assert_template_result("4", "{{ 5 | at_most:4 }}")
681
- assert_template_result("5", "{{ 5 | at_most:5 }}")
682
- assert_template_result("5", "{{ 5 | at_most:6 }}")
683
-
684
- assert_template_result("4.5", "{{ 4.5 | at_most:5 }}")
685
- assert_template_result("5", "{{ width | at_most:5 }}", 'width' => NumberLikeThing.new(6))
686
- assert_template_result("4", "{{ width | at_most:5 }}", 'width' => NumberLikeThing.new(4))
687
- assert_template_result("4", "{{ 5 | at_most: width }}", 'width' => NumberLikeThing.new(4))
688
- end
689
-
690
- def test_at_least
691
- assert_template_result("5", "{{ 5 | at_least:4 }}")
692
- assert_template_result("5", "{{ 5 | at_least:5 }}")
693
- assert_template_result("6", "{{ 5 | at_least:6 }}")
694
-
695
- assert_template_result("5", "{{ 4.5 | at_least:5 }}")
696
- assert_template_result("6", "{{ width | at_least:5 }}", 'width' => NumberLikeThing.new(6))
697
- assert_template_result("5", "{{ width | at_least:5 }}", 'width' => NumberLikeThing.new(4))
698
- assert_template_result("6", "{{ 5 | at_least: width }}", 'width' => NumberLikeThing.new(6))
699
- end
700
-
701
- def test_append
702
- assigns = { 'a' => 'bc', 'b' => 'd' }
703
- assert_template_result('bcd', "{{ a | append: 'd'}}", assigns)
704
- assert_template_result('bcd', "{{ a | append: b}}", assigns)
705
- end
706
-
707
- def test_concat
708
- assert_equal([1, 2, 3, 4], @filters.concat([1, 2], [3, 4]))
709
- assert_equal([1, 2, 'a'], @filters.concat([1, 2], ['a']))
710
- assert_equal([1, 2, 10], @filters.concat([1, 2], [10]))
711
-
712
- assert_raises(Liquid::ArgumentError, "concat filter requires an array argument") do
713
- @filters.concat([1, 2], 10)
714
- end
715
- end
716
-
717
- def test_prepend
718
- assigns = { 'a' => 'bc', 'b' => 'a' }
719
- assert_template_result('abc', "{{ a | prepend: 'a'}}", assigns)
720
- assert_template_result('abc', "{{ a | prepend: b}}", assigns)
721
- end
722
-
723
- def test_default
724
- assert_equal("foo", @filters.default("foo", "bar"))
725
- assert_equal("bar", @filters.default(nil, "bar"))
726
- assert_equal("bar", @filters.default("", "bar"))
727
- assert_equal("bar", @filters.default(false, "bar"))
728
- assert_equal("bar", @filters.default([], "bar"))
729
- assert_equal("bar", @filters.default({}, "bar"))
730
- assert_template_result('bar', "{{ false | default: 'bar' }}")
731
- assert_template_result('bar', "{{ drop | default: 'bar' }}", 'drop' => BooleanDrop.new(false))
732
- assert_template_result('Yay', "{{ drop | default: 'bar' }}", 'drop' => BooleanDrop.new(true))
733
- end
734
-
735
- def test_default_handle_false
736
- assert_equal("foo", @filters.default("foo", "bar", "allow_false" => true))
737
- assert_equal("bar", @filters.default(nil, "bar", "allow_false" => true))
738
- assert_equal("bar", @filters.default("", "bar", "allow_false" => true))
739
- assert_equal(false, @filters.default(false, "bar", "allow_false" => true))
740
- assert_equal("bar", @filters.default([], "bar", "allow_false" => true))
741
- assert_equal("bar", @filters.default({}, "bar", "allow_false" => true))
742
- assert_template_result('false', "{{ false | default: 'bar', allow_false: true }}")
743
- assert_template_result('Nay', "{{ drop | default: 'bar', allow_false: true }}", 'drop' => BooleanDrop.new(false))
744
- assert_template_result('Yay', "{{ drop | default: 'bar', allow_false: true }}", 'drop' => BooleanDrop.new(true))
745
- end
746
-
747
- def test_cannot_access_private_methods
748
- assert_template_result('a', "{{ 'a' | to_number }}")
749
- end
750
-
751
- def test_date_raises_nothing
752
- assert_template_result('', "{{ '' | date: '%D' }}")
753
- assert_template_result('abc', "{{ 'abc' | date: '%D' }}")
754
- end
755
-
756
- def test_where
757
- input = [
758
- { "handle" => "alpha", "ok" => true },
759
- { "handle" => "beta", "ok" => false },
760
- { "handle" => "gamma", "ok" => false },
761
- { "handle" => "delta", "ok" => true },
762
- ]
763
-
764
- expectation = [
765
- { "handle" => "alpha", "ok" => true },
766
- { "handle" => "delta", "ok" => true },
767
- ]
768
-
769
- assert_equal(expectation, @filters.where(input, "ok", true))
770
- assert_equal(expectation, @filters.where(input, "ok"))
771
- end
772
-
773
- def test_where_no_key_set
774
- input = [
775
- { "handle" => "alpha", "ok" => true },
776
- { "handle" => "beta" },
777
- { "handle" => "gamma" },
778
- { "handle" => "delta", "ok" => true },
779
- ]
780
-
781
- expectation = [
782
- { "handle" => "alpha", "ok" => true },
783
- { "handle" => "delta", "ok" => true },
784
- ]
785
-
786
- assert_equal(expectation, @filters.where(input, "ok", true))
787
- assert_equal(expectation, @filters.where(input, "ok"))
788
- end
789
-
790
- def test_where_non_array_map_input
791
- assert_equal([{ "a" => "ok" }], @filters.where({ "a" => "ok" }, "a", "ok"))
792
- assert_equal([], @filters.where({ "a" => "not ok" }, "a", "ok"))
793
- end
794
-
795
- def test_where_indexable_but_non_map_value
796
- assert_raises(Liquid::ArgumentError) { @filters.where(1, "ok", true) }
797
- assert_raises(Liquid::ArgumentError) { @filters.where(1, "ok") }
798
- end
799
-
800
- def test_where_non_boolean_value
801
- input = [
802
- { "message" => "Bonjour!", "language" => "French" },
803
- { "message" => "Hello!", "language" => "English" },
804
- { "message" => "Hallo!", "language" => "German" },
805
- ]
806
-
807
- assert_equal([{ "message" => "Bonjour!", "language" => "French" }], @filters.where(input, "language", "French"))
808
- assert_equal([{ "message" => "Hallo!", "language" => "German" }], @filters.where(input, "language", "German"))
809
- assert_equal([{ "message" => "Hello!", "language" => "English" }], @filters.where(input, "language", "English"))
810
- end
811
-
812
- def test_where_array_of_only_unindexable_values
813
- assert_nil(@filters.where([nil], "ok", true))
814
- assert_nil(@filters.where([nil], "ok"))
815
- end
816
-
817
- def test_all_filters_never_raise_non_liquid_exception
818
- test_drop = TestDrop.new
819
- test_drop.context = Context.new
820
- test_enum = TestEnumerable.new
821
- test_enum.context = Context.new
822
- test_types = [
823
- "foo",
824
- 123,
825
- 0,
826
- 0.0,
827
- -1234.003030303,
828
- -99999999,
829
- 1234.38383000383830003838300,
830
- nil,
831
- true,
832
- false,
833
- TestThing.new,
834
- test_drop,
835
- test_enum,
836
- ["foo", "bar"],
837
- { "foo" => "bar" },
838
- { foo: "bar" },
839
- [{ "foo" => "bar" }, { "foo" => 123 }, { "foo" => nil }, { "foo" => true }, { "foo" => ["foo", "bar"] }],
840
- { 1 => "bar" },
841
- ["foo", 123, nil, true, false, Drop, ["foo"], { foo: "bar" }],
842
- ]
843
- test_types.each do |first|
844
- test_types.each do |other|
845
- (@filters.methods - Object.methods).each do |method|
846
- arg_count = @filters.method(method).arity
847
- arg_count *= -1 if arg_count < 0
848
- inputs = [first]
849
- inputs << ([other] * (arg_count - 1)) if arg_count > 1
850
- begin
851
- @filters.send(method, *inputs)
852
- rescue Liquid::ArgumentError, Liquid::ZeroDivisionError
853
- nil
854
- end
855
- end
856
- end
857
- end
858
- end
859
-
860
- def test_where_no_target_value
861
- input = [
862
- { "foo" => false },
863
- { "foo" => true },
864
- { "foo" => "for sure" },
865
- { "bar" => true },
866
- ]
867
-
868
- assert_equal([{ "foo" => true }, { "foo" => "for sure" }], @filters.where(input, "foo"))
869
- end
870
-
871
- private
872
-
873
- def with_timezone(tz)
874
- old_tz = ENV['TZ']
875
- ENV['TZ'] = tz
876
- yield
877
- ensure
878
- ENV['TZ'] = old_tz
879
- end
880
- end # StandardFiltersTest