liquid 4.0.3 → 5.0.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 (125) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +33 -0
  3. data/README.md +6 -0
  4. data/lib/liquid.rb +17 -5
  5. data/lib/liquid/block.rb +31 -14
  6. data/lib/liquid/block_body.rb +164 -54
  7. data/lib/liquid/condition.rb +39 -18
  8. data/lib/liquid/context.rb +106 -51
  9. data/lib/liquid/document.rb +47 -9
  10. data/lib/liquid/drop.rb +4 -2
  11. data/lib/liquid/errors.rb +20 -18
  12. data/lib/liquid/expression.rb +29 -34
  13. data/lib/liquid/extensions.rb +2 -0
  14. data/lib/liquid/file_system.rb +6 -4
  15. data/lib/liquid/forloop_drop.rb +11 -4
  16. data/lib/liquid/i18n.rb +5 -3
  17. data/lib/liquid/interrupts.rb +3 -1
  18. data/lib/liquid/lexer.rb +30 -23
  19. data/lib/liquid/locales/en.yml +3 -1
  20. data/lib/liquid/parse_context.rb +16 -4
  21. data/lib/liquid/parse_tree_visitor.rb +2 -2
  22. data/lib/liquid/parser.rb +30 -18
  23. data/lib/liquid/parser_switching.rb +17 -3
  24. data/lib/liquid/partial_cache.rb +24 -0
  25. data/lib/liquid/profiler.rb +67 -86
  26. data/lib/liquid/profiler/hooks.rb +26 -14
  27. data/lib/liquid/range_lookup.rb +5 -3
  28. data/lib/liquid/register.rb +6 -0
  29. data/lib/liquid/resource_limits.rb +47 -8
  30. data/lib/liquid/standardfilters.rb +63 -44
  31. data/lib/liquid/static_registers.rb +44 -0
  32. data/lib/liquid/strainer_factory.rb +36 -0
  33. data/lib/liquid/strainer_template.rb +53 -0
  34. data/lib/liquid/tablerowloop_drop.rb +6 -4
  35. data/lib/liquid/tag.rb +28 -6
  36. data/lib/liquid/tag/disableable.rb +22 -0
  37. data/lib/liquid/tag/disabler.rb +21 -0
  38. data/lib/liquid/tags/assign.rb +24 -10
  39. data/lib/liquid/tags/break.rb +8 -3
  40. data/lib/liquid/tags/capture.rb +11 -8
  41. data/lib/liquid/tags/case.rb +33 -27
  42. data/lib/liquid/tags/comment.rb +5 -3
  43. data/lib/liquid/tags/continue.rb +8 -3
  44. data/lib/liquid/tags/cycle.rb +25 -14
  45. data/lib/liquid/tags/decrement.rb +6 -3
  46. data/lib/liquid/tags/echo.rb +26 -0
  47. data/lib/liquid/tags/for.rb +68 -44
  48. data/lib/liquid/tags/if.rb +35 -23
  49. data/lib/liquid/tags/ifchanged.rb +11 -10
  50. data/lib/liquid/tags/include.rb +34 -47
  51. data/lib/liquid/tags/increment.rb +7 -3
  52. data/lib/liquid/tags/raw.rb +14 -11
  53. data/lib/liquid/tags/render.rb +84 -0
  54. data/lib/liquid/tags/table_row.rb +23 -19
  55. data/lib/liquid/tags/unless.rb +15 -15
  56. data/lib/liquid/template.rb +55 -71
  57. data/lib/liquid/template_factory.rb +9 -0
  58. data/lib/liquid/tokenizer.rb +17 -9
  59. data/lib/liquid/usage.rb +8 -0
  60. data/lib/liquid/utils.rb +5 -3
  61. data/lib/liquid/variable.rb +46 -41
  62. data/lib/liquid/variable_lookup.rb +8 -6
  63. data/lib/liquid/version.rb +2 -1
  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 -83
  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 -22
  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 +2 -2
  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 +73 -47
  121. data/lib/liquid/strainer.rb +0 -66
  122. data/lib/liquid/truffle.rb +0 -5
  123. data/test/truffle/truffle_test.rb +0 -9
  124. data/test/unit/context_unit_test.rb +0 -489
  125. data/test/unit/strainer_unit_test.rb +0 -164
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
 
3
5
  class DocumentTest < Minitest::Test
@@ -7,13 +9,13 @@ class DocumentTest < Minitest::Test
7
9
  exc = assert_raises(SyntaxError) do
8
10
  Template.parse("{% else %}")
9
11
  end
10
- assert_equal exc.message, "Liquid syntax error: Unexpected outer 'else' tag"
12
+ assert_equal(exc.message, "Liquid syntax error: Unexpected outer 'else' tag")
11
13
  end
12
14
 
13
15
  def test_unknown_tag
14
16
  exc = assert_raises(SyntaxError) do
15
17
  Template.parse("{% foo %}")
16
18
  end
17
- assert_equal exc.message, "Liquid syntax error: Unknown tag 'foo'"
19
+ assert_equal(exc.message, "Liquid syntax error: Unknown tag 'foo'")
18
20
  end
19
21
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
 
3
5
  class ContextDrop < Liquid::Drop
@@ -31,7 +33,7 @@ class ProductDrop < Liquid::Drop
31
33
 
32
34
  class CatchallDrop < Liquid::Drop
33
35
  def liquid_method_missing(method)
34
- 'catchall_method: ' << method.to_s
36
+ "catchall_method: #{method}"
35
37
  end
36
38
  end
37
39
 
@@ -47,10 +49,6 @@ class ProductDrop < Liquid::Drop
47
49
  ContextDrop.new
48
50
  end
49
51
 
50
- def user_input
51
- "foo".taint
52
- end
53
-
54
52
  protected
55
53
 
56
54
  def callmenot
@@ -109,165 +107,151 @@ class DropsTest < Minitest::Test
109
107
 
110
108
  def test_product_drop
111
109
  tpl = Liquid::Template.parse(' ')
112
- assert_equal ' ', tpl.render!('product' => ProductDrop.new)
113
- end
114
-
115
- def test_rendering_raises_on_tainted_attr
116
- with_taint_mode(:error) do
117
- tpl = Liquid::Template.parse('{{ product.user_input }}')
118
- assert_raises TaintedError do
119
- tpl.render!('product' => ProductDrop.new)
120
- end
121
- end
122
- end
123
-
124
- def test_rendering_warns_on_tainted_attr
125
- with_taint_mode(:warn) do
126
- tpl = Liquid::Template.parse('{{ product.user_input }}')
127
- context = Context.new('product' => ProductDrop.new)
128
- tpl.render!(context)
129
- assert_equal [Liquid::TaintedError], context.warnings.map(&:class)
130
- assert_equal "variable 'product.user_input' is tainted and was not escaped", context.warnings.first.to_s(false)
131
- end
132
- end
133
-
134
- def test_rendering_doesnt_raise_on_escaped_tainted_attr
135
- with_taint_mode(:error) do
136
- tpl = Liquid::Template.parse('{{ product.user_input | escape }}')
137
- tpl.render!('product' => ProductDrop.new)
138
- end
110
+ assert_equal(' ', tpl.render!('product' => ProductDrop.new))
139
111
  end
140
112
 
141
113
  def test_drop_does_only_respond_to_whitelisted_methods
142
- assert_equal "", Liquid::Template.parse("{{ product.inspect }}").render!('product' => ProductDrop.new)
143
- assert_equal "", Liquid::Template.parse("{{ product.pretty_inspect }}").render!('product' => ProductDrop.new)
144
- assert_equal "", Liquid::Template.parse("{{ product.whatever }}").render!('product' => ProductDrop.new)
145
- assert_equal "", Liquid::Template.parse('{{ product | map: "inspect" }}').render!('product' => ProductDrop.new)
146
- assert_equal "", Liquid::Template.parse('{{ product | map: "pretty_inspect" }}').render!('product' => ProductDrop.new)
147
- assert_equal "", Liquid::Template.parse('{{ product | map: "whatever" }}').render!('product' => ProductDrop.new)
114
+ assert_equal("", Liquid::Template.parse("{{ product.inspect }}").render!('product' => ProductDrop.new))
115
+ assert_equal("", Liquid::Template.parse("{{ product.pretty_inspect }}").render!('product' => ProductDrop.new))
116
+ assert_equal("", Liquid::Template.parse("{{ product.whatever }}").render!('product' => ProductDrop.new))
117
+ assert_equal("", Liquid::Template.parse('{{ product | map: "inspect" }}').render!('product' => ProductDrop.new))
118
+ assert_equal("", Liquid::Template.parse('{{ product | map: "pretty_inspect" }}').render!('product' => ProductDrop.new))
119
+ assert_equal("", Liquid::Template.parse('{{ product | map: "whatever" }}').render!('product' => ProductDrop.new))
148
120
  end
149
121
 
150
122
  def test_drops_respond_to_to_liquid
151
- assert_equal "text1", Liquid::Template.parse("{{ product.to_liquid.texts.text }}").render!('product' => ProductDrop.new)
152
- assert_equal "text1", Liquid::Template.parse('{{ product | map: "to_liquid" | map: "texts" | map: "text" }}').render!('product' => ProductDrop.new)
123
+ assert_equal("text1", Liquid::Template.parse("{{ product.to_liquid.texts.text }}").render!('product' => ProductDrop.new))
124
+ assert_equal("text1", Liquid::Template.parse('{{ product | map: "to_liquid" | map: "texts" | map: "text" }}').render!('product' => ProductDrop.new))
153
125
  end
154
126
 
155
127
  def test_text_drop
156
128
  output = Liquid::Template.parse(' {{ product.texts.text }} ').render!('product' => ProductDrop.new)
157
- assert_equal ' text1 ', output
129
+ assert_equal(' text1 ', output)
158
130
  end
159
131
 
160
132
  def test_catchall_unknown_method
161
133
  output = Liquid::Template.parse(' {{ product.catchall.unknown }} ').render!('product' => ProductDrop.new)
162
- assert_equal ' catchall_method: unknown ', output
134
+ assert_equal(' catchall_method: unknown ', output)
163
135
  end
164
136
 
165
137
  def test_catchall_integer_argument_drop
166
138
  output = Liquid::Template.parse(' {{ product.catchall[8] }} ').render!('product' => ProductDrop.new)
167
- assert_equal ' catchall_method: 8 ', output
139
+ assert_equal(' catchall_method: 8 ', output)
168
140
  end
169
141
 
170
142
  def test_text_array_drop
171
143
  output = Liquid::Template.parse('{% for text in product.texts.array %} {{text}} {% endfor %}').render!('product' => ProductDrop.new)
172
- assert_equal ' text1 text2 ', output
144
+ assert_equal(' text1 text2 ', output)
173
145
  end
174
146
 
175
147
  def test_context_drop
176
148
  output = Liquid::Template.parse(' {{ context.bar }} ').render!('context' => ContextDrop.new, 'bar' => "carrot")
177
- assert_equal ' carrot ', output
149
+ assert_equal(' carrot ', output)
150
+ end
151
+
152
+ def test_context_drop_array_with_map
153
+ output = Liquid::Template.parse(' {{ contexts | map: "bar" }} ').render!('contexts' => [ContextDrop.new, ContextDrop.new], 'bar' => "carrot")
154
+ assert_equal(' carrotcarrot ', output)
178
155
  end
179
156
 
180
157
  def test_nested_context_drop
181
158
  output = Liquid::Template.parse(' {{ product.context.foo }} ').render!('product' => ProductDrop.new, 'foo' => "monkey")
182
- assert_equal ' monkey ', output
159
+ assert_equal(' monkey ', output)
183
160
  end
184
161
 
185
162
  def test_protected
186
163
  output = Liquid::Template.parse(' {{ product.callmenot }} ').render!('product' => ProductDrop.new)
187
- assert_equal ' ', output
164
+ assert_equal(' ', output)
188
165
  end
189
166
 
190
167
  def test_object_methods_not_allowed
191
168
  [:dup, :clone, :singleton_class, :eval, :class_eval, :inspect].each do |method|
192
169
  output = Liquid::Template.parse(" {{ product.#{method} }} ").render!('product' => ProductDrop.new)
193
- assert_equal ' ', output
170
+ assert_equal(' ', output)
194
171
  end
195
172
  end
196
173
 
197
174
  def test_scope
198
- assert_equal '1', Liquid::Template.parse('{{ context.scopes }}').render!('context' => ContextDrop.new)
199
- assert_equal '2', Liquid::Template.parse('{%for i in dummy%}{{ context.scopes }}{%endfor%}').render!('context' => ContextDrop.new, 'dummy' => [1])
200
- assert_equal '3', Liquid::Template.parse('{%for i in dummy%}{%for i in dummy%}{{ context.scopes }}{%endfor%}{%endfor%}').render!('context' => ContextDrop.new, 'dummy' => [1])
175
+ assert_equal('1', Liquid::Template.parse('{{ context.scopes }}').render!('context' => ContextDrop.new))
176
+ assert_equal('2', Liquid::Template.parse('{%for i in dummy%}{{ context.scopes }}{%endfor%}').render!('context' => ContextDrop.new, 'dummy' => [1]))
177
+ assert_equal('3', Liquid::Template.parse('{%for i in dummy%}{%for i in dummy%}{{ context.scopes }}{%endfor%}{%endfor%}').render!('context' => ContextDrop.new, 'dummy' => [1]))
201
178
  end
202
179
 
203
180
  def test_scope_though_proc
204
- assert_equal '1', Liquid::Template.parse('{{ s }}').render!('context' => ContextDrop.new, 's' => proc{ |c| c['context.scopes'] })
205
- assert_equal '2', Liquid::Template.parse('{%for i in dummy%}{{ s }}{%endfor%}').render!('context' => ContextDrop.new, 's' => proc{ |c| c['context.scopes'] }, 'dummy' => [1])
206
- assert_equal '3', Liquid::Template.parse('{%for i in dummy%}{%for i in dummy%}{{ s }}{%endfor%}{%endfor%}').render!('context' => ContextDrop.new, 's' => proc{ |c| c['context.scopes'] }, 'dummy' => [1])
181
+ assert_equal('1', Liquid::Template.parse('{{ s }}').render!('context' => ContextDrop.new, 's' => proc { |c| c['context.scopes'] }))
182
+ assert_equal('2', Liquid::Template.parse('{%for i in dummy%}{{ s }}{%endfor%}').render!('context' => ContextDrop.new, 's' => proc { |c| c['context.scopes'] }, 'dummy' => [1]))
183
+ assert_equal('3', Liquid::Template.parse('{%for i in dummy%}{%for i in dummy%}{{ s }}{%endfor%}{%endfor%}').render!('context' => ContextDrop.new, 's' => proc { |c| c['context.scopes'] }, 'dummy' => [1]))
207
184
  end
208
185
 
209
186
  def test_scope_with_assigns
210
- assert_equal 'variable', Liquid::Template.parse('{% assign a = "variable"%}{{a}}').render!('context' => ContextDrop.new)
211
- assert_equal 'variable', Liquid::Template.parse('{% assign a = "variable"%}{%for i in dummy%}{{a}}{%endfor%}').render!('context' => ContextDrop.new, 'dummy' => [1])
212
- assert_equal 'test', Liquid::Template.parse('{% assign header_gif = "test"%}{{header_gif}}').render!('context' => ContextDrop.new)
213
- assert_equal 'test', Liquid::Template.parse("{% assign header_gif = 'test'%}{{header_gif}}").render!('context' => ContextDrop.new)
187
+ assert_equal('variable', Liquid::Template.parse('{% assign a = "variable"%}{{a}}').render!('context' => ContextDrop.new))
188
+ assert_equal('variable', Liquid::Template.parse('{% assign a = "variable"%}{%for i in dummy%}{{a}}{%endfor%}').render!('context' => ContextDrop.new, 'dummy' => [1]))
189
+ assert_equal('test', Liquid::Template.parse('{% assign header_gif = "test"%}{{header_gif}}').render!('context' => ContextDrop.new))
190
+ assert_equal('test', Liquid::Template.parse("{% assign header_gif = 'test'%}{{header_gif}}").render!('context' => ContextDrop.new))
214
191
  end
215
192
 
216
193
  def test_scope_from_tags
217
- assert_equal '1', Liquid::Template.parse('{% for i in context.scopes_as_array %}{{i}}{% endfor %}').render!('context' => ContextDrop.new, 'dummy' => [1])
218
- assert_equal '12', Liquid::Template.parse('{%for a in dummy%}{% for i in context.scopes_as_array %}{{i}}{% endfor %}{% endfor %}').render!('context' => ContextDrop.new, 'dummy' => [1])
219
- assert_equal '123', Liquid::Template.parse('{%for a in dummy%}{%for a in dummy%}{% for i in context.scopes_as_array %}{{i}}{% endfor %}{% endfor %}{% endfor %}').render!('context' => ContextDrop.new, 'dummy' => [1])
194
+ assert_equal('1', Liquid::Template.parse('{% for i in context.scopes_as_array %}{{i}}{% endfor %}').render!('context' => ContextDrop.new, 'dummy' => [1]))
195
+ assert_equal('12', Liquid::Template.parse('{%for a in dummy%}{% for i in context.scopes_as_array %}{{i}}{% endfor %}{% endfor %}').render!('context' => ContextDrop.new, 'dummy' => [1]))
196
+ assert_equal('123', Liquid::Template.parse('{%for a in dummy%}{%for a in dummy%}{% for i in context.scopes_as_array %}{{i}}{% endfor %}{% endfor %}{% endfor %}').render!('context' => ContextDrop.new, 'dummy' => [1]))
220
197
  end
221
198
 
222
199
  def test_access_context_from_drop
223
- assert_equal '123', Liquid::Template.parse('{%for a in dummy%}{{ context.loop_pos }}{% endfor %}').render!('context' => ContextDrop.new, 'dummy' => [1, 2, 3])
200
+ assert_equal('123', Liquid::Template.parse('{%for a in dummy%}{{ context.loop_pos }}{% endfor %}').render!('context' => ContextDrop.new, 'dummy' => [1, 2, 3]))
224
201
  end
225
202
 
226
203
  def test_enumerable_drop
227
- assert_equal '123', Liquid::Template.parse('{% for c in collection %}{{c}}{% endfor %}').render!('collection' => EnumerableDrop.new)
204
+ assert_equal('123', Liquid::Template.parse('{% for c in collection %}{{c}}{% endfor %}').render!('collection' => EnumerableDrop.new))
228
205
  end
229
206
 
230
207
  def test_enumerable_drop_size
231
- assert_equal '3', Liquid::Template.parse('{{collection.size}}').render!('collection' => EnumerableDrop.new)
208
+ assert_equal('3', Liquid::Template.parse('{{collection.size}}').render!('collection' => EnumerableDrop.new))
232
209
  end
233
210
 
234
211
  def test_enumerable_drop_will_invoke_liquid_method_missing_for_clashing_method_names
235
212
  ["select", "each", "map", "cycle"].each do |method|
236
- assert_equal method.to_s, Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => EnumerableDrop.new)
237
- assert_equal method.to_s, Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => EnumerableDrop.new)
238
- assert_equal method.to_s, Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => RealEnumerableDrop.new)
239
- assert_equal method.to_s, Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => RealEnumerableDrop.new)
213
+ assert_equal(method.to_s, Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => EnumerableDrop.new))
214
+ assert_equal(method.to_s, Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => EnumerableDrop.new))
215
+ assert_equal(method.to_s, Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => RealEnumerableDrop.new))
216
+ assert_equal(method.to_s, Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => RealEnumerableDrop.new))
240
217
  end
241
218
  end
242
219
 
243
220
  def test_some_enumerable_methods_still_get_invoked
244
- [ :count, :max ].each do |method|
245
- assert_equal "3", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => RealEnumerableDrop.new)
246
- assert_equal "3", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => RealEnumerableDrop.new)
247
- assert_equal "3", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => EnumerableDrop.new)
248
- assert_equal "3", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => EnumerableDrop.new)
221
+ [:count, :max].each do |method|
222
+ assert_equal("3", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => RealEnumerableDrop.new))
223
+ assert_equal("3", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => RealEnumerableDrop.new))
224
+ assert_equal("3", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => EnumerableDrop.new))
225
+ assert_equal("3", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => EnumerableDrop.new))
249
226
  end
250
227
 
251
- assert_equal "yes", Liquid::Template.parse("{% if collection contains 3 %}yes{% endif %}").render!('collection' => RealEnumerableDrop.new)
228
+ assert_equal("yes", Liquid::Template.parse("{% if collection contains 3 %}yes{% endif %}").render!('collection' => RealEnumerableDrop.new))
252
229
 
253
- [ :min, :first ].each do |method|
254
- assert_equal "1", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => RealEnumerableDrop.new)
255
- assert_equal "1", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => RealEnumerableDrop.new)
256
- assert_equal "1", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => EnumerableDrop.new)
257
- assert_equal "1", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => EnumerableDrop.new)
230
+ [:min, :first].each do |method|
231
+ assert_equal("1", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => RealEnumerableDrop.new))
232
+ assert_equal("1", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => RealEnumerableDrop.new))
233
+ assert_equal("1", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => EnumerableDrop.new))
234
+ assert_equal("1", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => EnumerableDrop.new))
258
235
  end
259
236
  end
260
237
 
261
238
  def test_empty_string_value_access
262
- assert_equal '', Liquid::Template.parse('{{ product[value] }}').render!('product' => ProductDrop.new, 'value' => '')
239
+ assert_equal('', Liquid::Template.parse('{{ product[value] }}').render!('product' => ProductDrop.new, 'value' => ''))
263
240
  end
264
241
 
265
242
  def test_nil_value_access
266
- assert_equal '', Liquid::Template.parse('{{ product[value] }}').render!('product' => ProductDrop.new, 'value' => nil)
243
+ assert_equal('', Liquid::Template.parse('{{ product[value] }}').render!('product' => ProductDrop.new, 'value' => nil))
267
244
  end
268
245
 
269
246
  def test_default_to_s_on_drops
270
- assert_equal 'ProductDrop', Liquid::Template.parse("{{ product }}").render!('product' => ProductDrop.new)
271
- assert_equal 'EnumerableDrop', Liquid::Template.parse('{{ collection }}').render!('collection' => EnumerableDrop.new)
247
+ assert_equal('ProductDrop', Liquid::Template.parse("{{ product }}").render!('product' => ProductDrop.new))
248
+ assert_equal('EnumerableDrop', Liquid::Template.parse('{{ collection }}').render!('collection' => EnumerableDrop.new))
249
+ end
250
+
251
+ def test_invokable_methods
252
+ assert_equal(%w(to_liquid catchall context texts).to_set, ProductDrop.invokable_methods)
253
+ assert_equal(%w(to_liquid scopes_as_array loop_pos scopes).to_set, ContextDrop.invokable_methods)
254
+ assert_equal(%w(to_liquid size max min first count).to_set, EnumerableDrop.invokable_methods)
255
+ assert_equal(%w(to_liquid max min sort count first).to_set, RealEnumerableDrop.invokable_methods)
272
256
  end
273
257
  end # DropsTest
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
 
3
5
  class ErrorHandlingTest < Minitest::Test
@@ -33,31 +35,31 @@ class ErrorHandlingTest < Minitest::Test
33
35
  TEXT
34
36
 
35
37
  output = Liquid::Template.parse(template, line_numbers: true).render('errors' => ErrorDrop.new)
36
- assert_equal expected, output
38
+ assert_equal(expected, output)
37
39
  end
38
40
 
39
41
  def test_standard_error
40
42
  template = Liquid::Template.parse(' {{ errors.standard_error }} ')
41
- assert_equal ' Liquid error: standard error ', template.render('errors' => ErrorDrop.new)
43
+ assert_equal(' Liquid error: standard error ', template.render('errors' => ErrorDrop.new))
42
44
 
43
- assert_equal 1, template.errors.size
44
- assert_equal StandardError, template.errors.first.class
45
+ assert_equal(1, template.errors.size)
46
+ assert_equal(StandardError, template.errors.first.class)
45
47
  end
46
48
 
47
49
  def test_syntax
48
50
  template = Liquid::Template.parse(' {{ errors.syntax_error }} ')
49
- assert_equal ' Liquid syntax error: syntax error ', template.render('errors' => ErrorDrop.new)
51
+ assert_equal(' Liquid syntax error: syntax error ', template.render('errors' => ErrorDrop.new))
50
52
 
51
- assert_equal 1, template.errors.size
52
- assert_equal SyntaxError, template.errors.first.class
53
+ assert_equal(1, template.errors.size)
54
+ assert_equal(SyntaxError, template.errors.first.class)
53
55
  end
54
56
 
55
57
  def test_argument
56
58
  template = Liquid::Template.parse(' {{ errors.argument_error }} ')
57
- assert_equal ' Liquid error: argument error ', template.render('errors' => ErrorDrop.new)
59
+ assert_equal(' Liquid error: argument error ', template.render('errors' => ErrorDrop.new))
58
60
 
59
- assert_equal 1, template.errors.size
60
- assert_equal ArgumentError, template.errors.first.class
61
+ assert_equal(1, template.errors.size)
62
+ assert_equal(ArgumentError, template.errors.first.class)
61
63
  end
62
64
 
63
65
  def test_missing_endtag_parse_time_error
@@ -76,22 +78,21 @@ class ErrorHandlingTest < Minitest::Test
76
78
 
77
79
  def test_lax_unrecognized_operator
78
80
  template = Liquid::Template.parse(' {% if 1 =! 2 %}ok{% endif %} ', error_mode: :lax)
79
- assert_equal ' Liquid error: Unknown operator =! ', template.render
80
- assert_equal 1, template.errors.size
81
- assert_equal Liquid::ArgumentError, template.errors.first.class
81
+ assert_equal(' Liquid error: Unknown operator =! ', template.render)
82
+ assert_equal(1, template.errors.size)
83
+ assert_equal(Liquid::ArgumentError, template.errors.first.class)
82
84
  end
83
85
 
84
86
  def test_with_line_numbers_adds_numbers_to_parser_errors
85
87
  err = assert_raises(SyntaxError) do
86
- Liquid::Template.parse(%q(
88
+ Liquid::Template.parse('
87
89
  foobar
88
90
 
89
91
  {% "cat" | foobar %}
90
92
 
91
93
  bla
92
- ),
93
- line_numbers: true
94
- )
94
+ ',
95
+ line_numbers: true)
95
96
  end
96
97
 
97
98
  assert_match(/Liquid syntax error \(line 4\)/, err.message)
@@ -99,15 +100,14 @@ class ErrorHandlingTest < Minitest::Test
99
100
 
100
101
  def test_with_line_numbers_adds_numbers_to_parser_errors_with_whitespace_trim
101
102
  err = assert_raises(SyntaxError) do
102
- Liquid::Template.parse(%q(
103
+ Liquid::Template.parse('
103
104
  foobar
104
105
 
105
106
  {%- "cat" | foobar -%}
106
107
 
107
108
  bla
108
- ),
109
- line_numbers: true
110
- )
109
+ ',
110
+ line_numbers: true)
111
111
  end
112
112
 
113
113
  assert_match(/Liquid syntax error \(line 4\)/, err.message)
@@ -122,11 +122,10 @@ class ErrorHandlingTest < Minitest::Test
122
122
  bla
123
123
  ',
124
124
  error_mode: :warn,
125
- line_numbers: true
126
- )
125
+ line_numbers: true)
127
126
 
128
- assert_equal ['Liquid syntax error (line 4): Unexpected character = in "1 =! 2"'],
129
- template.warnings.map(&:message)
127
+ assert_equal(['Liquid syntax error (line 4): Unexpected character = in "1 =! 2"'],
128
+ template.warnings.map(&:message))
130
129
  end
131
130
 
132
131
  def test_parsing_strict_with_line_numbers_adds_numbers_to_lexer_errors
@@ -139,11 +138,10 @@ class ErrorHandlingTest < Minitest::Test
139
138
  bla
140
139
  ',
141
140
  error_mode: :strict,
142
- line_numbers: true
143
- )
141
+ line_numbers: true)
144
142
  end
145
143
 
146
- assert_equal 'Liquid syntax error (line 4): Unexpected character = in "1 =! 2"', err.message
144
+ assert_equal('Liquid syntax error (line 4): Unexpected character = in "1 =! 2"', err.message)
147
145
  end
148
146
 
149
147
  def test_syntax_errors_in_nested_blocks_have_correct_line_number
@@ -157,46 +155,45 @@ class ErrorHandlingTest < Minitest::Test
157
155
 
158
156
  bla
159
157
  ',
160
- line_numbers: true
161
- )
158
+ line_numbers: true)
162
159
  end
163
160
 
164
- assert_equal "Liquid syntax error (line 5): Unknown tag 'foo'", err.message
161
+ assert_equal("Liquid syntax error (line 5): Unknown tag 'foo'", err.message)
165
162
  end
166
163
 
167
164
  def test_strict_error_messages
168
165
  err = assert_raises(SyntaxError) do
169
166
  Liquid::Template.parse(' {% if 1 =! 2 %}ok{% endif %} ', error_mode: :strict)
170
167
  end
171
- assert_equal 'Liquid syntax error: Unexpected character = in "1 =! 2"', err.message
168
+ assert_equal('Liquid syntax error: Unexpected character = in "1 =! 2"', err.message)
172
169
 
173
170
  err = assert_raises(SyntaxError) do
174
171
  Liquid::Template.parse('{{%%%}}', error_mode: :strict)
175
172
  end
176
- assert_equal 'Liquid syntax error: Unexpected character % in "{{%%%}}"', err.message
173
+ assert_equal('Liquid syntax error: Unexpected character % in "{{%%%}}"', err.message)
177
174
  end
178
175
 
179
176
  def test_warnings
180
177
  template = Liquid::Template.parse('{% if ~~~ %}{{%%%}}{% else %}{{ hello. }}{% endif %}', error_mode: :warn)
181
- assert_equal 3, template.warnings.size
182
- assert_equal 'Unexpected character ~ in "~~~"', template.warnings[0].to_s(false)
183
- assert_equal 'Unexpected character % in "{{%%%}}"', template.warnings[1].to_s(false)
184
- assert_equal 'Expected id but found end_of_string in "{{ hello. }}"', template.warnings[2].to_s(false)
185
- assert_equal '', template.render
178
+ assert_equal(3, template.warnings.size)
179
+ assert_equal('Unexpected character ~ in "~~~"', template.warnings[0].to_s(false))
180
+ assert_equal('Unexpected character % in "{{%%%}}"', template.warnings[1].to_s(false))
181
+ assert_equal('Expected id but found end_of_string in "{{ hello. }}"', template.warnings[2].to_s(false))
182
+ assert_equal('', template.render)
186
183
  end
187
184
 
188
185
  def test_warning_line_numbers
189
186
  template = Liquid::Template.parse("{% if ~~~ %}\n{{%%%}}{% else %}\n{{ hello. }}{% endif %}", error_mode: :warn, line_numbers: true)
190
- assert_equal 'Liquid syntax error (line 1): Unexpected character ~ in "~~~"', template.warnings[0].message
191
- assert_equal 'Liquid syntax error (line 2): Unexpected character % in "{{%%%}}"', template.warnings[1].message
192
- assert_equal 'Liquid syntax error (line 3): Expected id but found end_of_string in "{{ hello. }}"', template.warnings[2].message
193
- assert_equal 3, template.warnings.size
194
- assert_equal [1, 2, 3], template.warnings.map(&:line_number)
187
+ assert_equal('Liquid syntax error (line 1): Unexpected character ~ in "~~~"', template.warnings[0].message)
188
+ assert_equal('Liquid syntax error (line 2): Unexpected character % in "{{%%%}}"', template.warnings[1].message)
189
+ assert_equal('Liquid syntax error (line 3): Expected id but found end_of_string in "{{ hello. }}"', template.warnings[2].message)
190
+ assert_equal(3, template.warnings.size)
191
+ assert_equal([1, 2, 3], template.warnings.map(&:line_number))
195
192
  end
196
193
 
197
194
  # Liquid should not catch Exceptions that are not subclasses of StandardError, like Interrupt and NoMemoryError
198
195
  def test_exceptions_propagate
199
- assert_raises Exception do
196
+ assert_raises(Exception) do
200
197
  template = Liquid::Template.parse('{{ errors.exception }}')
201
198
  template.render('errors' => ErrorDrop.new)
202
199
  end
@@ -205,41 +202,47 @@ class ErrorHandlingTest < Minitest::Test
205
202
  def test_default_exception_renderer_with_internal_error
206
203
  template = Liquid::Template.parse('This is a runtime error: {{ errors.runtime_error }}', line_numbers: true)
207
204
 
208
- output = template.render({ 'errors' => ErrorDrop.new })
205
+ output = template.render('errors' => ErrorDrop.new)
209
206
 
210
- assert_equal 'This is a runtime error: Liquid error (line 1): internal', output
211
- assert_equal [Liquid::InternalError], template.errors.map(&:class)
207
+ assert_equal('This is a runtime error: Liquid error (line 1): internal', output)
208
+ assert_equal([Liquid::InternalError], template.errors.map(&:class))
212
209
  end
213
210
 
214
211
  def test_setting_default_exception_renderer
215
212
  old_exception_renderer = Liquid::Template.default_exception_renderer
216
213
  exceptions = []
217
- Liquid::Template.default_exception_renderer = ->(e) { exceptions << e; '' }
214
+ Liquid::Template.default_exception_renderer = ->(e) {
215
+ exceptions << e
216
+ ''
217
+ }
218
218
  template = Liquid::Template.parse('This is a runtime error: {{ errors.argument_error }}')
219
219
 
220
- output = template.render({ 'errors' => ErrorDrop.new })
220
+ output = template.render('errors' => ErrorDrop.new)
221
221
 
222
- assert_equal 'This is a runtime error: ', output
223
- assert_equal [Liquid::ArgumentError], template.errors.map(&:class)
222
+ assert_equal('This is a runtime error: ', output)
223
+ assert_equal([Liquid::ArgumentError], template.errors.map(&:class))
224
224
  ensure
225
225
  Liquid::Template.default_exception_renderer = old_exception_renderer if old_exception_renderer
226
226
  end
227
227
 
228
228
  def test_exception_renderer_exposing_non_liquid_error
229
- template = Liquid::Template.parse('This is a runtime error: {{ errors.runtime_error }}', line_numbers: true)
229
+ template = Liquid::Template.parse('This is a runtime error: {{ errors.runtime_error }}', line_numbers: true)
230
230
  exceptions = []
231
- handler = ->(e) { exceptions << e; e.cause }
231
+ handler = ->(e) {
232
+ exceptions << e
233
+ e.cause
234
+ }
232
235
 
233
236
  output = template.render({ 'errors' => ErrorDrop.new }, exception_renderer: handler)
234
237
 
235
- assert_equal 'This is a runtime error: runtime error', output
236
- assert_equal [Liquid::InternalError], exceptions.map(&:class)
237
- assert_equal exceptions, template.errors
238
- assert_equal '#<RuntimeError: runtime error>', exceptions.first.cause.inspect
238
+ assert_equal('This is a runtime error: runtime error', output)
239
+ assert_equal([Liquid::InternalError], exceptions.map(&:class))
240
+ assert_equal(exceptions, template.errors)
241
+ assert_equal('#<RuntimeError: runtime error>', exceptions.first.cause.inspect)
239
242
  end
240
243
 
241
244
  class TestFileSystem
242
- def read_template_file(template_path)
245
+ def read_template_file(_template_path)
243
246
  "{{ errors.argument_error }}"
244
247
  end
245
248
  end
@@ -249,12 +252,21 @@ class ErrorHandlingTest < Minitest::Test
249
252
 
250
253
  begin
251
254
  Liquid::Template.file_system = TestFileSystem.new
255
+
252
256
  template = Liquid::Template.parse("Argument error:\n{% include 'product' %}", line_numbers: true)
253
- page = template.render('errors' => ErrorDrop.new)
257
+ page = template.render('errors' => ErrorDrop.new)
254
258
  ensure
255
259
  Liquid::Template.file_system = old_file_system
256
260
  end
257
- assert_equal "Argument error:\nLiquid error (product line 1): argument error", page
258
- assert_equal "product", template.errors.first.template_name
261
+ assert_equal("Argument error:\nLiquid error (product line 1): argument error", page)
262
+ assert_equal("product", template.errors.first.template_name)
263
+ end
264
+
265
+ def test_bug_compatible_silencing_of_errors_in_blank_nodes
266
+ output = Liquid::Template.parse("{% assign x = 0 %}{% if 1 < '2' %}not blank{% assign x = 3 %}{% endif %}{{ x }}").render
267
+ assert_equal("Liquid error: comparison of Integer with String failed0", output)
268
+
269
+ output = Liquid::Template.parse("{% assign x = 0 %}{% if 1 < '2' %}{% assign x = 3 %}{% endif %}{{ x }}").render
270
+ assert_equal("0", output)
259
271
  end
260
272
  end