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