liquid-4-0-2 4.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +7 -0
  2. data/History.md +235 -0
  3. data/LICENSE +20 -0
  4. data/README.md +108 -0
  5. data/lib/liquid.rb +80 -0
  6. data/lib/liquid/block.rb +77 -0
  7. data/lib/liquid/block_body.rb +142 -0
  8. data/lib/liquid/condition.rb +151 -0
  9. data/lib/liquid/context.rb +226 -0
  10. data/lib/liquid/document.rb +27 -0
  11. data/lib/liquid/drop.rb +78 -0
  12. data/lib/liquid/errors.rb +56 -0
  13. data/lib/liquid/expression.rb +49 -0
  14. data/lib/liquid/extensions.rb +74 -0
  15. data/lib/liquid/file_system.rb +73 -0
  16. data/lib/liquid/forloop_drop.rb +42 -0
  17. data/lib/liquid/i18n.rb +39 -0
  18. data/lib/liquid/interrupts.rb +16 -0
  19. data/lib/liquid/lexer.rb +55 -0
  20. data/lib/liquid/locales/en.yml +26 -0
  21. data/lib/liquid/parse_context.rb +38 -0
  22. data/lib/liquid/parse_tree_visitor.rb +42 -0
  23. data/lib/liquid/parser.rb +90 -0
  24. data/lib/liquid/parser_switching.rb +31 -0
  25. data/lib/liquid/profiler.rb +158 -0
  26. data/lib/liquid/profiler/hooks.rb +23 -0
  27. data/lib/liquid/range_lookup.rb +37 -0
  28. data/lib/liquid/resource_limits.rb +23 -0
  29. data/lib/liquid/standardfilters.rb +485 -0
  30. data/lib/liquid/strainer.rb +66 -0
  31. data/lib/liquid/tablerowloop_drop.rb +62 -0
  32. data/lib/liquid/tag.rb +43 -0
  33. data/lib/liquid/tags/assign.rb +59 -0
  34. data/lib/liquid/tags/break.rb +18 -0
  35. data/lib/liquid/tags/capture.rb +38 -0
  36. data/lib/liquid/tags/case.rb +94 -0
  37. data/lib/liquid/tags/comment.rb +16 -0
  38. data/lib/liquid/tags/continue.rb +18 -0
  39. data/lib/liquid/tags/cycle.rb +65 -0
  40. data/lib/liquid/tags/decrement.rb +35 -0
  41. data/lib/liquid/tags/for.rb +203 -0
  42. data/lib/liquid/tags/if.rb +122 -0
  43. data/lib/liquid/tags/ifchanged.rb +18 -0
  44. data/lib/liquid/tags/include.rb +124 -0
  45. data/lib/liquid/tags/increment.rb +31 -0
  46. data/lib/liquid/tags/raw.rb +47 -0
  47. data/lib/liquid/tags/table_row.rb +62 -0
  48. data/lib/liquid/tags/unless.rb +30 -0
  49. data/lib/liquid/template.rb +254 -0
  50. data/lib/liquid/tokenizer.rb +31 -0
  51. data/lib/liquid/utils.rb +83 -0
  52. data/lib/liquid/variable.rb +148 -0
  53. data/lib/liquid/variable_lookup.rb +88 -0
  54. data/lib/liquid/version.rb +4 -0
  55. data/test/fixtures/en_locale.yml +9 -0
  56. data/test/integration/assign_test.rb +48 -0
  57. data/test/integration/blank_test.rb +106 -0
  58. data/test/integration/block_test.rb +12 -0
  59. data/test/integration/capture_test.rb +50 -0
  60. data/test/integration/context_test.rb +32 -0
  61. data/test/integration/document_test.rb +19 -0
  62. data/test/integration/drop_test.rb +273 -0
  63. data/test/integration/error_handling_test.rb +260 -0
  64. data/test/integration/filter_test.rb +178 -0
  65. data/test/integration/hash_ordering_test.rb +23 -0
  66. data/test/integration/output_test.rb +123 -0
  67. data/test/integration/parse_tree_visitor_test.rb +247 -0
  68. data/test/integration/parsing_quirks_test.rb +122 -0
  69. data/test/integration/render_profiling_test.rb +154 -0
  70. data/test/integration/security_test.rb +80 -0
  71. data/test/integration/standard_filter_test.rb +698 -0
  72. data/test/integration/tags/break_tag_test.rb +15 -0
  73. data/test/integration/tags/continue_tag_test.rb +15 -0
  74. data/test/integration/tags/for_tag_test.rb +410 -0
  75. data/test/integration/tags/if_else_tag_test.rb +188 -0
  76. data/test/integration/tags/include_tag_test.rb +245 -0
  77. data/test/integration/tags/increment_tag_test.rb +23 -0
  78. data/test/integration/tags/raw_tag_test.rb +31 -0
  79. data/test/integration/tags/standard_tag_test.rb +296 -0
  80. data/test/integration/tags/statements_test.rb +111 -0
  81. data/test/integration/tags/table_row_test.rb +64 -0
  82. data/test/integration/tags/unless_else_tag_test.rb +26 -0
  83. data/test/integration/template_test.rb +332 -0
  84. data/test/integration/trim_mode_test.rb +529 -0
  85. data/test/integration/variable_test.rb +96 -0
  86. data/test/test_helper.rb +116 -0
  87. data/test/unit/block_unit_test.rb +58 -0
  88. data/test/unit/condition_unit_test.rb +166 -0
  89. data/test/unit/context_unit_test.rb +489 -0
  90. data/test/unit/file_system_unit_test.rb +35 -0
  91. data/test/unit/i18n_unit_test.rb +37 -0
  92. data/test/unit/lexer_unit_test.rb +51 -0
  93. data/test/unit/parser_unit_test.rb +82 -0
  94. data/test/unit/regexp_unit_test.rb +44 -0
  95. data/test/unit/strainer_unit_test.rb +164 -0
  96. data/test/unit/tag_unit_test.rb +21 -0
  97. data/test/unit/tags/case_tag_unit_test.rb +10 -0
  98. data/test/unit/tags/for_tag_unit_test.rb +13 -0
  99. data/test/unit/tags/if_tag_unit_test.rb +8 -0
  100. data/test/unit/template_unit_test.rb +78 -0
  101. data/test/unit/tokenizer_unit_test.rb +55 -0
  102. data/test/unit/variable_unit_test.rb +162 -0
  103. metadata +224 -0
@@ -0,0 +1,15 @@
1
+ require 'test_helper'
2
+
3
+ class BreakTagTest < Minitest::Test
4
+ include Liquid
5
+
6
+ # tests that no weird errors are raised if break is called outside of a
7
+ # block
8
+ def test_break_with_no_block
9
+ assigns = { 'i' => 1 }
10
+ markup = '{% break %}'
11
+ expected = ''
12
+
13
+ assert_template_result(expected, markup, assigns)
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ require 'test_helper'
2
+
3
+ class ContinueTagTest < Minitest::Test
4
+ include Liquid
5
+
6
+ # tests that no weird errors are raised if continue is called outside of a
7
+ # block
8
+ def test_continue_with_no_block
9
+ assigns = {}
10
+ markup = '{% continue %}'
11
+ expected = ''
12
+
13
+ assert_template_result(expected, markup, assigns)
14
+ end
15
+ end
@@ -0,0 +1,410 @@
1
+ require 'test_helper'
2
+
3
+ class ThingWithValue < Liquid::Drop
4
+ def value
5
+ 3
6
+ end
7
+ end
8
+
9
+ class ForTagTest < Minitest::Test
10
+ include Liquid
11
+
12
+ def test_for
13
+ assert_template_result(' yo yo yo yo ', '{%for item in array%} yo {%endfor%}', 'array' => [1, 2, 3, 4])
14
+ assert_template_result('yoyo', '{%for item in array%}yo{%endfor%}', 'array' => [1, 2])
15
+ assert_template_result(' yo ', '{%for item in array%} yo {%endfor%}', 'array' => [1])
16
+ assert_template_result('', '{%for item in array%}{%endfor%}', 'array' => [1, 2])
17
+ expected = <<HERE
18
+
19
+ yo
20
+
21
+ yo
22
+
23
+ yo
24
+
25
+ HERE
26
+ template = <<HERE
27
+ {%for item in array%}
28
+ yo
29
+ {%endfor%}
30
+ HERE
31
+ assert_template_result(expected, template, 'array' => [1, 2, 3])
32
+ end
33
+
34
+ def test_for_reversed
35
+ assigns = { 'array' => [ 1, 2, 3] }
36
+ assert_template_result('321', '{%for item in array reversed %}{{item}}{%endfor%}', assigns)
37
+ end
38
+
39
+ def test_for_with_range
40
+ assert_template_result(' 1 2 3 ', '{%for item in (1..3) %} {{item}} {%endfor%}')
41
+
42
+ assert_raises(Liquid::ArgumentError) do
43
+ Template.parse('{% for i in (a..2) %}{% endfor %}').render!("a" => [1, 2])
44
+ end
45
+
46
+ assert_template_result(' 0 1 2 3 ', '{% for item in (a..3) %} {{item}} {% endfor %}', "a" => "invalid integer")
47
+ end
48
+
49
+ def test_for_with_variable_range
50
+ assert_template_result(' 1 2 3 ', '{%for item in (1..foobar) %} {{item}} {%endfor%}', "foobar" => 3)
51
+ end
52
+
53
+ def test_for_with_hash_value_range
54
+ foobar = { "value" => 3 }
55
+ assert_template_result(' 1 2 3 ', '{%for item in (1..foobar.value) %} {{item}} {%endfor%}', "foobar" => foobar)
56
+ end
57
+
58
+ def test_for_with_drop_value_range
59
+ foobar = ThingWithValue.new
60
+ assert_template_result(' 1 2 3 ', '{%for item in (1..foobar.value) %} {{item}} {%endfor%}', "foobar" => foobar)
61
+ end
62
+
63
+ def test_for_with_variable
64
+ assert_template_result(' 1 2 3 ', '{%for item in array%} {{item}} {%endfor%}', 'array' => [1, 2, 3])
65
+ assert_template_result('123', '{%for item in array%}{{item}}{%endfor%}', 'array' => [1, 2, 3])
66
+ assert_template_result('123', '{% for item in array %}{{item}}{% endfor %}', 'array' => [1, 2, 3])
67
+ assert_template_result('abcd', '{%for item in array%}{{item}}{%endfor%}', 'array' => ['a', 'b', 'c', 'd'])
68
+ assert_template_result('a b c', '{%for item in array%}{{item}}{%endfor%}', 'array' => ['a', ' ', 'b', ' ', 'c'])
69
+ assert_template_result('abc', '{%for item in array%}{{item}}{%endfor%}', 'array' => ['a', '', 'b', '', 'c'])
70
+ end
71
+
72
+ def test_for_helpers
73
+ assigns = { 'array' => [1, 2, 3] }
74
+ assert_template_result(' 1/3 2/3 3/3 ',
75
+ '{%for item in array%} {{forloop.index}}/{{forloop.length}} {%endfor%}',
76
+ assigns)
77
+ assert_template_result(' 1 2 3 ', '{%for item in array%} {{forloop.index}} {%endfor%}', assigns)
78
+ assert_template_result(' 0 1 2 ', '{%for item in array%} {{forloop.index0}} {%endfor%}', assigns)
79
+ assert_template_result(' 2 1 0 ', '{%for item in array%} {{forloop.rindex0}} {%endfor%}', assigns)
80
+ assert_template_result(' 3 2 1 ', '{%for item in array%} {{forloop.rindex}} {%endfor%}', assigns)
81
+ assert_template_result(' true false false ', '{%for item in array%} {{forloop.first}} {%endfor%}', assigns)
82
+ assert_template_result(' false false true ', '{%for item in array%} {{forloop.last}} {%endfor%}', assigns)
83
+ end
84
+
85
+ def test_for_and_if
86
+ assigns = { 'array' => [1, 2, 3] }
87
+ assert_template_result('+--',
88
+ '{%for item in array%}{% if forloop.first %}+{% else %}-{% endif %}{%endfor%}',
89
+ assigns)
90
+ end
91
+
92
+ def test_for_else
93
+ assert_template_result('+++', '{%for item in array%}+{%else%}-{%endfor%}', 'array' => [1, 2, 3])
94
+ assert_template_result('-', '{%for item in array%}+{%else%}-{%endfor%}', 'array' => [])
95
+ assert_template_result('-', '{%for item in array%}+{%else%}-{%endfor%}', 'array' => nil)
96
+ end
97
+
98
+ def test_limiting
99
+ assigns = { 'array' => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] }
100
+ assert_template_result('12', '{%for i in array limit:2 %}{{ i }}{%endfor%}', assigns)
101
+ assert_template_result('1234', '{%for i in array limit:4 %}{{ i }}{%endfor%}', assigns)
102
+ assert_template_result('3456', '{%for i in array limit:4 offset:2 %}{{ i }}{%endfor%}', assigns)
103
+ assert_template_result('3456', '{%for i in array limit: 4 offset: 2 %}{{ i }}{%endfor%}', assigns)
104
+ end
105
+
106
+ def test_dynamic_variable_limiting
107
+ assigns = { 'array' => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] }
108
+ assigns['limit'] = 2
109
+ assigns['offset'] = 2
110
+
111
+ assert_template_result('34', '{%for i in array limit: limit offset: offset %}{{ i }}{%endfor%}', assigns)
112
+ end
113
+
114
+ def test_nested_for
115
+ assigns = { 'array' => [[1, 2], [3, 4], [5, 6]] }
116
+ assert_template_result('123456', '{%for item in array%}{%for i in item%}{{ i }}{%endfor%}{%endfor%}', assigns)
117
+ end
118
+
119
+ def test_offset_only
120
+ assigns = { 'array' => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] }
121
+ assert_template_result('890', '{%for i in array offset:7 %}{{ i }}{%endfor%}', assigns)
122
+ end
123
+
124
+ def test_pause_resume
125
+ assigns = { 'array' => { 'items' => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] } }
126
+ markup = <<-MKUP
127
+ {%for i in array.items limit: 3 %}{{i}}{%endfor%}
128
+ next
129
+ {%for i in array.items offset:continue limit: 3 %}{{i}}{%endfor%}
130
+ next
131
+ {%for i in array.items offset:continue limit: 3 %}{{i}}{%endfor%}
132
+ MKUP
133
+ expected = <<-XPCTD
134
+ 123
135
+ next
136
+ 456
137
+ next
138
+ 789
139
+ XPCTD
140
+ assert_template_result(expected, markup, assigns)
141
+ end
142
+
143
+ def test_pause_resume_limit
144
+ assigns = { 'array' => { 'items' => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] } }
145
+ markup = <<-MKUP
146
+ {%for i in array.items limit:3 %}{{i}}{%endfor%}
147
+ next
148
+ {%for i in array.items offset:continue limit:3 %}{{i}}{%endfor%}
149
+ next
150
+ {%for i in array.items offset:continue limit:1 %}{{i}}{%endfor%}
151
+ MKUP
152
+ expected = <<-XPCTD
153
+ 123
154
+ next
155
+ 456
156
+ next
157
+ 7
158
+ XPCTD
159
+ assert_template_result(expected, markup, assigns)
160
+ end
161
+
162
+ def test_pause_resume_big_limit
163
+ assigns = { 'array' => { 'items' => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] } }
164
+ markup = <<-MKUP
165
+ {%for i in array.items limit:3 %}{{i}}{%endfor%}
166
+ next
167
+ {%for i in array.items offset:continue limit:3 %}{{i}}{%endfor%}
168
+ next
169
+ {%for i in array.items offset:continue limit:1000 %}{{i}}{%endfor%}
170
+ MKUP
171
+ expected = <<-XPCTD
172
+ 123
173
+ next
174
+ 456
175
+ next
176
+ 7890
177
+ XPCTD
178
+ assert_template_result(expected, markup, assigns)
179
+ end
180
+
181
+ def test_pause_resume_big_offset
182
+ assigns = { 'array' => { 'items' => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] } }
183
+ markup = '{%for i in array.items limit:3 %}{{i}}{%endfor%}
184
+ next
185
+ {%for i in array.items offset:continue limit:3 %}{{i}}{%endfor%}
186
+ next
187
+ {%for i in array.items offset:continue limit:3 offset:1000 %}{{i}}{%endfor%}'
188
+ expected = '123
189
+ next
190
+ 456
191
+ next
192
+ '
193
+ assert_template_result(expected, markup, assigns)
194
+ end
195
+
196
+ def test_for_with_break
197
+ assigns = { 'array' => { 'items' => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] } }
198
+
199
+ markup = '{% for i in array.items %}{% break %}{% endfor %}'
200
+ expected = ""
201
+ assert_template_result(expected, markup, assigns)
202
+
203
+ markup = '{% for i in array.items %}{{ i }}{% break %}{% endfor %}'
204
+ expected = "1"
205
+ assert_template_result(expected, markup, assigns)
206
+
207
+ markup = '{% for i in array.items %}{% break %}{{ i }}{% endfor %}'
208
+ expected = ""
209
+ assert_template_result(expected, markup, assigns)
210
+
211
+ markup = '{% for i in array.items %}{{ i }}{% if i > 3 %}{% break %}{% endif %}{% endfor %}'
212
+ expected = "1234"
213
+ assert_template_result(expected, markup, assigns)
214
+
215
+ # tests to ensure it only breaks out of the local for loop
216
+ # and not all of them.
217
+ assigns = { 'array' => [[1, 2], [3, 4], [5, 6]] }
218
+ markup = '{% for item in array %}' \
219
+ '{% for i in item %}' \
220
+ '{% if i == 1 %}' \
221
+ '{% break %}' \
222
+ '{% endif %}' \
223
+ '{{ i }}' \
224
+ '{% endfor %}' \
225
+ '{% endfor %}'
226
+ expected = '3456'
227
+ assert_template_result(expected, markup, assigns)
228
+
229
+ # test break does nothing when unreached
230
+ assigns = { 'array' => { 'items' => [1, 2, 3, 4, 5] } }
231
+ markup = '{% for i in array.items %}{% if i == 9999 %}{% break %}{% endif %}{{ i }}{% endfor %}'
232
+ expected = '12345'
233
+ assert_template_result(expected, markup, assigns)
234
+ end
235
+
236
+ def test_for_with_continue
237
+ assigns = { 'array' => { 'items' => [1, 2, 3, 4, 5] } }
238
+
239
+ markup = '{% for i in array.items %}{% continue %}{% endfor %}'
240
+ expected = ""
241
+ assert_template_result(expected, markup, assigns)
242
+
243
+ markup = '{% for i in array.items %}{{ i }}{% continue %}{% endfor %}'
244
+ expected = "12345"
245
+ assert_template_result(expected, markup, assigns)
246
+
247
+ markup = '{% for i in array.items %}{% continue %}{{ i }}{% endfor %}'
248
+ expected = ""
249
+ assert_template_result(expected, markup, assigns)
250
+
251
+ markup = '{% for i in array.items %}{% if i > 3 %}{% continue %}{% endif %}{{ i }}{% endfor %}'
252
+ expected = "123"
253
+ assert_template_result(expected, markup, assigns)
254
+
255
+ markup = '{% for i in array.items %}{% if i == 3 %}{% continue %}{% else %}{{ i }}{% endif %}{% endfor %}'
256
+ expected = "1245"
257
+ assert_template_result(expected, markup, assigns)
258
+
259
+ # tests to ensure it only continues the local for loop and not all of them.
260
+ assigns = { 'array' => [[1, 2], [3, 4], [5, 6]] }
261
+ markup = '{% for item in array %}' \
262
+ '{% for i in item %}' \
263
+ '{% if i == 1 %}' \
264
+ '{% continue %}' \
265
+ '{% endif %}' \
266
+ '{{ i }}' \
267
+ '{% endfor %}' \
268
+ '{% endfor %}'
269
+ expected = '23456'
270
+ assert_template_result(expected, markup, assigns)
271
+
272
+ # test continue does nothing when unreached
273
+ assigns = { 'array' => { 'items' => [1, 2, 3, 4, 5] } }
274
+ markup = '{% for i in array.items %}{% if i == 9999 %}{% continue %}{% endif %}{{ i }}{% endfor %}'
275
+ expected = '12345'
276
+ assert_template_result(expected, markup, assigns)
277
+ end
278
+
279
+ def test_for_tag_string
280
+ # ruby 1.8.7 "String".each => Enumerator with single "String" element.
281
+ # ruby 1.9.3 no longer supports .each on String though we mimic
282
+ # the functionality for backwards compatibility
283
+
284
+ assert_template_result('test string',
285
+ '{%for val in string%}{{val}}{%endfor%}',
286
+ 'string' => "test string")
287
+
288
+ assert_template_result('test string',
289
+ '{%for val in string limit:1%}{{val}}{%endfor%}',
290
+ 'string' => "test string")
291
+
292
+ assert_template_result('val-string-1-1-0-1-0-true-true-test string',
293
+ '{%for val in string%}' \
294
+ '{{forloop.name}}-' \
295
+ '{{forloop.index}}-' \
296
+ '{{forloop.length}}-' \
297
+ '{{forloop.index0}}-' \
298
+ '{{forloop.rindex}}-' \
299
+ '{{forloop.rindex0}}-' \
300
+ '{{forloop.first}}-' \
301
+ '{{forloop.last}}-' \
302
+ '{{val}}{%endfor%}',
303
+ 'string' => "test string")
304
+ end
305
+
306
+ def test_for_parentloop_references_parent_loop
307
+ assert_template_result('1.1 1.2 1.3 2.1 2.2 2.3 ',
308
+ '{% for inner in outer %}{% for k in inner %}' \
309
+ '{{ forloop.parentloop.index }}.{{ forloop.index }} ' \
310
+ '{% endfor %}{% endfor %}',
311
+ 'outer' => [[1, 1, 1], [1, 1, 1]])
312
+ end
313
+
314
+ def test_for_parentloop_nil_when_not_present
315
+ assert_template_result('.1 .2 ',
316
+ '{% for inner in outer %}' \
317
+ '{{ forloop.parentloop.index }}.{{ forloop.index }} ' \
318
+ '{% endfor %}',
319
+ 'outer' => [[1, 1, 1], [1, 1, 1]])
320
+ end
321
+
322
+ def test_inner_for_over_empty_input
323
+ assert_template_result 'oo', '{% for a in (1..2) %}o{% for b in empty %}{% endfor %}{% endfor %}'
324
+ end
325
+
326
+ def test_blank_string_not_iterable
327
+ assert_template_result('', "{% for char in characters %}I WILL NOT BE OUTPUT{% endfor %}", 'characters' => '')
328
+ end
329
+
330
+ def test_bad_variable_naming_in_for_loop
331
+ assert_raises(Liquid::SyntaxError) do
332
+ Liquid::Template.parse('{% for a/b in x %}{% endfor %}')
333
+ end
334
+ end
335
+
336
+ def test_spacing_with_variable_naming_in_for_loop
337
+ expected = '12345'
338
+ template = '{% for item in items %}{{item}}{% endfor %}'
339
+ assigns = { 'items' => [1, 2, 3, 4, 5] }
340
+ assert_template_result(expected, template, assigns)
341
+ end
342
+
343
+ class LoaderDrop < Liquid::Drop
344
+ attr_accessor :each_called, :load_slice_called
345
+
346
+ def initialize(data)
347
+ @data = data
348
+ end
349
+
350
+ def each
351
+ @each_called = true
352
+ @data.each { |el| yield el }
353
+ end
354
+
355
+ def load_slice(from, to)
356
+ @load_slice_called = true
357
+ @data[(from..to - 1)]
358
+ end
359
+ end
360
+
361
+ def test_iterate_with_each_when_no_limit_applied
362
+ loader = LoaderDrop.new([1, 2, 3, 4, 5])
363
+ assigns = { 'items' => loader }
364
+ expected = '12345'
365
+ template = '{% for item in items %}{{item}}{% endfor %}'
366
+ assert_template_result(expected, template, assigns)
367
+ assert loader.each_called
368
+ assert !loader.load_slice_called
369
+ end
370
+
371
+ def test_iterate_with_load_slice_when_limit_applied
372
+ loader = LoaderDrop.new([1, 2, 3, 4, 5])
373
+ assigns = { 'items' => loader }
374
+ expected = '1'
375
+ template = '{% for item in items limit:1 %}{{item}}{% endfor %}'
376
+ assert_template_result(expected, template, assigns)
377
+ assert !loader.each_called
378
+ assert loader.load_slice_called
379
+ end
380
+
381
+ def test_iterate_with_load_slice_when_limit_and_offset_applied
382
+ loader = LoaderDrop.new([1, 2, 3, 4, 5])
383
+ assigns = { 'items' => loader }
384
+ expected = '34'
385
+ template = '{% for item in items offset:2 limit:2 %}{{item}}{% endfor %}'
386
+ assert_template_result(expected, template, assigns)
387
+ assert !loader.each_called
388
+ assert loader.load_slice_called
389
+ end
390
+
391
+ def test_iterate_with_load_slice_returns_same_results_as_without
392
+ loader = LoaderDrop.new([1, 2, 3, 4, 5])
393
+ loader_assigns = { 'items' => loader }
394
+ array_assigns = { 'items' => [1, 2, 3, 4, 5] }
395
+ expected = '34'
396
+ template = '{% for item in items offset:2 limit:2 %}{{item}}{% endfor %}'
397
+ assert_template_result(expected, template, loader_assigns)
398
+ assert_template_result(expected, template, array_assigns)
399
+ end
400
+
401
+ def test_for_cleans_up_registers
402
+ context = Context.new(ErrorDrop.new)
403
+
404
+ assert_raises(StandardError) do
405
+ Liquid::Template.parse('{% for i in (1..2) %}{{ standard_error }}{% endfor %}').render!(context)
406
+ end
407
+
408
+ assert context.registers[:for_stack].empty?
409
+ end
410
+ end
@@ -0,0 +1,188 @@
1
+ require 'test_helper'
2
+
3
+ class IfElseTagTest < Minitest::Test
4
+ include Liquid
5
+
6
+ def test_if
7
+ assert_template_result(' ', ' {% if false %} this text should not go into the output {% endif %} ')
8
+ assert_template_result(' this text should go into the output ',
9
+ ' {% if true %} this text should go into the output {% endif %} ')
10
+ assert_template_result(' you rock ?', '{% if false %} you suck {% endif %} {% if true %} you rock {% endif %}?')
11
+ end
12
+
13
+ def test_literal_comparisons
14
+ assert_template_result(' NO ', '{% assign v = false %}{% if v %} YES {% else %} NO {% endif %}')
15
+ assert_template_result(' YES ', '{% assign v = nil %}{% if v == nil %} YES {% else %} NO {% endif %}')
16
+ end
17
+
18
+ def test_if_else
19
+ assert_template_result(' YES ', '{% if false %} NO {% else %} YES {% endif %}')
20
+ assert_template_result(' YES ', '{% if true %} YES {% else %} NO {% endif %}')
21
+ assert_template_result(' YES ', '{% if "foo" %} YES {% else %} NO {% endif %}')
22
+ end
23
+
24
+ def test_if_boolean
25
+ assert_template_result(' YES ', '{% if var %} YES {% endif %}', 'var' => true)
26
+ end
27
+
28
+ def test_if_or
29
+ assert_template_result(' YES ', '{% if a or b %} YES {% endif %}', 'a' => true, 'b' => true)
30
+ assert_template_result(' YES ', '{% if a or b %} YES {% endif %}', 'a' => true, 'b' => false)
31
+ assert_template_result(' YES ', '{% if a or b %} YES {% endif %}', 'a' => false, 'b' => true)
32
+ assert_template_result('', '{% if a or b %} YES {% endif %}', 'a' => false, 'b' => false)
33
+
34
+ assert_template_result(' YES ', '{% if a or b or c %} YES {% endif %}', 'a' => false, 'b' => false, 'c' => true)
35
+ assert_template_result('', '{% if a or b or c %} YES {% endif %}', 'a' => false, 'b' => false, 'c' => false)
36
+ end
37
+
38
+ def test_if_or_with_operators
39
+ assert_template_result(' YES ', '{% if a == true or b == true %} YES {% endif %}', 'a' => true, 'b' => true)
40
+ assert_template_result(' YES ', '{% if a == true or b == false %} YES {% endif %}', 'a' => true, 'b' => true)
41
+ assert_template_result('', '{% if a == false or b == false %} YES {% endif %}', 'a' => true, 'b' => true)
42
+ end
43
+
44
+ def test_comparison_of_strings_containing_and_or_or
45
+ awful_markup = "a == 'and' and b == 'or' and c == 'foo and bar' and d == 'bar or baz' and e == 'foo' and foo and bar"
46
+ assigns = { 'a' => 'and', 'b' => 'or', 'c' => 'foo and bar', 'd' => 'bar or baz', 'e' => 'foo', 'foo' => true, 'bar' => true }
47
+ assert_template_result(' YES ', "{% if #{awful_markup} %} YES {% endif %}", assigns)
48
+ end
49
+
50
+ def test_comparison_of_expressions_starting_with_and_or_or
51
+ assigns = { 'order' => { 'items_count' => 0 }, 'android' => { 'name' => 'Roy' } }
52
+ assert_template_result("YES",
53
+ "{% if android.name == 'Roy' %}YES{% endif %}",
54
+ assigns)
55
+ assert_template_result("YES",
56
+ "{% if order.items_count == 0 %}YES{% endif %}",
57
+ assigns)
58
+ end
59
+
60
+ def test_if_and
61
+ assert_template_result(' YES ', '{% if true and true %} YES {% endif %}')
62
+ assert_template_result('', '{% if false and true %} YES {% endif %}')
63
+ assert_template_result('', '{% if false and true %} YES {% endif %}')
64
+ end
65
+
66
+ def test_hash_miss_generates_false
67
+ assert_template_result('', '{% if foo.bar %} NO {% endif %}', 'foo' => {})
68
+ end
69
+
70
+ def test_if_from_variable
71
+ assert_template_result('', '{% if var %} NO {% endif %}', 'var' => false)
72
+ assert_template_result('', '{% if var %} NO {% endif %}', 'var' => nil)
73
+ assert_template_result('', '{% if foo.bar %} NO {% endif %}', 'foo' => { 'bar' => false })
74
+ assert_template_result('', '{% if foo.bar %} NO {% endif %}', 'foo' => {})
75
+ assert_template_result('', '{% if foo.bar %} NO {% endif %}', 'foo' => nil)
76
+ assert_template_result('', '{% if foo.bar %} NO {% endif %}', 'foo' => true)
77
+
78
+ assert_template_result(' YES ', '{% if var %} YES {% endif %}', 'var' => "text")
79
+ assert_template_result(' YES ', '{% if var %} YES {% endif %}', 'var' => true)
80
+ assert_template_result(' YES ', '{% if var %} YES {% endif %}', 'var' => 1)
81
+ assert_template_result(' YES ', '{% if var %} YES {% endif %}', 'var' => {})
82
+ assert_template_result(' YES ', '{% if var %} YES {% endif %}', 'var' => [])
83
+ assert_template_result(' YES ', '{% if "foo" %} YES {% endif %}')
84
+ assert_template_result(' YES ', '{% if foo.bar %} YES {% endif %}', 'foo' => { 'bar' => true })
85
+ assert_template_result(' YES ', '{% if foo.bar %} YES {% endif %}', 'foo' => { 'bar' => "text" })
86
+ assert_template_result(' YES ', '{% if foo.bar %} YES {% endif %}', 'foo' => { 'bar' => 1 })
87
+ assert_template_result(' YES ', '{% if foo.bar %} YES {% endif %}', 'foo' => { 'bar' => {} })
88
+ assert_template_result(' YES ', '{% if foo.bar %} YES {% endif %}', 'foo' => { 'bar' => [] })
89
+
90
+ assert_template_result(' YES ', '{% if var %} NO {% else %} YES {% endif %}', 'var' => false)
91
+ assert_template_result(' YES ', '{% if var %} NO {% else %} YES {% endif %}', 'var' => nil)
92
+ assert_template_result(' YES ', '{% if var %} YES {% else %} NO {% endif %}', 'var' => true)
93
+ assert_template_result(' YES ', '{% if "foo" %} YES {% else %} NO {% endif %}', 'var' => "text")
94
+
95
+ assert_template_result(' YES ', '{% if foo.bar %} NO {% else %} YES {% endif %}', 'foo' => { 'bar' => false })
96
+ assert_template_result(' YES ', '{% if foo.bar %} YES {% else %} NO {% endif %}', 'foo' => { 'bar' => true })
97
+ assert_template_result(' YES ', '{% if foo.bar %} YES {% else %} NO {% endif %}', 'foo' => { 'bar' => "text" })
98
+ assert_template_result(' YES ', '{% if foo.bar %} NO {% else %} YES {% endif %}', 'foo' => { 'notbar' => true })
99
+ assert_template_result(' YES ', '{% if foo.bar %} NO {% else %} YES {% endif %}', 'foo' => {})
100
+ assert_template_result(' YES ', '{% if foo.bar %} NO {% else %} YES {% endif %}', 'notfoo' => { 'bar' => true })
101
+ end
102
+
103
+ def test_nested_if
104
+ assert_template_result('', '{% if false %}{% if false %} NO {% endif %}{% endif %}')
105
+ assert_template_result('', '{% if false %}{% if true %} NO {% endif %}{% endif %}')
106
+ assert_template_result('', '{% if true %}{% if false %} NO {% endif %}{% endif %}')
107
+ assert_template_result(' YES ', '{% if true %}{% if true %} YES {% endif %}{% endif %}')
108
+
109
+ assert_template_result(' YES ', '{% if true %}{% if true %} YES {% else %} NO {% endif %}{% else %} NO {% endif %}')
110
+ assert_template_result(' YES ', '{% if true %}{% if false %} NO {% else %} YES {% endif %}{% else %} NO {% endif %}')
111
+ assert_template_result(' YES ', '{% if false %}{% if true %} NO {% else %} NONO {% endif %}{% else %} YES {% endif %}')
112
+ end
113
+
114
+ def test_comparisons_on_null
115
+ assert_template_result('', '{% if null < 10 %} NO {% endif %}')
116
+ assert_template_result('', '{% if null <= 10 %} NO {% endif %}')
117
+ assert_template_result('', '{% if null >= 10 %} NO {% endif %}')
118
+ assert_template_result('', '{% if null > 10 %} NO {% endif %}')
119
+
120
+ assert_template_result('', '{% if 10 < null %} NO {% endif %}')
121
+ assert_template_result('', '{% if 10 <= null %} NO {% endif %}')
122
+ assert_template_result('', '{% if 10 >= null %} NO {% endif %}')
123
+ assert_template_result('', '{% if 10 > null %} NO {% endif %}')
124
+ end
125
+
126
+ def test_else_if
127
+ assert_template_result('0', '{% if 0 == 0 %}0{% elsif 1 == 1%}1{% else %}2{% endif %}')
128
+ assert_template_result('1', '{% if 0 != 0 %}0{% elsif 1 == 1%}1{% else %}2{% endif %}')
129
+ assert_template_result('2', '{% if 0 != 0 %}0{% elsif 1 != 1%}1{% else %}2{% endif %}')
130
+
131
+ assert_template_result('elsif', '{% if false %}if{% elsif true %}elsif{% endif %}')
132
+ end
133
+
134
+ def test_syntax_error_no_variable
135
+ assert_raises(SyntaxError){ assert_template_result('', '{% if jerry == 1 %}') }
136
+ end
137
+
138
+ def test_syntax_error_no_expression
139
+ assert_raises(SyntaxError) { assert_template_result('', '{% if %}') }
140
+ end
141
+
142
+ def test_if_with_custom_condition
143
+ original_op = Condition.operators['contains']
144
+ Condition.operators['contains'] = :[]
145
+
146
+ assert_template_result('yes', %({% if 'bob' contains 'o' %}yes{% endif %}))
147
+ assert_template_result('no', %({% if 'bob' contains 'f' %}yes{% else %}no{% endif %}))
148
+ ensure
149
+ Condition.operators['contains'] = original_op
150
+ end
151
+
152
+ def test_operators_are_ignored_unless_isolated
153
+ original_op = Condition.operators['contains']
154
+ Condition.operators['contains'] = :[]
155
+
156
+ assert_template_result('yes',
157
+ %({% if 'gnomeslab-and-or-liquid' contains 'gnomeslab-and-or-liquid' %}yes{% endif %}))
158
+ ensure
159
+ Condition.operators['contains'] = original_op
160
+ end
161
+
162
+ def test_operators_are_whitelisted
163
+ assert_raises(SyntaxError) do
164
+ assert_template_result('', %({% if 1 or throw or or 1 %}yes{% endif %}))
165
+ end
166
+ end
167
+
168
+ def test_multiple_conditions
169
+ tpl = "{% if a or b and c %}true{% else %}false{% endif %}"
170
+
171
+ tests = {
172
+ [true, true, true] => true,
173
+ [true, true, false] => true,
174
+ [true, false, true] => true,
175
+ [true, false, false] => true,
176
+ [false, true, true] => true,
177
+ [false, true, false] => false,
178
+ [false, false, true] => false,
179
+ [false, false, false] => false,
180
+ }
181
+
182
+ tests.each do |vals, expected|
183
+ a, b, c = vals
184
+ assigns = { 'a' => a, 'b' => b, 'c' => c }
185
+ assert_template_result expected.to_s, tpl, assigns, assigns.to_s
186
+ end
187
+ end
188
+ end