liquid 4.0.3 → 5.4.0

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