liquid 4.0.4 → 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 (123) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +32 -4
  3. data/README.md +6 -0
  4. data/lib/liquid/block.rb +31 -14
  5. data/lib/liquid/block_body.rb +164 -54
  6. data/lib/liquid/condition.rb +39 -18
  7. data/lib/liquid/context.rb +106 -51
  8. data/lib/liquid/document.rb +47 -9
  9. data/lib/liquid/drop.rb +4 -2
  10. data/lib/liquid/errors.rb +20 -18
  11. data/lib/liquid/expression.rb +29 -34
  12. data/lib/liquid/extensions.rb +2 -0
  13. data/lib/liquid/file_system.rb +6 -4
  14. data/lib/liquid/forloop_drop.rb +11 -4
  15. data/lib/liquid/i18n.rb +5 -3
  16. data/lib/liquid/interrupts.rb +3 -1
  17. data/lib/liquid/lexer.rb +30 -23
  18. data/lib/liquid/locales/en.yml +3 -1
  19. data/lib/liquid/parse_context.rb +16 -4
  20. data/lib/liquid/parse_tree_visitor.rb +2 -2
  21. data/lib/liquid/parser.rb +30 -18
  22. data/lib/liquid/parser_switching.rb +17 -3
  23. data/lib/liquid/partial_cache.rb +24 -0
  24. data/lib/liquid/profiler/hooks.rb +26 -14
  25. data/lib/liquid/profiler.rb +67 -86
  26. data/lib/liquid/range_lookup.rb +5 -3
  27. data/lib/liquid/register.rb +6 -0
  28. data/lib/liquid/resource_limits.rb +47 -8
  29. data/lib/liquid/standardfilters.rb +62 -43
  30. data/lib/liquid/static_registers.rb +44 -0
  31. data/lib/liquid/strainer_factory.rb +36 -0
  32. data/lib/liquid/strainer_template.rb +53 -0
  33. data/lib/liquid/tablerowloop_drop.rb +6 -4
  34. data/lib/liquid/tag/disableable.rb +22 -0
  35. data/lib/liquid/tag/disabler.rb +21 -0
  36. data/lib/liquid/tag.rb +28 -6
  37. data/lib/liquid/tags/assign.rb +24 -10
  38. data/lib/liquid/tags/break.rb +8 -3
  39. data/lib/liquid/tags/capture.rb +11 -8
  40. data/lib/liquid/tags/case.rb +33 -27
  41. data/lib/liquid/tags/comment.rb +5 -3
  42. data/lib/liquid/tags/continue.rb +8 -3
  43. data/lib/liquid/tags/cycle.rb +25 -14
  44. data/lib/liquid/tags/decrement.rb +6 -3
  45. data/lib/liquid/tags/echo.rb +26 -0
  46. data/lib/liquid/tags/for.rb +68 -44
  47. data/lib/liquid/tags/if.rb +35 -23
  48. data/lib/liquid/tags/ifchanged.rb +11 -10
  49. data/lib/liquid/tags/include.rb +34 -47
  50. data/lib/liquid/tags/increment.rb +7 -3
  51. data/lib/liquid/tags/raw.rb +14 -11
  52. data/lib/liquid/tags/render.rb +84 -0
  53. data/lib/liquid/tags/table_row.rb +23 -19
  54. data/lib/liquid/tags/unless.rb +15 -15
  55. data/lib/liquid/template.rb +55 -69
  56. data/lib/liquid/template_factory.rb +9 -0
  57. data/lib/liquid/tokenizer.rb +17 -9
  58. data/lib/liquid/usage.rb +8 -0
  59. data/lib/liquid/utils.rb +5 -3
  60. data/lib/liquid/variable.rb +47 -19
  61. data/lib/liquid/variable_lookup.rb +8 -6
  62. data/lib/liquid/version.rb +2 -1
  63. data/lib/liquid.rb +17 -5
  64. data/test/integration/assign_test.rb +74 -5
  65. data/test/integration/blank_test.rb +11 -8
  66. data/test/integration/block_test.rb +47 -1
  67. data/test/integration/capture_test.rb +18 -10
  68. data/test/integration/context_test.rb +608 -5
  69. data/test/integration/document_test.rb +4 -2
  70. data/test/integration/drop_test.rb +67 -57
  71. data/test/integration/error_handling_test.rb +73 -61
  72. data/test/integration/expression_test.rb +46 -0
  73. data/test/integration/filter_test.rb +53 -42
  74. data/test/integration/hash_ordering_test.rb +5 -3
  75. data/test/integration/output_test.rb +26 -24
  76. data/test/integration/parsing_quirks_test.rb +19 -7
  77. data/test/integration/{render_profiling_test.rb → profiler_test.rb} +84 -25
  78. data/test/integration/security_test.rb +30 -21
  79. data/test/integration/standard_filter_test.rb +339 -281
  80. data/test/integration/tag/disableable_test.rb +59 -0
  81. data/test/integration/tag_test.rb +45 -0
  82. data/test/integration/tags/break_tag_test.rb +4 -2
  83. data/test/integration/tags/continue_tag_test.rb +4 -2
  84. data/test/integration/tags/echo_test.rb +13 -0
  85. data/test/integration/tags/for_tag_test.rb +107 -51
  86. data/test/integration/tags/if_else_tag_test.rb +5 -3
  87. data/test/integration/tags/include_tag_test.rb +70 -54
  88. data/test/integration/tags/increment_tag_test.rb +4 -2
  89. data/test/integration/tags/liquid_tag_test.rb +116 -0
  90. data/test/integration/tags/raw_tag_test.rb +14 -11
  91. data/test/integration/tags/render_tag_test.rb +213 -0
  92. data/test/integration/tags/standard_tag_test.rb +38 -31
  93. data/test/integration/tags/statements_test.rb +23 -21
  94. data/test/integration/tags/table_row_test.rb +2 -0
  95. data/test/integration/tags/unless_else_tag_test.rb +4 -2
  96. data/test/integration/template_test.rb +118 -124
  97. data/test/integration/trim_mode_test.rb +78 -44
  98. data/test/integration/variable_test.rb +43 -32
  99. data/test/test_helper.rb +75 -14
  100. data/test/unit/block_unit_test.rb +19 -24
  101. data/test/unit/condition_unit_test.rb +79 -77
  102. data/test/unit/file_system_unit_test.rb +6 -4
  103. data/test/unit/i18n_unit_test.rb +7 -5
  104. data/test/unit/lexer_unit_test.rb +11 -9
  105. data/test/{integration → unit}/parse_tree_visitor_test.rb +1 -1
  106. data/test/unit/parser_unit_test.rb +37 -35
  107. data/test/unit/partial_cache_unit_test.rb +128 -0
  108. data/test/unit/regexp_unit_test.rb +17 -15
  109. data/test/unit/static_registers_unit_test.rb +156 -0
  110. data/test/unit/strainer_factory_unit_test.rb +100 -0
  111. data/test/unit/strainer_template_unit_test.rb +82 -0
  112. data/test/unit/tag_unit_test.rb +5 -3
  113. data/test/unit/tags/case_tag_unit_test.rb +3 -1
  114. data/test/unit/tags/for_tag_unit_test.rb +4 -2
  115. data/test/unit/tags/if_tag_unit_test.rb +3 -1
  116. data/test/unit/template_factory_unit_test.rb +12 -0
  117. data/test/unit/template_unit_test.rb +19 -10
  118. data/test/unit/tokenizer_unit_test.rb +19 -17
  119. data/test/unit/variable_unit_test.rb +51 -49
  120. metadata +75 -47
  121. data/lib/liquid/strainer.rb +0 -66
  122. data/test/unit/context_unit_test.rb +0 -490
  123. data/test/unit/strainer_unit_test.rb +0 -164
@@ -1,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"
52
- end
53
-
54
52
  protected
55
53
 
56
54
  def callmenot
@@ -109,139 +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)
110
+ assert_equal(' ', tpl.render!('product' => ProductDrop.new))
113
111
  end
114
112
 
115
113
  def test_drop_does_only_respond_to_whitelisted_methods
116
- assert_equal "", Liquid::Template.parse("{{ product.inspect }}").render!('product' => ProductDrop.new)
117
- assert_equal "", Liquid::Template.parse("{{ product.pretty_inspect }}").render!('product' => ProductDrop.new)
118
- assert_equal "", Liquid::Template.parse("{{ product.whatever }}").render!('product' => ProductDrop.new)
119
- assert_equal "", Liquid::Template.parse('{{ product | map: "inspect" }}').render!('product' => ProductDrop.new)
120
- assert_equal "", Liquid::Template.parse('{{ product | map: "pretty_inspect" }}').render!('product' => ProductDrop.new)
121
- 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))
122
120
  end
123
121
 
124
122
  def test_drops_respond_to_to_liquid
125
- assert_equal "text1", Liquid::Template.parse("{{ product.to_liquid.texts.text }}").render!('product' => ProductDrop.new)
126
- 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))
127
125
  end
128
126
 
129
127
  def test_text_drop
130
128
  output = Liquid::Template.parse(' {{ product.texts.text }} ').render!('product' => ProductDrop.new)
131
- assert_equal ' text1 ', output
129
+ assert_equal(' text1 ', output)
132
130
  end
133
131
 
134
132
  def test_catchall_unknown_method
135
133
  output = Liquid::Template.parse(' {{ product.catchall.unknown }} ').render!('product' => ProductDrop.new)
136
- assert_equal ' catchall_method: unknown ', output
134
+ assert_equal(' catchall_method: unknown ', output)
137
135
  end
138
136
 
139
137
  def test_catchall_integer_argument_drop
140
138
  output = Liquid::Template.parse(' {{ product.catchall[8] }} ').render!('product' => ProductDrop.new)
141
- assert_equal ' catchall_method: 8 ', output
139
+ assert_equal(' catchall_method: 8 ', output)
142
140
  end
143
141
 
144
142
  def test_text_array_drop
145
143
  output = Liquid::Template.parse('{% for text in product.texts.array %} {{text}} {% endfor %}').render!('product' => ProductDrop.new)
146
- assert_equal ' text1 text2 ', output
144
+ assert_equal(' text1 text2 ', output)
147
145
  end
148
146
 
149
147
  def test_context_drop
150
148
  output = Liquid::Template.parse(' {{ context.bar }} ').render!('context' => ContextDrop.new, 'bar' => "carrot")
151
- 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)
152
155
  end
153
156
 
154
157
  def test_nested_context_drop
155
158
  output = Liquid::Template.parse(' {{ product.context.foo }} ').render!('product' => ProductDrop.new, 'foo' => "monkey")
156
- assert_equal ' monkey ', output
159
+ assert_equal(' monkey ', output)
157
160
  end
158
161
 
159
162
  def test_protected
160
163
  output = Liquid::Template.parse(' {{ product.callmenot }} ').render!('product' => ProductDrop.new)
161
- assert_equal ' ', output
164
+ assert_equal(' ', output)
162
165
  end
163
166
 
164
167
  def test_object_methods_not_allowed
165
168
  [:dup, :clone, :singleton_class, :eval, :class_eval, :inspect].each do |method|
166
169
  output = Liquid::Template.parse(" {{ product.#{method} }} ").render!('product' => ProductDrop.new)
167
- assert_equal ' ', output
170
+ assert_equal(' ', output)
168
171
  end
169
172
  end
170
173
 
171
174
  def test_scope
172
- assert_equal '1', Liquid::Template.parse('{{ context.scopes }}').render!('context' => ContextDrop.new)
173
- assert_equal '2', Liquid::Template.parse('{%for i in dummy%}{{ context.scopes }}{%endfor%}').render!('context' => ContextDrop.new, 'dummy' => [1])
174
- 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]))
175
178
  end
176
179
 
177
180
  def test_scope_though_proc
178
- assert_equal '1', Liquid::Template.parse('{{ s }}').render!('context' => ContextDrop.new, 's' => proc{ |c| c['context.scopes'] })
179
- assert_equal '2', Liquid::Template.parse('{%for i in dummy%}{{ s }}{%endfor%}').render!('context' => ContextDrop.new, 's' => proc{ |c| c['context.scopes'] }, 'dummy' => [1])
180
- 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]))
181
184
  end
182
185
 
183
186
  def test_scope_with_assigns
184
- assert_equal 'variable', Liquid::Template.parse('{% assign a = "variable"%}{{a}}').render!('context' => ContextDrop.new)
185
- assert_equal 'variable', Liquid::Template.parse('{% assign a = "variable"%}{%for i in dummy%}{{a}}{%endfor%}').render!('context' => ContextDrop.new, 'dummy' => [1])
186
- assert_equal 'test', Liquid::Template.parse('{% assign header_gif = "test"%}{{header_gif}}').render!('context' => ContextDrop.new)
187
- 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))
188
191
  end
189
192
 
190
193
  def test_scope_from_tags
191
- assert_equal '1', Liquid::Template.parse('{% for i in context.scopes_as_array %}{{i}}{% endfor %}').render!('context' => ContextDrop.new, 'dummy' => [1])
192
- 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])
193
- 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]))
194
197
  end
195
198
 
196
199
  def test_access_context_from_drop
197
- 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]))
198
201
  end
199
202
 
200
203
  def test_enumerable_drop
201
- 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))
202
205
  end
203
206
 
204
207
  def test_enumerable_drop_size
205
- 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))
206
209
  end
207
210
 
208
211
  def test_enumerable_drop_will_invoke_liquid_method_missing_for_clashing_method_names
209
212
  ["select", "each", "map", "cycle"].each do |method|
210
- assert_equal method.to_s, Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => EnumerableDrop.new)
211
- assert_equal method.to_s, Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => EnumerableDrop.new)
212
- 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' => 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))
214
217
  end
215
218
  end
216
219
 
217
220
  def test_some_enumerable_methods_still_get_invoked
218
- [ :count, :max ].each do |method|
219
- assert_equal "3", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => RealEnumerableDrop.new)
220
- assert_equal "3", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => RealEnumerableDrop.new)
221
- assert_equal "3", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => EnumerableDrop.new)
222
- 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))
223
226
  end
224
227
 
225
- 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))
226
229
 
227
- [ :min, :first ].each do |method|
228
- assert_equal "1", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => RealEnumerableDrop.new)
229
- assert_equal "1", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => RealEnumerableDrop.new)
230
- assert_equal "1", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => EnumerableDrop.new)
231
- 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))
232
235
  end
233
236
  end
234
237
 
235
238
  def test_empty_string_value_access
236
- 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' => ''))
237
240
  end
238
241
 
239
242
  def test_nil_value_access
240
- 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))
241
244
  end
242
245
 
243
246
  def test_default_to_s_on_drops
244
- assert_equal 'ProductDrop', Liquid::Template.parse("{{ product }}").render!('product' => ProductDrop.new)
245
- 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)
246
256
  end
247
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
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ class ExpressionTest < Minitest::Test
6
+ def test_keyword_literals
7
+ assert_equal(true, parse_and_eval("true"))
8
+ assert_equal(true, parse_and_eval(" true "))
9
+ end
10
+
11
+ def test_string
12
+ assert_equal("single quoted", parse_and_eval("'single quoted'"))
13
+ assert_equal("double quoted", parse_and_eval('"double quoted"'))
14
+ assert_equal("spaced", parse_and_eval(" 'spaced' "))
15
+ assert_equal("spaced2", parse_and_eval(' "spaced2" '))
16
+ end
17
+
18
+ def test_int
19
+ assert_equal(123, parse_and_eval("123"))
20
+ assert_equal(456, parse_and_eval(" 456 "))
21
+ assert_equal(12, parse_and_eval("012"))
22
+ end
23
+
24
+ def test_float
25
+ assert_equal(1.5, parse_and_eval("1.5"))
26
+ assert_equal(2.5, parse_and_eval(" 2.5 "))
27
+ end
28
+
29
+ def test_range
30
+ assert_equal(1..2, parse_and_eval("(1..2)"))
31
+ assert_equal(3..4, parse_and_eval(" ( 3 .. 4 ) "))
32
+ end
33
+
34
+ private
35
+
36
+ def parse_and_eval(markup, **assigns)
37
+ if Liquid::Template.error_mode == :strict
38
+ p = Liquid::Parser.new(markup)
39
+ markup = p.expression
40
+ p.consume(:end_of_string)
41
+ end
42
+ expression = Liquid::Expression.parse(markup)
43
+ context = Liquid::Context.new(assigns)
44
+ context.evaluate(expression)
45
+ end
46
+ end