liquid 3.0.6 → 4.0.3

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 (103) hide show
  1. checksums.yaml +5 -5
  2. data/History.md +154 -58
  3. data/{MIT-LICENSE → LICENSE} +0 -0
  4. data/README.md +33 -0
  5. data/lib/liquid/block.rb +42 -125
  6. data/lib/liquid/block_body.rb +99 -79
  7. data/lib/liquid/condition.rb +52 -32
  8. data/lib/liquid/context.rb +57 -51
  9. data/lib/liquid/document.rb +19 -9
  10. data/lib/liquid/drop.rb +17 -16
  11. data/lib/liquid/errors.rb +20 -24
  12. data/lib/liquid/expression.rb +26 -10
  13. data/lib/liquid/extensions.rb +19 -7
  14. data/lib/liquid/file_system.rb +11 -11
  15. data/lib/liquid/forloop_drop.rb +42 -0
  16. data/lib/liquid/i18n.rb +6 -6
  17. data/lib/liquid/interrupts.rb +1 -2
  18. data/lib/liquid/lexer.rb +12 -8
  19. data/lib/liquid/locales/en.yml +6 -2
  20. data/lib/liquid/parse_context.rb +38 -0
  21. data/lib/liquid/parse_tree_visitor.rb +42 -0
  22. data/lib/liquid/parser_switching.rb +4 -4
  23. data/lib/liquid/profiler/hooks.rb +7 -7
  24. data/lib/liquid/profiler.rb +18 -19
  25. data/lib/liquid/range_lookup.rb +16 -1
  26. data/lib/liquid/resource_limits.rb +23 -0
  27. data/lib/liquid/standardfilters.rb +207 -61
  28. data/lib/liquid/strainer.rb +15 -8
  29. data/lib/liquid/tablerowloop_drop.rb +62 -0
  30. data/lib/liquid/tag.rb +9 -8
  31. data/lib/liquid/tags/assign.rb +25 -4
  32. data/lib/liquid/tags/break.rb +0 -3
  33. data/lib/liquid/tags/capture.rb +1 -1
  34. data/lib/liquid/tags/case.rb +27 -12
  35. data/lib/liquid/tags/comment.rb +2 -2
  36. data/lib/liquid/tags/cycle.rb +16 -8
  37. data/lib/liquid/tags/decrement.rb +1 -4
  38. data/lib/liquid/tags/for.rb +103 -75
  39. data/lib/liquid/tags/if.rb +60 -44
  40. data/lib/liquid/tags/ifchanged.rb +0 -2
  41. data/lib/liquid/tags/include.rb +71 -51
  42. data/lib/liquid/tags/raw.rb +32 -4
  43. data/lib/liquid/tags/table_row.rb +21 -31
  44. data/lib/liquid/tags/unless.rb +3 -4
  45. data/lib/liquid/template.rb +42 -54
  46. data/lib/liquid/tokenizer.rb +31 -0
  47. data/lib/liquid/truffle.rb +5 -0
  48. data/lib/liquid/utils.rb +52 -8
  49. data/lib/liquid/variable.rb +59 -46
  50. data/lib/liquid/variable_lookup.rb +14 -6
  51. data/lib/liquid/version.rb +2 -1
  52. data/lib/liquid.rb +10 -7
  53. data/test/integration/assign_test.rb +8 -8
  54. data/test/integration/blank_test.rb +14 -14
  55. data/test/integration/block_test.rb +12 -0
  56. data/test/integration/context_test.rb +2 -2
  57. data/test/integration/document_test.rb +19 -0
  58. data/test/integration/drop_test.rb +42 -40
  59. data/test/integration/error_handling_test.rb +96 -43
  60. data/test/integration/filter_test.rb +60 -20
  61. data/test/integration/hash_ordering_test.rb +9 -9
  62. data/test/integration/output_test.rb +26 -27
  63. data/test/integration/parse_tree_visitor_test.rb +247 -0
  64. data/test/integration/parsing_quirks_test.rb +19 -13
  65. data/test/integration/render_profiling_test.rb +20 -20
  66. data/test/integration/security_test.rb +23 -7
  67. data/test/integration/standard_filter_test.rb +426 -46
  68. data/test/integration/tags/break_tag_test.rb +1 -2
  69. data/test/integration/tags/continue_tag_test.rb +0 -1
  70. data/test/integration/tags/for_tag_test.rb +135 -100
  71. data/test/integration/tags/if_else_tag_test.rb +75 -77
  72. data/test/integration/tags/include_tag_test.rb +50 -31
  73. data/test/integration/tags/increment_tag_test.rb +10 -11
  74. data/test/integration/tags/raw_tag_test.rb +7 -1
  75. data/test/integration/tags/standard_tag_test.rb +121 -122
  76. data/test/integration/tags/statements_test.rb +3 -5
  77. data/test/integration/tags/table_row_test.rb +20 -19
  78. data/test/integration/tags/unless_else_tag_test.rb +6 -6
  79. data/test/integration/template_test.rb +199 -49
  80. data/test/integration/trim_mode_test.rb +529 -0
  81. data/test/integration/variable_test.rb +27 -13
  82. data/test/test_helper.rb +33 -6
  83. data/test/truffle/truffle_test.rb +9 -0
  84. data/test/unit/block_unit_test.rb +8 -5
  85. data/test/unit/condition_unit_test.rb +94 -77
  86. data/test/unit/context_unit_test.rb +69 -72
  87. data/test/unit/file_system_unit_test.rb +3 -3
  88. data/test/unit/i18n_unit_test.rb +2 -2
  89. data/test/unit/lexer_unit_test.rb +12 -9
  90. data/test/unit/parser_unit_test.rb +2 -2
  91. data/test/unit/regexp_unit_test.rb +1 -1
  92. data/test/unit/strainer_unit_test.rb +96 -1
  93. data/test/unit/tag_unit_test.rb +7 -2
  94. data/test/unit/tags/case_tag_unit_test.rb +1 -1
  95. data/test/unit/tags/for_tag_unit_test.rb +2 -2
  96. data/test/unit/tags/if_tag_unit_test.rb +1 -1
  97. data/test/unit/template_unit_test.rb +14 -5
  98. data/test/unit/tokenizer_unit_test.rb +24 -7
  99. data/test/unit/variable_unit_test.rb +60 -43
  100. metadata +62 -50
  101. data/lib/liquid/module_ex.rb +0 -62
  102. data/lib/liquid/token.rb +0 -18
  103. data/test/unit/module_ex_unit_test.rb +0 -87
@@ -6,11 +6,10 @@ class BreakTagTest < Minitest::Test
6
6
  # tests that no weird errors are raised if break is called outside of a
7
7
  # block
8
8
  def test_break_with_no_block
9
- assigns = {'i' => 1}
9
+ assigns = { 'i' => 1 }
10
10
  markup = '{% break %}'
11
11
  expected = ''
12
12
 
13
13
  assert_template_result(expected, markup, assigns)
14
14
  end
15
-
16
15
  end
@@ -12,5 +12,4 @@ class ContinueTagTest < Minitest::Test
12
12
 
13
13
  assert_template_result(expected, markup, assigns)
14
14
  end
15
-
16
15
  end
@@ -10,10 +10,10 @@ class ForTagTest < Minitest::Test
10
10
  include Liquid
11
11
 
12
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])
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
17
  expected = <<HERE
18
18
 
19
19
  yo
@@ -28,46 +28,52 @@ HERE
28
28
  yo
29
29
  {%endfor%}
30
30
  HERE
31
- assert_template_result(expected,template,'array' => [1,2,3])
31
+ assert_template_result(expected, template, 'array' => [1, 2, 3])
32
32
  end
33
33
 
34
34
  def test_for_reversed
35
- assigns = {'array' => [ 1, 2, 3] }
36
- assert_template_result('321','{%for item in array reversed %}{{item}}{%endfor%}',assigns)
35
+ assigns = { 'array' => [ 1, 2, 3] }
36
+ assert_template_result('321', '{%for item in array reversed %}{{item}}{%endfor%}', assigns)
37
37
  end
38
38
 
39
39
  def test_for_with_range
40
- assert_template_result(' 1 2 3 ','{%for item in (1..3) %} {{item}} {%endfor%}')
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")
41
47
  end
42
48
 
43
49
  def test_for_with_variable_range
44
- assert_template_result(' 1 2 3 ','{%for item in (1..foobar) %} {{item}} {%endfor%}', "foobar" => 3)
50
+ assert_template_result(' 1 2 3 ', '{%for item in (1..foobar) %} {{item}} {%endfor%}', "foobar" => 3)
45
51
  end
46
52
 
47
53
  def test_for_with_hash_value_range
48
54
  foobar = { "value" => 3 }
49
- assert_template_result(' 1 2 3 ','{%for item in (1..foobar.value) %} {{item}} {%endfor%}', "foobar" => foobar)
55
+ assert_template_result(' 1 2 3 ', '{%for item in (1..foobar.value) %} {{item}} {%endfor%}', "foobar" => foobar)
50
56
  end
51
57
 
52
58
  def test_for_with_drop_value_range
53
59
  foobar = ThingWithValue.new
54
- assert_template_result(' 1 2 3 ','{%for item in (1..foobar.value) %} {{item}} {%endfor%}', "foobar" => foobar)
60
+ assert_template_result(' 1 2 3 ', '{%for item in (1..foobar.value) %} {{item}} {%endfor%}', "foobar" => foobar)
55
61
  end
56
62
 
57
63
  def test_for_with_variable
58
- assert_template_result(' 1 2 3 ','{%for item in array%} {{item}} {%endfor%}','array' => [1,2,3])
59
- assert_template_result('123','{%for item in array%}{{item}}{%endfor%}','array' => [1,2,3])
60
- assert_template_result('123','{% for item in array %}{{item}}{% endfor %}','array' => [1,2,3])
61
- assert_template_result('abcd','{%for item in array%}{{item}}{%endfor%}','array' => ['a','b','c','d'])
62
- assert_template_result('a b c','{%for item in array%}{{item}}{%endfor%}','array' => ['a',' ','b',' ','c'])
63
- assert_template_result('abc','{%for item in array%}{{item}}{%endfor%}','array' => ['a','','b','','c'])
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'])
64
70
  end
65
71
 
66
72
  def test_for_helpers
67
- assigns = {'array' => [1,2,3] }
73
+ assigns = { 'array' => [1, 2, 3] }
68
74
  assert_template_result(' 1/3 2/3 3/3 ',
69
- '{%for item in array%} {{forloop.index}}/{{forloop.length}} {%endfor%}',
70
- assigns)
75
+ '{%for item in array%} {{forloop.index}}/{{forloop.length}} {%endfor%}',
76
+ assigns)
71
77
  assert_template_result(' 1 2 3 ', '{%for item in array%} {{forloop.index}} {%endfor%}', assigns)
72
78
  assert_template_result(' 0 1 2 ', '{%for item in array%} {{forloop.index0}} {%endfor%}', assigns)
73
79
  assert_template_result(' 2 1 0 ', '{%for item in array%} {{forloop.rindex0}} {%endfor%}', assigns)
@@ -77,20 +83,20 @@ HERE
77
83
  end
78
84
 
79
85
  def test_for_and_if
80
- assigns = {'array' => [1,2,3] }
86
+ assigns = { 'array' => [1, 2, 3] }
81
87
  assert_template_result('+--',
82
- '{%for item in array%}{% if forloop.first %}+{% else %}-{% endif %}{%endfor%}',
83
- assigns)
88
+ '{%for item in array%}{% if forloop.first %}+{% else %}-{% endif %}{%endfor%}',
89
+ assigns)
84
90
  end
85
91
 
86
92
  def test_for_else
87
- assert_template_result('+++', '{%for item in array%}+{%else%}-{%endfor%}', 'array'=>[1,2,3])
88
- assert_template_result('-', '{%for item in array%}+{%else%}-{%endfor%}', 'array'=>[])
89
- assert_template_result('-', '{%for item in array%}+{%else%}-{%endfor%}', 'array'=>nil)
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)
90
96
  end
91
97
 
92
98
  def test_limiting
93
- assigns = {'array' => [1,2,3,4,5,6,7,8,9,0]}
99
+ assigns = { 'array' => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] }
94
100
  assert_template_result('12', '{%for i in array limit:2 %}{{ i }}{%endfor%}', assigns)
95
101
  assert_template_result('1234', '{%for i in array limit:4 %}{{ i }}{%endfor%}', assigns)
96
102
  assert_template_result('3456', '{%for i in array limit:4 offset:2 %}{{ i }}{%endfor%}', assigns)
@@ -98,7 +104,7 @@ HERE
98
104
  end
99
105
 
100
106
  def test_dynamic_variable_limiting
101
- assigns = {'array' => [1,2,3,4,5,6,7,8,9,0]}
107
+ assigns = { 'array' => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] }
102
108
  assigns['limit'] = 2
103
109
  assigns['offset'] = 2
104
110
 
@@ -106,17 +112,17 @@ HERE
106
112
  end
107
113
 
108
114
  def test_nested_for
109
- assigns = {'array' => [[1,2],[3,4],[5,6]] }
115
+ assigns = { 'array' => [[1, 2], [3, 4], [5, 6]] }
110
116
  assert_template_result('123456', '{%for item in array%}{%for i in item%}{{ i }}{%endfor%}{%endfor%}', assigns)
111
117
  end
112
118
 
113
119
  def test_offset_only
114
- assigns = {'array' => [1,2,3,4,5,6,7,8,9,0]}
120
+ assigns = { 'array' => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] }
115
121
  assert_template_result('890', '{%for i in array offset:7 %}{{ i }}{%endfor%}', assigns)
116
122
  end
117
123
 
118
124
  def test_pause_resume
119
- assigns = {'array' => {'items' => [1,2,3,4,5,6,7,8,9,0]}}
125
+ assigns = { 'array' => { 'items' => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] } }
120
126
  markup = <<-MKUP
121
127
  {%for i in array.items limit: 3 %}{{i}}{%endfor%}
122
128
  next
@@ -131,11 +137,11 @@ HERE
131
137
  next
132
138
  789
133
139
  XPCTD
134
- assert_template_result(expected,markup,assigns)
140
+ assert_template_result(expected, markup, assigns)
135
141
  end
136
142
 
137
143
  def test_pause_resume_limit
138
- assigns = {'array' => {'items' => [1,2,3,4,5,6,7,8,9,0]}}
144
+ assigns = { 'array' => { 'items' => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] } }
139
145
  markup = <<-MKUP
140
146
  {%for i in array.items limit:3 %}{{i}}{%endfor%}
141
147
  next
@@ -150,11 +156,11 @@ HERE
150
156
  next
151
157
  7
152
158
  XPCTD
153
- assert_template_result(expected,markup,assigns)
159
+ assert_template_result(expected, markup, assigns)
154
160
  end
155
161
 
156
- def test_pause_resume_BIG_limit
157
- assigns = {'array' => {'items' => [1,2,3,4,5,6,7,8,9,0]}}
162
+ def test_pause_resume_big_limit
163
+ assigns = { 'array' => { 'items' => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] } }
158
164
  markup = <<-MKUP
159
165
  {%for i in array.items limit:3 %}{{i}}{%endfor%}
160
166
  next
@@ -169,103 +175,102 @@ HERE
169
175
  next
170
176
  7890
171
177
  XPCTD
172
- assert_template_result(expected,markup,assigns)
178
+ assert_template_result(expected, markup, assigns)
173
179
  end
174
180
 
175
-
176
- def test_pause_resume_BIG_offset
177
- assigns = {'array' => {'items' => [1,2,3,4,5,6,7,8,9,0]}}
178
- markup = %q({%for i in array.items limit:3 %}{{i}}{%endfor%}
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%}
179
184
  next
180
185
  {%for i in array.items offset:continue limit:3 %}{{i}}{%endfor%}
181
186
  next
182
- {%for i in array.items offset:continue limit:3 offset:1000 %}{{i}}{%endfor%})
183
- expected = %q(123
187
+ {%for i in array.items offset:continue limit:3 offset:1000 %}{{i}}{%endfor%}'
188
+ expected = '123
184
189
  next
185
190
  456
186
191
  next
187
- )
188
- assert_template_result(expected,markup,assigns)
192
+ '
193
+ assert_template_result(expected, markup, assigns)
189
194
  end
190
195
 
191
196
  def test_for_with_break
192
- assigns = {'array' => {'items' => [1,2,3,4,5,6,7,8,9,10]}}
197
+ assigns = { 'array' => { 'items' => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] } }
193
198
 
194
199
  markup = '{% for i in array.items %}{% break %}{% endfor %}'
195
200
  expected = ""
196
- assert_template_result(expected,markup,assigns)
201
+ assert_template_result(expected, markup, assigns)
197
202
 
198
203
  markup = '{% for i in array.items %}{{ i }}{% break %}{% endfor %}'
199
204
  expected = "1"
200
- assert_template_result(expected,markup,assigns)
205
+ assert_template_result(expected, markup, assigns)
201
206
 
202
207
  markup = '{% for i in array.items %}{% break %}{{ i }}{% endfor %}'
203
208
  expected = ""
204
- assert_template_result(expected,markup,assigns)
209
+ assert_template_result(expected, markup, assigns)
205
210
 
206
211
  markup = '{% for i in array.items %}{{ i }}{% if i > 3 %}{% break %}{% endif %}{% endfor %}'
207
212
  expected = "1234"
208
- assert_template_result(expected,markup,assigns)
213
+ assert_template_result(expected, markup, assigns)
209
214
 
210
215
  # tests to ensure it only breaks out of the local for loop
211
216
  # and not all of them.
212
- assigns = {'array' => [[1,2],[3,4],[5,6]] }
213
- markup = '{% for item in array %}' +
214
- '{% for i in item %}' +
215
- '{% if i == 1 %}' +
216
- '{% break %}' +
217
- '{% endif %}' +
218
- '{{ i }}' +
219
- '{% endfor %}' +
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 %}' \
220
225
  '{% endfor %}'
221
226
  expected = '3456'
222
227
  assert_template_result(expected, markup, assigns)
223
228
 
224
229
  # test break does nothing when unreached
225
- assigns = {'array' => {'items' => [1,2,3,4,5]}}
230
+ assigns = { 'array' => { 'items' => [1, 2, 3, 4, 5] } }
226
231
  markup = '{% for i in array.items %}{% if i == 9999 %}{% break %}{% endif %}{{ i }}{% endfor %}'
227
232
  expected = '12345'
228
233
  assert_template_result(expected, markup, assigns)
229
234
  end
230
235
 
231
236
  def test_for_with_continue
232
- assigns = {'array' => {'items' => [1,2,3,4,5]}}
237
+ assigns = { 'array' => { 'items' => [1, 2, 3, 4, 5] } }
233
238
 
234
239
  markup = '{% for i in array.items %}{% continue %}{% endfor %}'
235
240
  expected = ""
236
- assert_template_result(expected,markup,assigns)
241
+ assert_template_result(expected, markup, assigns)
237
242
 
238
243
  markup = '{% for i in array.items %}{{ i }}{% continue %}{% endfor %}'
239
244
  expected = "12345"
240
- assert_template_result(expected,markup,assigns)
245
+ assert_template_result(expected, markup, assigns)
241
246
 
242
247
  markup = '{% for i in array.items %}{% continue %}{{ i }}{% endfor %}'
243
248
  expected = ""
244
- assert_template_result(expected,markup,assigns)
249
+ assert_template_result(expected, markup, assigns)
245
250
 
246
251
  markup = '{% for i in array.items %}{% if i > 3 %}{% continue %}{% endif %}{{ i }}{% endfor %}'
247
252
  expected = "123"
248
- assert_template_result(expected,markup,assigns)
253
+ assert_template_result(expected, markup, assigns)
249
254
 
250
255
  markup = '{% for i in array.items %}{% if i == 3 %}{% continue %}{% else %}{{ i }}{% endif %}{% endfor %}'
251
256
  expected = "1245"
252
- assert_template_result(expected,markup,assigns)
257
+ assert_template_result(expected, markup, assigns)
253
258
 
254
259
  # tests to ensure it only continues the local for loop and not all of them.
255
- assigns = {'array' => [[1,2],[3,4],[5,6]] }
256
- markup = '{% for item in array %}' +
257
- '{% for i in item %}' +
258
- '{% if i == 1 %}' +
259
- '{% continue %}' +
260
- '{% endif %}' +
261
- '{{ i }}' +
262
- '{% endfor %}' +
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 %}' \
263
268
  '{% endfor %}'
264
269
  expected = '23456'
265
270
  assert_template_result(expected, markup, assigns)
266
271
 
267
272
  # test continue does nothing when unreached
268
- assigns = {'array' => {'items' => [1,2,3,4,5]}}
273
+ assigns = { 'array' => { 'items' => [1, 2, 3, 4, 5] } }
269
274
  markup = '{% for i in array.items %}{% if i == 9999 %}{% continue %}{% endif %}{{ i }}{% endfor %}'
270
275
  expected = '12345'
271
276
  assert_template_result(expected, markup, assigns)
@@ -277,25 +282,45 @@ HERE
277
282
  # the functionality for backwards compatibility
278
283
 
279
284
  assert_template_result('test string',
280
- '{%for val in string%}{{val}}{%endfor%}',
281
- 'string' => "test string")
285
+ '{%for val in string%}{{val}}{%endfor%}',
286
+ 'string' => "test string")
282
287
 
283
288
  assert_template_result('test string',
284
- '{%for val in string limit:1%}{{val}}{%endfor%}',
285
- 'string' => "test string")
289
+ '{%for val in string limit:1%}{{val}}{%endfor%}',
290
+ 'string' => "test string")
286
291
 
287
292
  assert_template_result('val-string-1-1-0-1-0-true-true-test string',
288
- '{%for val in string%}' +
289
- '{{forloop.name}}-' +
290
- '{{forloop.index}}-' +
291
- '{{forloop.length}}-' +
292
- '{{forloop.index0}}-' +
293
- '{{forloop.rindex}}-' +
294
- '{{forloop.rindex0}}-' +
295
- '{{forloop.first}}-' +
296
- '{{forloop.last}}-' +
297
- '{{val}}{%endfor%}',
298
- 'string' => "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 %}'
299
324
  end
300
325
 
301
326
  def test_blank_string_not_iterable
@@ -311,7 +336,7 @@ HERE
311
336
  def test_spacing_with_variable_naming_in_for_loop
312
337
  expected = '12345'
313
338
  template = '{% for item in items %}{{item}}{% endfor %}'
314
- assigns = {'items' => [1,2,3,4,5]}
339
+ assigns = { 'items' => [1, 2, 3, 4, 5] }
315
340
  assert_template_result(expected, template, assigns)
316
341
  end
317
342
 
@@ -329,13 +354,13 @@ HERE
329
354
 
330
355
  def load_slice(from, to)
331
356
  @load_slice_called = true
332
- @data[(from..to-1)]
357
+ @data[(from..to - 1)]
333
358
  end
334
359
  end
335
360
 
336
361
  def test_iterate_with_each_when_no_limit_applied
337
- loader = LoaderDrop.new([1,2,3,4,5])
338
- assigns = {'items' => loader}
362
+ loader = LoaderDrop.new([1, 2, 3, 4, 5])
363
+ assigns = { 'items' => loader }
339
364
  expected = '12345'
340
365
  template = '{% for item in items %}{{item}}{% endfor %}'
341
366
  assert_template_result(expected, template, assigns)
@@ -344,8 +369,8 @@ HERE
344
369
  end
345
370
 
346
371
  def test_iterate_with_load_slice_when_limit_applied
347
- loader = LoaderDrop.new([1,2,3,4,5])
348
- assigns = {'items' => loader}
372
+ loader = LoaderDrop.new([1, 2, 3, 4, 5])
373
+ assigns = { 'items' => loader }
349
374
  expected = '1'
350
375
  template = '{% for item in items limit:1 %}{{item}}{% endfor %}'
351
376
  assert_template_result(expected, template, assigns)
@@ -354,8 +379,8 @@ HERE
354
379
  end
355
380
 
356
381
  def test_iterate_with_load_slice_when_limit_and_offset_applied
357
- loader = LoaderDrop.new([1,2,3,4,5])
358
- assigns = {'items' => loader}
382
+ loader = LoaderDrop.new([1, 2, 3, 4, 5])
383
+ assigns = { 'items' => loader }
359
384
  expected = '34'
360
385
  template = '{% for item in items offset:2 limit:2 %}{{item}}{% endfor %}'
361
386
  assert_template_result(expected, template, assigns)
@@ -364,12 +389,22 @@ HERE
364
389
  end
365
390
 
366
391
  def test_iterate_with_load_slice_returns_same_results_as_without
367
- loader = LoaderDrop.new([1,2,3,4,5])
368
- loader_assigns = {'items' => loader}
369
- array_assigns = {'items' => [1,2,3,4,5]}
392
+ loader = LoaderDrop.new([1, 2, 3, 4, 5])
393
+ loader_assigns = { 'items' => loader }
394
+ array_assigns = { 'items' => [1, 2, 3, 4, 5] }
370
395
  expected = '34'
371
396
  template = '{% for item in items offset:2 limit:2 %}{{item}}{% endfor %}'
372
397
  assert_template_result(expected, template, loader_assigns)
373
398
  assert_template_result(expected, template, array_assigns)
374
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
375
410
  end