liquid 4.0.4 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (123) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +32 -4
  3. data/README.md +6 -0
  4. data/lib/liquid/block.rb +31 -14
  5. data/lib/liquid/block_body.rb +164 -54
  6. data/lib/liquid/condition.rb +39 -18
  7. data/lib/liquid/context.rb +106 -51
  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 +29 -34
  12. data/lib/liquid/extensions.rb +2 -0
  13. data/lib/liquid/file_system.rb +6 -4
  14. data/lib/liquid/forloop_drop.rb +11 -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 +3 -1
  19. data/lib/liquid/parse_context.rb +16 -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 +5 -3
  27. data/lib/liquid/register.rb +6 -0
  28. data/lib/liquid/resource_limits.rb +47 -8
  29. data/lib/liquid/standardfilters.rb +62 -43
  30. data/lib/liquid/static_registers.rb +44 -0
  31. data/lib/liquid/strainer_factory.rb +36 -0
  32. data/lib/liquid/strainer_template.rb +53 -0
  33. data/lib/liquid/tablerowloop_drop.rb +6 -4
  34. data/lib/liquid/tag/disableable.rb +22 -0
  35. data/lib/liquid/tag/disabler.rb +21 -0
  36. data/lib/liquid/tag.rb +28 -6
  37. data/lib/liquid/tags/assign.rb +24 -10
  38. data/lib/liquid/tags/break.rb +8 -3
  39. data/lib/liquid/tags/capture.rb +11 -8
  40. data/lib/liquid/tags/case.rb +33 -27
  41. data/lib/liquid/tags/comment.rb +5 -3
  42. data/lib/liquid/tags/continue.rb +8 -3
  43. data/lib/liquid/tags/cycle.rb +25 -14
  44. data/lib/liquid/tags/decrement.rb +6 -3
  45. data/lib/liquid/tags/echo.rb +26 -0
  46. data/lib/liquid/tags/for.rb +68 -44
  47. data/lib/liquid/tags/if.rb +35 -23
  48. data/lib/liquid/tags/ifchanged.rb +11 -10
  49. data/lib/liquid/tags/include.rb +34 -47
  50. data/lib/liquid/tags/increment.rb +7 -3
  51. data/lib/liquid/tags/raw.rb +14 -11
  52. data/lib/liquid/tags/render.rb +84 -0
  53. data/lib/liquid/tags/table_row.rb +23 -19
  54. data/lib/liquid/tags/unless.rb +15 -15
  55. data/lib/liquid/template.rb +55 -69
  56. data/lib/liquid/template_factory.rb +9 -0
  57. data/lib/liquid/tokenizer.rb +17 -9
  58. data/lib/liquid/usage.rb +8 -0
  59. data/lib/liquid/utils.rb +5 -3
  60. data/lib/liquid/variable.rb +47 -19
  61. data/lib/liquid/variable_lookup.rb +8 -6
  62. data/lib/liquid/version.rb +2 -1
  63. data/lib/liquid.rb +17 -5
  64. data/test/integration/assign_test.rb +74 -5
  65. data/test/integration/blank_test.rb +11 -8
  66. data/test/integration/block_test.rb +47 -1
  67. data/test/integration/capture_test.rb +18 -10
  68. data/test/integration/context_test.rb +608 -5
  69. data/test/integration/document_test.rb +4 -2
  70. data/test/integration/drop_test.rb +67 -57
  71. data/test/integration/error_handling_test.rb +73 -61
  72. data/test/integration/expression_test.rb +46 -0
  73. data/test/integration/filter_test.rb +53 -42
  74. data/test/integration/hash_ordering_test.rb +5 -3
  75. data/test/integration/output_test.rb +26 -24
  76. data/test/integration/parsing_quirks_test.rb +19 -7
  77. data/test/integration/{render_profiling_test.rb → profiler_test.rb} +84 -25
  78. data/test/integration/security_test.rb +30 -21
  79. data/test/integration/standard_filter_test.rb +339 -281
  80. data/test/integration/tag/disableable_test.rb +59 -0
  81. data/test/integration/tag_test.rb +45 -0
  82. data/test/integration/tags/break_tag_test.rb +4 -2
  83. data/test/integration/tags/continue_tag_test.rb +4 -2
  84. data/test/integration/tags/echo_test.rb +13 -0
  85. data/test/integration/tags/for_tag_test.rb +107 -51
  86. data/test/integration/tags/if_else_tag_test.rb +5 -3
  87. data/test/integration/tags/include_tag_test.rb +70 -54
  88. data/test/integration/tags/increment_tag_test.rb +4 -2
  89. data/test/integration/tags/liquid_tag_test.rb +116 -0
  90. data/test/integration/tags/raw_tag_test.rb +14 -11
  91. data/test/integration/tags/render_tag_test.rb +213 -0
  92. data/test/integration/tags/standard_tag_test.rb +38 -31
  93. data/test/integration/tags/statements_test.rb +23 -21
  94. data/test/integration/tags/table_row_test.rb +2 -0
  95. data/test/integration/tags/unless_else_tag_test.rb +4 -2
  96. data/test/integration/template_test.rb +118 -124
  97. data/test/integration/trim_mode_test.rb +78 -44
  98. data/test/integration/variable_test.rb +43 -32
  99. data/test/test_helper.rb +75 -14
  100. data/test/unit/block_unit_test.rb +19 -24
  101. data/test/unit/condition_unit_test.rb +79 -77
  102. data/test/unit/file_system_unit_test.rb +6 -4
  103. data/test/unit/i18n_unit_test.rb +7 -5
  104. data/test/unit/lexer_unit_test.rb +11 -9
  105. data/test/{integration → unit}/parse_tree_visitor_test.rb +1 -1
  106. data/test/unit/parser_unit_test.rb +37 -35
  107. data/test/unit/partial_cache_unit_test.rb +128 -0
  108. data/test/unit/regexp_unit_test.rb +17 -15
  109. data/test/unit/static_registers_unit_test.rb +156 -0
  110. data/test/unit/strainer_factory_unit_test.rb +100 -0
  111. data/test/unit/strainer_template_unit_test.rb +82 -0
  112. data/test/unit/tag_unit_test.rb +5 -3
  113. data/test/unit/tags/case_tag_unit_test.rb +3 -1
  114. data/test/unit/tags/for_tag_unit_test.rb +4 -2
  115. data/test/unit/tags/if_tag_unit_test.rb +3 -1
  116. data/test/unit/template_factory_unit_test.rb +12 -0
  117. data/test/unit/template_unit_test.rb +19 -10
  118. data/test/unit/tokenizer_unit_test.rb +19 -17
  119. data/test/unit/variable_unit_test.rb +51 -49
  120. metadata +75 -47
  121. data/lib/liquid/strainer.rb +0 -66
  122. data/test/unit/context_unit_test.rb +0 -490
  123. data/test/unit/strainer_unit_test.rb +0 -164
@@ -1,490 +0,0 @@
1
- require 'test_helper'
2
-
3
- class HundredCentes
4
- def to_liquid
5
- 100
6
- end
7
- end
8
-
9
- class CentsDrop < Liquid::Drop
10
- def amount
11
- HundredCentes.new
12
- end
13
-
14
- def non_zero?
15
- true
16
- end
17
- end
18
-
19
- class ContextSensitiveDrop < Liquid::Drop
20
- def test
21
- @context['test']
22
- end
23
- end
24
-
25
- class Category < Liquid::Drop
26
- attr_accessor :name
27
-
28
- def initialize(name)
29
- @name = name
30
- end
31
-
32
- def to_liquid
33
- CategoryDrop.new(self)
34
- end
35
- end
36
-
37
- class CategoryDrop
38
- attr_accessor :category, :context
39
- def initialize(category)
40
- @category = category
41
- end
42
- end
43
-
44
- class CounterDrop < Liquid::Drop
45
- def count
46
- @count ||= 0
47
- @count += 1
48
- end
49
- end
50
-
51
- class ArrayLike
52
- def fetch(index)
53
- end
54
-
55
- def [](index)
56
- @counts ||= []
57
- @counts[index] ||= 0
58
- @counts[index] += 1
59
- end
60
-
61
- def to_liquid
62
- self
63
- end
64
- end
65
-
66
- class ContextUnitTest < Minitest::Test
67
- include Liquid
68
-
69
- def setup
70
- @context = Liquid::Context.new
71
- end
72
-
73
- def test_variables
74
- @context['string'] = 'string'
75
- assert_equal 'string', @context['string']
76
-
77
- @context['num'] = 5
78
- assert_equal 5, @context['num']
79
-
80
- @context['time'] = Time.parse('2006-06-06 12:00:00')
81
- assert_equal Time.parse('2006-06-06 12:00:00'), @context['time']
82
-
83
- @context['date'] = Date.today
84
- assert_equal Date.today, @context['date']
85
-
86
- now = DateTime.now
87
- @context['datetime'] = now
88
- assert_equal now, @context['datetime']
89
-
90
- @context['bool'] = true
91
- assert_equal true, @context['bool']
92
-
93
- @context['bool'] = false
94
- assert_equal false, @context['bool']
95
-
96
- @context['nil'] = nil
97
- assert_nil @context['nil']
98
- assert_nil @context['nil']
99
- end
100
-
101
- def test_variables_not_existing
102
- assert_nil @context['does_not_exist']
103
- end
104
-
105
- def test_scoping
106
- @context.push
107
- @context.pop
108
-
109
- assert_raises(Liquid::ContextError) do
110
- @context.pop
111
- end
112
-
113
- assert_raises(Liquid::ContextError) do
114
- @context.push
115
- @context.pop
116
- @context.pop
117
- end
118
- end
119
-
120
- def test_length_query
121
- @context['numbers'] = [1, 2, 3, 4]
122
-
123
- assert_equal 4, @context['numbers.size']
124
-
125
- @context['numbers'] = { 1 => 1, 2 => 2, 3 => 3, 4 => 4 }
126
-
127
- assert_equal 4, @context['numbers.size']
128
-
129
- @context['numbers'] = { 1 => 1, 2 => 2, 3 => 3, 4 => 4, 'size' => 1000 }
130
-
131
- assert_equal 1000, @context['numbers.size']
132
- end
133
-
134
- def test_hyphenated_variable
135
- @context['oh-my'] = 'godz'
136
- assert_equal 'godz', @context['oh-my']
137
- end
138
-
139
- def test_add_filter
140
- filter = Module.new do
141
- def hi(output)
142
- output + ' hi!'
143
- end
144
- end
145
-
146
- context = Context.new
147
- context.add_filters(filter)
148
- assert_equal 'hi? hi!', context.invoke(:hi, 'hi?')
149
-
150
- context = Context.new
151
- assert_equal 'hi?', context.invoke(:hi, 'hi?')
152
-
153
- context.add_filters(filter)
154
- assert_equal 'hi? hi!', context.invoke(:hi, 'hi?')
155
- end
156
-
157
- def test_only_intended_filters_make_it_there
158
- filter = Module.new do
159
- def hi(output)
160
- output + ' hi!'
161
- end
162
- end
163
-
164
- context = Context.new
165
- assert_equal "Wookie", context.invoke("hi", "Wookie")
166
-
167
- context.add_filters(filter)
168
- assert_equal "Wookie hi!", context.invoke("hi", "Wookie")
169
- end
170
-
171
- def test_add_item_in_outer_scope
172
- @context['test'] = 'test'
173
- @context.push
174
- assert_equal 'test', @context['test']
175
- @context.pop
176
- assert_equal 'test', @context['test']
177
- end
178
-
179
- def test_add_item_in_inner_scope
180
- @context.push
181
- @context['test'] = 'test'
182
- assert_equal 'test', @context['test']
183
- @context.pop
184
- assert_nil @context['test']
185
- end
186
-
187
- def test_hierachical_data
188
- @context['hash'] = { "name" => 'tobi' }
189
- assert_equal 'tobi', @context['hash.name']
190
- assert_equal 'tobi', @context['hash["name"]']
191
- end
192
-
193
- def test_keywords
194
- assert_equal true, @context['true']
195
- assert_equal false, @context['false']
196
- end
197
-
198
- def test_digits
199
- assert_equal 100, @context['100']
200
- assert_equal 100.00, @context['100.00']
201
- end
202
-
203
- def test_strings
204
- assert_equal "hello!", @context['"hello!"']
205
- assert_equal "hello!", @context["'hello!'"]
206
- end
207
-
208
- def test_merge
209
- @context.merge({ "test" => "test" })
210
- assert_equal 'test', @context['test']
211
- @context.merge({ "test" => "newvalue", "foo" => "bar" })
212
- assert_equal 'newvalue', @context['test']
213
- assert_equal 'bar', @context['foo']
214
- end
215
-
216
- def test_array_notation
217
- @context['test'] = [1, 2, 3, 4, 5]
218
-
219
- assert_equal 1, @context['test[0]']
220
- assert_equal 2, @context['test[1]']
221
- assert_equal 3, @context['test[2]']
222
- assert_equal 4, @context['test[3]']
223
- assert_equal 5, @context['test[4]']
224
- end
225
-
226
- def test_recoursive_array_notation
227
- @context['test'] = { 'test' => [1, 2, 3, 4, 5] }
228
-
229
- assert_equal 1, @context['test.test[0]']
230
-
231
- @context['test'] = [{ 'test' => 'worked' }]
232
-
233
- assert_equal 'worked', @context['test[0].test']
234
- end
235
-
236
- def test_hash_to_array_transition
237
- @context['colors'] = {
238
- 'Blue' => ['003366', '336699', '6699CC', '99CCFF'],
239
- 'Green' => ['003300', '336633', '669966', '99CC99'],
240
- 'Yellow' => ['CC9900', 'FFCC00', 'FFFF99', 'FFFFCC'],
241
- 'Red' => ['660000', '993333', 'CC6666', 'FF9999']
242
- }
243
-
244
- assert_equal '003366', @context['colors.Blue[0]']
245
- assert_equal 'FF9999', @context['colors.Red[3]']
246
- end
247
-
248
- def test_try_first
249
- @context['test'] = [1, 2, 3, 4, 5]
250
-
251
- assert_equal 1, @context['test.first']
252
- assert_equal 5, @context['test.last']
253
-
254
- @context['test'] = { 'test' => [1, 2, 3, 4, 5] }
255
-
256
- assert_equal 1, @context['test.test.first']
257
- assert_equal 5, @context['test.test.last']
258
-
259
- @context['test'] = [1]
260
- assert_equal 1, @context['test.first']
261
- assert_equal 1, @context['test.last']
262
- end
263
-
264
- def test_access_hashes_with_hash_notation
265
- @context['products'] = { 'count' => 5, 'tags' => ['deepsnow', 'freestyle'] }
266
- @context['product'] = { 'variants' => [ { 'title' => 'draft151cm' }, { 'title' => 'element151cm' } ] }
267
-
268
- assert_equal 5, @context['products["count"]']
269
- assert_equal 'deepsnow', @context['products["tags"][0]']
270
- assert_equal 'deepsnow', @context['products["tags"].first']
271
- assert_equal 'draft151cm', @context['product["variants"][0]["title"]']
272
- assert_equal 'element151cm', @context['product["variants"][1]["title"]']
273
- assert_equal 'draft151cm', @context['product["variants"][0]["title"]']
274
- assert_equal 'element151cm', @context['product["variants"].last["title"]']
275
- end
276
-
277
- def test_access_variable_with_hash_notation
278
- @context['foo'] = 'baz'
279
- @context['bar'] = 'foo'
280
-
281
- assert_equal 'baz', @context['["foo"]']
282
- assert_equal 'baz', @context['[bar]']
283
- end
284
-
285
- def test_access_hashes_with_hash_access_variables
286
- @context['var'] = 'tags'
287
- @context['nested'] = { 'var' => 'tags' }
288
- @context['products'] = { 'count' => 5, 'tags' => ['deepsnow', 'freestyle'] }
289
-
290
- assert_equal 'deepsnow', @context['products[var].first']
291
- assert_equal 'freestyle', @context['products[nested.var].last']
292
- end
293
-
294
- def test_hash_notation_only_for_hash_access
295
- @context['array'] = [1, 2, 3, 4, 5]
296
- @context['hash'] = { 'first' => 'Hello' }
297
-
298
- assert_equal 1, @context['array.first']
299
- assert_nil @context['array["first"]']
300
- assert_equal 'Hello', @context['hash["first"]']
301
- end
302
-
303
- def test_first_can_appear_in_middle_of_callchain
304
- @context['product'] = { 'variants' => [ { 'title' => 'draft151cm' }, { 'title' => 'element151cm' } ] }
305
-
306
- assert_equal 'draft151cm', @context['product.variants[0].title']
307
- assert_equal 'element151cm', @context['product.variants[1].title']
308
- assert_equal 'draft151cm', @context['product.variants.first.title']
309
- assert_equal 'element151cm', @context['product.variants.last.title']
310
- end
311
-
312
- def test_cents
313
- @context.merge("cents" => HundredCentes.new)
314
- assert_equal 100, @context['cents']
315
- end
316
-
317
- def test_nested_cents
318
- @context.merge("cents" => { 'amount' => HundredCentes.new })
319
- assert_equal 100, @context['cents.amount']
320
-
321
- @context.merge("cents" => { 'cents' => { 'amount' => HundredCentes.new } })
322
- assert_equal 100, @context['cents.cents.amount']
323
- end
324
-
325
- def test_cents_through_drop
326
- @context.merge("cents" => CentsDrop.new)
327
- assert_equal 100, @context['cents.amount']
328
- end
329
-
330
- def test_nested_cents_through_drop
331
- @context.merge("vars" => { "cents" => CentsDrop.new })
332
- assert_equal 100, @context['vars.cents.amount']
333
- end
334
-
335
- def test_drop_methods_with_question_marks
336
- @context.merge("cents" => CentsDrop.new)
337
- assert @context['cents.non_zero?']
338
- end
339
-
340
- def test_context_from_within_drop
341
- @context.merge("test" => '123', "vars" => ContextSensitiveDrop.new)
342
- assert_equal '123', @context['vars.test']
343
- end
344
-
345
- def test_nested_context_from_within_drop
346
- @context.merge("test" => '123', "vars" => { "local" => ContextSensitiveDrop.new })
347
- assert_equal '123', @context['vars.local.test']
348
- end
349
-
350
- def test_ranges
351
- @context.merge("test" => '5')
352
- assert_equal (1..5), @context['(1..5)']
353
- assert_equal (1..5), @context['(1..test)']
354
- assert_equal (5..5), @context['(test..test)']
355
- end
356
-
357
- def test_cents_through_drop_nestedly
358
- @context.merge("cents" => { "cents" => CentsDrop.new })
359
- assert_equal 100, @context['cents.cents.amount']
360
-
361
- @context.merge("cents" => { "cents" => { "cents" => CentsDrop.new } })
362
- assert_equal 100, @context['cents.cents.cents.amount']
363
- end
364
-
365
- def test_drop_with_variable_called_only_once
366
- @context['counter'] = CounterDrop.new
367
-
368
- assert_equal 1, @context['counter.count']
369
- assert_equal 2, @context['counter.count']
370
- assert_equal 3, @context['counter.count']
371
- end
372
-
373
- def test_drop_with_key_called_only_once
374
- @context['counter'] = CounterDrop.new
375
-
376
- assert_equal 1, @context['counter["count"]']
377
- assert_equal 2, @context['counter["count"]']
378
- assert_equal 3, @context['counter["count"]']
379
- end
380
-
381
- def test_proc_as_variable
382
- @context['dynamic'] = proc { 'Hello' }
383
-
384
- assert_equal 'Hello', @context['dynamic']
385
- end
386
-
387
- def test_lambda_as_variable
388
- @context['dynamic'] = proc { 'Hello' }
389
-
390
- assert_equal 'Hello', @context['dynamic']
391
- end
392
-
393
- def test_nested_lambda_as_variable
394
- @context['dynamic'] = { "lambda" => proc { 'Hello' } }
395
-
396
- assert_equal 'Hello', @context['dynamic.lambda']
397
- end
398
-
399
- def test_array_containing_lambda_as_variable
400
- @context['dynamic'] = [1, 2, proc { 'Hello' }, 4, 5]
401
-
402
- assert_equal 'Hello', @context['dynamic[2]']
403
- end
404
-
405
- def test_lambda_is_called_once
406
- @context['callcount'] = proc { @global ||= 0; @global += 1; @global.to_s }
407
-
408
- assert_equal '1', @context['callcount']
409
- assert_equal '1', @context['callcount']
410
- assert_equal '1', @context['callcount']
411
-
412
- @global = nil
413
- end
414
-
415
- def test_nested_lambda_is_called_once
416
- @context['callcount'] = { "lambda" => proc { @global ||= 0; @global += 1; @global.to_s } }
417
-
418
- assert_equal '1', @context['callcount.lambda']
419
- assert_equal '1', @context['callcount.lambda']
420
- assert_equal '1', @context['callcount.lambda']
421
-
422
- @global = nil
423
- end
424
-
425
- def test_lambda_in_array_is_called_once
426
- @context['callcount'] = [1, 2, proc { @global ||= 0; @global += 1; @global.to_s }, 4, 5]
427
-
428
- assert_equal '1', @context['callcount[2]']
429
- assert_equal '1', @context['callcount[2]']
430
- assert_equal '1', @context['callcount[2]']
431
-
432
- @global = nil
433
- end
434
-
435
- def test_access_to_context_from_proc
436
- @context.registers[:magic] = 345392
437
-
438
- @context['magic'] = proc { @context.registers[:magic] }
439
-
440
- assert_equal 345392, @context['magic']
441
- end
442
-
443
- def test_to_liquid_and_context_at_first_level
444
- @context['category'] = Category.new("foobar")
445
- assert_kind_of CategoryDrop, @context['category']
446
- assert_equal @context, @context['category'].context
447
- end
448
-
449
- def test_interrupt_avoids_object_allocations
450
- @context.interrupt? # ruby 3.0.0 allocates on the first call
451
- assert_no_object_allocations do
452
- @context.interrupt?
453
- end
454
- end
455
-
456
- def test_context_initialization_with_a_proc_in_environment
457
- contx = Context.new([test: ->(c) { c['poutine'] }], { test: :foo })
458
-
459
- assert contx
460
- assert_nil contx['poutine']
461
- end
462
-
463
- def test_apply_global_filter
464
- global_filter_proc = ->(output) { "#{output} filtered" }
465
-
466
- context = Context.new
467
- context.global_filter = global_filter_proc
468
-
469
- assert_equal 'hi filtered', context.apply_global_filter('hi')
470
- end
471
-
472
- def test_apply_global_filter_when_no_global_filter_exist
473
- context = Context.new
474
- assert_equal 'hi', context.apply_global_filter('hi')
475
- end
476
-
477
- private
478
-
479
- def assert_no_object_allocations
480
- unless RUBY_ENGINE == 'ruby'
481
- skip "stackprof needed to count object allocations"
482
- end
483
- require 'stackprof'
484
-
485
- profile = StackProf.run(mode: :object) do
486
- yield
487
- end
488
- assert_equal 0, profile[:samples]
489
- end
490
- end # ContextTest
@@ -1,164 +0,0 @@
1
- require 'test_helper'
2
-
3
- class StrainerUnitTest < Minitest::Test
4
- include Liquid
5
-
6
- module AccessScopeFilters
7
- def public_filter
8
- "public"
9
- end
10
-
11
- def private_filter
12
- "private"
13
- end
14
- private :private_filter
15
- end
16
-
17
- Strainer.global_filter(AccessScopeFilters)
18
-
19
- def test_strainer
20
- strainer = Strainer.create(nil)
21
- assert_equal 5, strainer.invoke('size', 'input')
22
- assert_equal "public", strainer.invoke("public_filter")
23
- end
24
-
25
- def test_stainer_raises_argument_error
26
- strainer = Strainer.create(nil)
27
- assert_raises(Liquid::ArgumentError) do
28
- strainer.invoke("public_filter", 1)
29
- end
30
- end
31
-
32
- def test_stainer_argument_error_contains_backtrace
33
- strainer = Strainer.create(nil)
34
- begin
35
- strainer.invoke("public_filter", 1)
36
- rescue Liquid::ArgumentError => e
37
- assert_match(
38
- /\ALiquid error: wrong number of arguments \((1 for 0|given 1, expected 0)\)\z/,
39
- e.message)
40
- assert_equal e.backtrace[0].split(':')[0], __FILE__
41
- end
42
- end
43
-
44
- def test_strainer_only_invokes_public_filter_methods
45
- strainer = Strainer.create(nil)
46
- assert_equal false, strainer.class.invokable?('__test__')
47
- assert_equal false, strainer.class.invokable?('test')
48
- assert_equal false, strainer.class.invokable?('instance_eval')
49
- assert_equal false, strainer.class.invokable?('__send__')
50
- assert_equal true, strainer.class.invokable?('size') # from the standard lib
51
- end
52
-
53
- def test_strainer_returns_nil_if_no_filter_method_found
54
- strainer = Strainer.create(nil)
55
- assert_nil strainer.invoke("private_filter")
56
- assert_nil strainer.invoke("undef_the_filter")
57
- end
58
-
59
- def test_strainer_returns_first_argument_if_no_method_and_arguments_given
60
- strainer = Strainer.create(nil)
61
- assert_equal "password", strainer.invoke("undef_the_method", "password")
62
- end
63
-
64
- def test_strainer_only_allows_methods_defined_in_filters
65
- strainer = Strainer.create(nil)
66
- assert_equal "1 + 1", strainer.invoke("instance_eval", "1 + 1")
67
- assert_equal "puts", strainer.invoke("__send__", "puts", "Hi Mom")
68
- assert_equal "has_method?", strainer.invoke("invoke", "has_method?", "invoke")
69
- end
70
-
71
- def test_strainer_uses_a_class_cache_to_avoid_method_cache_invalidation
72
- a = Module.new
73
- b = Module.new
74
- strainer = Strainer.create(nil, [a, b])
75
- assert_kind_of Strainer, strainer
76
- assert_kind_of a, strainer
77
- assert_kind_of b, strainer
78
- assert_kind_of Liquid::StandardFilters, strainer
79
- end
80
-
81
- def test_add_filter_when_wrong_filter_class
82
- c = Context.new
83
- s = c.strainer
84
- wrong_filter = ->(v) { v.reverse }
85
-
86
- assert_raises ArgumentError do
87
- s.class.add_filter(wrong_filter)
88
- end
89
- end
90
-
91
- module PrivateMethodOverrideFilter
92
- private
93
-
94
- def public_filter
95
- "overriden as private"
96
- end
97
- end
98
-
99
- def test_add_filter_raises_when_module_privately_overrides_registered_public_methods
100
- strainer = Context.new.strainer
101
-
102
- error = assert_raises(Liquid::MethodOverrideError) do
103
- strainer.class.add_filter(PrivateMethodOverrideFilter)
104
- end
105
- assert_equal 'Liquid error: Filter overrides registered public methods as non public: public_filter', error.message
106
- end
107
-
108
- module ProtectedMethodOverrideFilter
109
- protected
110
-
111
- def public_filter
112
- "overriden as protected"
113
- end
114
- end
115
-
116
- def test_add_filter_raises_when_module_overrides_registered_public_method_as_protected
117
- strainer = Context.new.strainer
118
-
119
- error = assert_raises(Liquid::MethodOverrideError) do
120
- strainer.class.add_filter(ProtectedMethodOverrideFilter)
121
- end
122
- assert_equal 'Liquid error: Filter overrides registered public methods as non public: public_filter', error.message
123
- end
124
-
125
- module PublicMethodOverrideFilter
126
- def public_filter
127
- "public"
128
- end
129
- end
130
-
131
- def test_add_filter_does_not_raise_when_module_overrides_previously_registered_method
132
- strainer = Context.new.strainer
133
- strainer.class.add_filter(PublicMethodOverrideFilter)
134
- assert strainer.class.filter_methods.include?('public_filter')
135
- end
136
-
137
- module LateAddedFilter
138
- def late_added_filter(input)
139
- "filtered"
140
- end
141
- end
142
-
143
- def test_global_filter_clears_cache
144
- assert_equal 'input', Strainer.create(nil).invoke('late_added_filter', 'input')
145
- Strainer.global_filter(LateAddedFilter)
146
- assert_equal 'filtered', Strainer.create(nil).invoke('late_added_filter', 'input')
147
- end
148
-
149
- def test_add_filter_does_not_include_already_included_module
150
- mod = Module.new do
151
- class << self
152
- attr_accessor :include_count
153
- def included(mod)
154
- self.include_count += 1
155
- end
156
- end
157
- self.include_count = 0
158
- end
159
- strainer = Context.new.strainer
160
- strainer.class.add_filter(mod)
161
- strainer.class.add_filter(mod)
162
- assert_equal 1, mod.include_count
163
- end
164
- end # StrainerTest