liquid 2.6.3 → 3.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 (102) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +46 -13
  3. data/README.md +27 -2
  4. data/lib/liquid/block.rb +85 -51
  5. data/lib/liquid/block_body.rb +123 -0
  6. data/lib/liquid/condition.rb +26 -15
  7. data/lib/liquid/context.rb +106 -140
  8. data/lib/liquid/document.rb +3 -3
  9. data/lib/liquid/drop.rb +17 -1
  10. data/lib/liquid/errors.rb +50 -2
  11. data/lib/liquid/expression.rb +33 -0
  12. data/lib/liquid/file_system.rb +17 -6
  13. data/lib/liquid/i18n.rb +39 -0
  14. data/lib/liquid/interrupts.rb +1 -1
  15. data/lib/liquid/lexer.rb +51 -0
  16. data/lib/liquid/locales/en.yml +22 -0
  17. data/lib/liquid/parser.rb +90 -0
  18. data/lib/liquid/parser_switching.rb +31 -0
  19. data/lib/liquid/profiler/hooks.rb +23 -0
  20. data/lib/liquid/profiler.rb +159 -0
  21. data/lib/liquid/range_lookup.rb +22 -0
  22. data/lib/liquid/standardfilters.rb +143 -55
  23. data/lib/liquid/strainer.rb +14 -4
  24. data/lib/liquid/tag.rb +25 -9
  25. data/lib/liquid/tags/assign.rb +12 -9
  26. data/lib/liquid/tags/break.rb +1 -1
  27. data/lib/liquid/tags/capture.rb +10 -8
  28. data/lib/liquid/tags/case.rb +13 -13
  29. data/lib/liquid/tags/comment.rb +9 -2
  30. data/lib/liquid/tags/continue.rb +1 -4
  31. data/lib/liquid/tags/cycle.rb +5 -7
  32. data/lib/liquid/tags/decrement.rb +3 -4
  33. data/lib/liquid/tags/for.rb +69 -36
  34. data/lib/liquid/tags/if.rb +52 -25
  35. data/lib/liquid/tags/ifchanged.rb +3 -3
  36. data/lib/liquid/tags/include.rb +19 -8
  37. data/lib/liquid/tags/increment.rb +4 -8
  38. data/lib/liquid/tags/raw.rb +4 -7
  39. data/lib/liquid/tags/table_row.rb +73 -0
  40. data/lib/liquid/tags/unless.rb +2 -4
  41. data/lib/liquid/template.rb +124 -14
  42. data/lib/liquid/token.rb +18 -0
  43. data/lib/liquid/utils.rb +13 -4
  44. data/lib/liquid/variable.rb +103 -25
  45. data/lib/liquid/variable_lookup.rb +78 -0
  46. data/lib/liquid/version.rb +1 -1
  47. data/lib/liquid.rb +19 -11
  48. data/test/fixtures/en_locale.yml +9 -0
  49. data/test/{liquid → integration}/assign_test.rb +18 -1
  50. data/test/integration/blank_test.rb +106 -0
  51. data/test/{liquid → integration}/capture_test.rb +3 -3
  52. data/test/integration/context_test.rb +32 -0
  53. data/test/integration/drop_test.rb +271 -0
  54. data/test/integration/error_handling_test.rb +207 -0
  55. data/test/{liquid → integration}/filter_test.rb +11 -11
  56. data/test/integration/hash_ordering_test.rb +23 -0
  57. data/test/{liquid → integration}/output_test.rb +13 -13
  58. data/test/integration/parsing_quirks_test.rb +116 -0
  59. data/test/integration/render_profiling_test.rb +154 -0
  60. data/test/{liquid → integration}/security_test.rb +10 -10
  61. data/test/{liquid → integration}/standard_filter_test.rb +148 -32
  62. data/test/{liquid → integration}/tags/break_tag_test.rb +1 -1
  63. data/test/{liquid → integration}/tags/continue_tag_test.rb +1 -1
  64. data/test/{liquid → integration}/tags/for_tag_test.rb +80 -2
  65. data/test/{liquid → integration}/tags/if_else_tag_test.rb +24 -21
  66. data/test/integration/tags/include_tag_test.rb +234 -0
  67. data/test/{liquid → integration}/tags/increment_tag_test.rb +1 -1
  68. data/test/{liquid → integration}/tags/raw_tag_test.rb +2 -1
  69. data/test/{liquid → integration}/tags/standard_tag_test.rb +28 -26
  70. data/test/integration/tags/statements_test.rb +113 -0
  71. data/test/{liquid/tags/html_tag_test.rb → integration/tags/table_row_test.rb} +5 -5
  72. data/test/{liquid → integration}/tags/unless_else_tag_test.rb +1 -1
  73. data/test/{liquid → integration}/template_test.rb +81 -45
  74. data/test/integration/variable_test.rb +82 -0
  75. data/test/test_helper.rb +73 -20
  76. data/test/{liquid/block_test.rb → unit/block_unit_test.rb} +2 -5
  77. data/test/{liquid/condition_test.rb → unit/condition_unit_test.rb} +23 -1
  78. data/test/{liquid/context_test.rb → unit/context_unit_test.rb} +39 -25
  79. data/test/{liquid/file_system_test.rb → unit/file_system_unit_test.rb} +11 -5
  80. data/test/unit/i18n_unit_test.rb +37 -0
  81. data/test/unit/lexer_unit_test.rb +48 -0
  82. data/test/{liquid/module_ex_test.rb → unit/module_ex_unit_test.rb} +7 -7
  83. data/test/unit/parser_unit_test.rb +82 -0
  84. data/test/{liquid/regexp_test.rb → unit/regexp_unit_test.rb} +3 -3
  85. data/test/{liquid/strainer_test.rb → unit/strainer_unit_test.rb} +20 -1
  86. data/test/unit/tag_unit_test.rb +16 -0
  87. data/test/unit/tags/case_tag_unit_test.rb +10 -0
  88. data/test/unit/tags/for_tag_unit_test.rb +13 -0
  89. data/test/unit/tags/if_tag_unit_test.rb +8 -0
  90. data/test/unit/template_unit_test.rb +69 -0
  91. data/test/unit/tokenizer_unit_test.rb +38 -0
  92. data/test/unit/variable_unit_test.rb +139 -0
  93. metadata +135 -67
  94. data/lib/extras/liquid_view.rb +0 -51
  95. data/lib/liquid/htmltags.rb +0 -73
  96. data/test/liquid/drop_test.rb +0 -180
  97. data/test/liquid/error_handling_test.rb +0 -81
  98. data/test/liquid/hash_ordering_test.rb +0 -25
  99. data/test/liquid/parsing_quirks_test.rb +0 -52
  100. data/test/liquid/tags/include_tag_test.rb +0 -166
  101. data/test/liquid/tags/statements_test.rb +0 -134
  102. data/test/liquid/variable_test.rb +0 -186
@@ -0,0 +1,207 @@
1
+ require 'test_helper'
2
+
3
+ class ErrorDrop < Liquid::Drop
4
+ def standard_error
5
+ raise Liquid::StandardError, 'standard error'
6
+ end
7
+
8
+ def argument_error
9
+ raise Liquid::ArgumentError, 'argument error'
10
+ end
11
+
12
+ def syntax_error
13
+ raise Liquid::SyntaxError, 'syntax error'
14
+ end
15
+
16
+ def exception
17
+ raise Exception, 'exception'
18
+ end
19
+
20
+ end
21
+
22
+ class ErrorHandlingTest < Minitest::Test
23
+ include Liquid
24
+
25
+ def test_templates_parsed_with_line_numbers_renders_them_in_errors
26
+ template = <<-LIQUID
27
+ Hello,
28
+
29
+ {{ errors.standard_error }} will raise a standard error.
30
+
31
+ Bla bla test.
32
+
33
+ {{ errors.syntax_error }} will raise a syntax error.
34
+
35
+ This is an argument error: {{ errors.argument_error }}
36
+
37
+ Bla.
38
+ LIQUID
39
+
40
+ expected = <<-TEXT
41
+ Hello,
42
+
43
+ Liquid error (line 3): standard error will raise a standard error.
44
+
45
+ Bla bla test.
46
+
47
+ Liquid syntax error (line 7): syntax error will raise a syntax error.
48
+
49
+ This is an argument error: Liquid error (line 9): argument error
50
+
51
+ Bla.
52
+ TEXT
53
+
54
+ output = Liquid::Template.parse(template, line_numbers: true).render('errors' => ErrorDrop.new)
55
+ assert_equal expected, output
56
+ end
57
+
58
+ def test_standard_error
59
+ template = Liquid::Template.parse( ' {{ errors.standard_error }} ' )
60
+ assert_equal ' Liquid error: standard error ', template.render('errors' => ErrorDrop.new)
61
+
62
+ assert_equal 1, template.errors.size
63
+ assert_equal StandardError, template.errors.first.class
64
+ end
65
+
66
+ def test_syntax
67
+ template = Liquid::Template.parse( ' {{ errors.syntax_error }} ' )
68
+ assert_equal ' Liquid syntax error: syntax error ', template.render('errors' => ErrorDrop.new)
69
+
70
+ assert_equal 1, template.errors.size
71
+ assert_equal SyntaxError, template.errors.first.class
72
+ end
73
+
74
+ def test_argument
75
+ template = Liquid::Template.parse( ' {{ errors.argument_error }} ' )
76
+ assert_equal ' Liquid error: argument error ', template.render('errors' => ErrorDrop.new)
77
+
78
+ assert_equal 1, template.errors.size
79
+ assert_equal ArgumentError, template.errors.first.class
80
+ end
81
+
82
+ def test_missing_endtag_parse_time_error
83
+ assert_raises(Liquid::SyntaxError) do
84
+ Liquid::Template.parse(' {% for a in b %} ... ')
85
+ end
86
+ end
87
+
88
+ def test_unrecognized_operator
89
+ with_error_mode(:strict) do
90
+ assert_raises(SyntaxError) do
91
+ Liquid::Template.parse(' {% if 1 =! 2 %}ok{% endif %} ')
92
+ end
93
+ end
94
+ end
95
+
96
+ def test_lax_unrecognized_operator
97
+ template = Liquid::Template.parse(' {% if 1 =! 2 %}ok{% endif %} ', :error_mode => :lax)
98
+ assert_equal ' Liquid error: Unknown operator =! ', template.render
99
+ assert_equal 1, template.errors.size
100
+ assert_equal Liquid::ArgumentError, template.errors.first.class
101
+ end
102
+
103
+ def test_with_line_numbers_adds_numbers_to_parser_errors
104
+ err = assert_raises(SyntaxError) do
105
+ template = Liquid::Template.parse(%q{
106
+ foobar
107
+
108
+ {% "cat" | foobar %}
109
+
110
+ bla
111
+ },
112
+ :line_numbers => true
113
+ )
114
+ end
115
+
116
+ assert_match /Liquid syntax error \(line 4\)/, err.message
117
+ end
118
+
119
+ def test_parsing_warn_with_line_numbers_adds_numbers_to_lexer_errors
120
+ template = Liquid::Template.parse(%q{
121
+ foobar
122
+
123
+ {% if 1 =! 2 %}ok{% endif %}
124
+
125
+ bla
126
+ },
127
+ :error_mode => :warn,
128
+ :line_numbers => true
129
+ )
130
+
131
+ assert_equal ['Liquid syntax error (line 4): Unexpected character = in "1 =! 2"'],
132
+ template.warnings.map(&:message)
133
+ end
134
+
135
+ def test_parsing_strict_with_line_numbers_adds_numbers_to_lexer_errors
136
+ err = assert_raises(SyntaxError) do
137
+ Liquid::Template.parse(%q{
138
+ foobar
139
+
140
+ {% if 1 =! 2 %}ok{% endif %}
141
+
142
+ bla
143
+ },
144
+ :error_mode => :strict,
145
+ :line_numbers => true
146
+ )
147
+ end
148
+
149
+ assert_equal 'Liquid syntax error (line 4): Unexpected character = in "1 =! 2"', err.message
150
+ end
151
+
152
+ def test_syntax_errors_in_nested_blocks_have_correct_line_number
153
+ err = assert_raises(SyntaxError) do
154
+ Liquid::Template.parse(%q{
155
+ foobar
156
+
157
+ {% if 1 != 2 %}
158
+ {% foo %}
159
+ {% endif %}
160
+
161
+ bla
162
+ },
163
+ :line_numbers => true
164
+ )
165
+ end
166
+
167
+ assert_equal "Liquid syntax error (line 5): Unknown tag 'foo'", err.message
168
+ end
169
+
170
+ def test_strict_error_messages
171
+ err = assert_raises(SyntaxError) do
172
+ Liquid::Template.parse(' {% if 1 =! 2 %}ok{% endif %} ', :error_mode => :strict)
173
+ end
174
+ assert_equal 'Liquid syntax error: Unexpected character = in "1 =! 2"', err.message
175
+
176
+ err = assert_raises(SyntaxError) do
177
+ Liquid::Template.parse('{{%%%}}', :error_mode => :strict)
178
+ end
179
+ assert_equal 'Liquid syntax error: Unexpected character % in "{{%%%}}"', err.message
180
+ end
181
+
182
+ def test_warnings
183
+ template = Liquid::Template.parse('{% if ~~~ %}{{%%%}}{% else %}{{ hello. }}{% endif %}', :error_mode => :warn)
184
+ assert_equal 3, template.warnings.size
185
+ assert_equal 'Unexpected character ~ in "~~~"', template.warnings[0].to_s(false)
186
+ assert_equal 'Unexpected character % in "{{%%%}}"', template.warnings[1].to_s(false)
187
+ assert_equal 'Expected id but found end_of_string in "{{ hello. }}"', template.warnings[2].to_s(false)
188
+ assert_equal '', template.render
189
+ end
190
+
191
+ def test_warning_line_numbers
192
+ template = Liquid::Template.parse("{% if ~~~ %}\n{{%%%}}{% else %}\n{{ hello. }}{% endif %}", :error_mode => :warn, :line_numbers => true)
193
+ assert_equal 'Liquid syntax error (line 1): Unexpected character ~ in "~~~"', template.warnings[0].message
194
+ assert_equal 'Liquid syntax error (line 2): Unexpected character % in "{{%%%}}"', template.warnings[1].message
195
+ assert_equal 'Liquid syntax error (line 3): Expected id but found end_of_string in "{{ hello. }}"', template.warnings[2].message
196
+ assert_equal 3, template.warnings.size
197
+ assert_equal [1,2,3], template.warnings.map(&:line_number)
198
+ end
199
+
200
+ # Liquid should not catch Exceptions that are not subclasses of StandardError, like Interrupt and NoMemoryError
201
+ def test_exceptions_propagate
202
+ assert_raises Exception do
203
+ template = Liquid::Template.parse('{{ errors.exception }}')
204
+ template.render('errors' => ErrorDrop.new)
205
+ end
206
+ end
207
+ end
@@ -22,7 +22,7 @@ module SubstituteFilter
22
22
  end
23
23
  end
24
24
 
25
- class FiltersTest < Test::Unit::TestCase
25
+ class FiltersTest < Minitest::Test
26
26
  include Liquid
27
27
 
28
28
  def setup
@@ -67,12 +67,12 @@ class FiltersTest < Test::Unit::TestCase
67
67
  @context['value'] = 3
68
68
  @context['numbers'] = [2,1,4,3]
69
69
  @context['words'] = ['expected', 'as', 'alphabetic']
70
- @context['arrays'] = [['flattened'], ['are']]
70
+ @context['arrays'] = ['flower', 'are']
71
71
 
72
72
  assert_equal [1,2,3,4], Variable.new("numbers | sort").render(@context)
73
73
  assert_equal ['alphabetic', 'as', 'expected'], Variable.new("words | sort").render(@context)
74
74
  assert_equal [3], Variable.new("value | sort").render(@context)
75
- assert_equal ['are', 'flattened'], Variable.new("arrays | sort").render(@context)
75
+ assert_equal ['are', 'flower'], Variable.new("arrays | sort").render(@context)
76
76
  end
77
77
 
78
78
  def test_strip_html
@@ -107,19 +107,19 @@ class FiltersTest < Test::Unit::TestCase
107
107
  end
108
108
  end
109
109
 
110
- class FiltersInTemplate < Test::Unit::TestCase
110
+ class FiltersInTemplate < Minitest::Test
111
111
  include Liquid
112
112
 
113
113
  def test_local_global
114
- Template.register_filter(MoneyFilter)
115
-
116
- assert_equal " 1000$ ", Template.parse("{{1000 | money}}").render(nil, nil)
117
- assert_equal " 1000$ CAD ", Template.parse("{{1000 | money}}").render(nil, :filters => CanadianMoneyFilter)
118
- assert_equal " 1000$ CAD ", Template.parse("{{1000 | money}}").render(nil, :filters => [CanadianMoneyFilter])
114
+ with_global_filter(MoneyFilter) do
115
+ assert_equal " 1000$ ", Template.parse("{{1000 | money}}").render!(nil, nil)
116
+ assert_equal " 1000$ CAD ", Template.parse("{{1000 | money}}").render!(nil, :filters => CanadianMoneyFilter)
117
+ assert_equal " 1000$ CAD ", Template.parse("{{1000 | money}}").render!(nil, :filters => [CanadianMoneyFilter])
118
+ end
119
119
  end
120
120
 
121
121
  def test_local_filter_with_deprecated_syntax
122
- assert_equal " 1000$ CAD ", Template.parse("{{1000 | money}}").render(nil, CanadianMoneyFilter)
123
- assert_equal " 1000$ CAD ", Template.parse("{{1000 | money}}").render(nil, [CanadianMoneyFilter])
122
+ assert_equal " 1000$ CAD ", Template.parse("{{1000 | money}}").render!(nil, CanadianMoneyFilter)
123
+ assert_equal " 1000$ CAD ", Template.parse("{{1000 | money}}").render!(nil, [CanadianMoneyFilter])
124
124
  end
125
125
  end # FiltersTest
@@ -0,0 +1,23 @@
1
+ require 'test_helper'
2
+
3
+ module MoneyFilter
4
+ def money(input)
5
+ sprintf(' %d$ ', input)
6
+ end
7
+ end
8
+
9
+ module CanadianMoneyFilter
10
+ def money(input)
11
+ sprintf(' %d$ CAD ', input)
12
+ end
13
+ end
14
+
15
+ class HashOrderingTest < Minitest::Test
16
+ include Liquid
17
+
18
+ def test_global_register_order
19
+ with_global_filter(MoneyFilter, CanadianMoneyFilter) do
20
+ assert_equal " 1000$ CAD ", Template.parse("{{1000 | money}}").render(nil, nil)
21
+ end
22
+ end
23
+ end
@@ -27,7 +27,7 @@ module FunnyFilter
27
27
 
28
28
  end
29
29
 
30
- class OutputTest < Test::Unit::TestCase
30
+ class OutputTest < Minitest::Test
31
31
  include Liquid
32
32
 
33
33
  def setup
@@ -41,76 +41,76 @@ class OutputTest < Test::Unit::TestCase
41
41
  text = %| {{best_cars}} |
42
42
 
43
43
  expected = %| bmw |
44
- assert_equal expected, Template.parse(text).render(@assigns)
44
+ assert_equal expected, Template.parse(text).render!(@assigns)
45
45
  end
46
46
 
47
47
  def test_variable_traversing
48
48
  text = %| {{car.bmw}} {{car.gm}} {{car.bmw}} |
49
49
 
50
50
  expected = %| good bad good |
51
- assert_equal expected, Template.parse(text).render(@assigns)
51
+ assert_equal expected, Template.parse(text).render!(@assigns)
52
52
  end
53
53
 
54
54
  def test_variable_piping
55
55
  text = %( {{ car.gm | make_funny }} )
56
56
  expected = %| LOL |
57
57
 
58
- assert_equal expected, Template.parse(text).render(@assigns, :filters => [FunnyFilter])
58
+ assert_equal expected, Template.parse(text).render!(@assigns, :filters => [FunnyFilter])
59
59
  end
60
60
 
61
61
  def test_variable_piping_with_input
62
62
  text = %( {{ car.gm | cite_funny }} )
63
63
  expected = %| LOL: bad |
64
64
 
65
- assert_equal expected, Template.parse(text).render(@assigns, :filters => [FunnyFilter])
65
+ assert_equal expected, Template.parse(text).render!(@assigns, :filters => [FunnyFilter])
66
66
  end
67
67
 
68
68
  def test_variable_piping_with_args
69
69
  text = %! {{ car.gm | add_smiley : ':-(' }} !
70
70
  expected = %| bad :-( |
71
71
 
72
- assert_equal expected, Template.parse(text).render(@assigns, :filters => [FunnyFilter])
72
+ assert_equal expected, Template.parse(text).render!(@assigns, :filters => [FunnyFilter])
73
73
  end
74
74
 
75
75
  def test_variable_piping_with_no_args
76
76
  text = %! {{ car.gm | add_smiley }} !
77
77
  expected = %| bad :-) |
78
78
 
79
- assert_equal expected, Template.parse(text).render(@assigns, :filters => [FunnyFilter])
79
+ assert_equal expected, Template.parse(text).render!(@assigns, :filters => [FunnyFilter])
80
80
  end
81
81
 
82
82
  def test_multiple_variable_piping_with_args
83
83
  text = %! {{ car.gm | add_smiley : ':-(' | add_smiley : ':-('}} !
84
84
  expected = %| bad :-( :-( |
85
85
 
86
- assert_equal expected, Template.parse(text).render(@assigns, :filters => [FunnyFilter])
86
+ assert_equal expected, Template.parse(text).render!(@assigns, :filters => [FunnyFilter])
87
87
  end
88
88
 
89
- def test_variable_piping_with_args
89
+ def test_variable_piping_with_multiple_args
90
90
  text = %! {{ car.gm | add_tag : 'span', 'bar'}} !
91
91
  expected = %| <span id="bar">bad</span> |
92
92
 
93
- assert_equal expected, Template.parse(text).render(@assigns, :filters => [FunnyFilter])
93
+ assert_equal expected, Template.parse(text).render!(@assigns, :filters => [FunnyFilter])
94
94
  end
95
95
 
96
96
  def test_variable_piping_with_variable_args
97
97
  text = %! {{ car.gm | add_tag : 'span', car.bmw}} !
98
98
  expected = %| <span id="good">bad</span> |
99
99
 
100
- assert_equal expected, Template.parse(text).render(@assigns, :filters => [FunnyFilter])
100
+ assert_equal expected, Template.parse(text).render!(@assigns, :filters => [FunnyFilter])
101
101
  end
102
102
 
103
103
  def test_multiple_pipings
104
104
  text = %( {{ best_cars | cite_funny | paragraph }} )
105
105
  expected = %| <p>LOL: bmw</p> |
106
106
 
107
- assert_equal expected, Template.parse(text).render(@assigns, :filters => [FunnyFilter])
107
+ assert_equal expected, Template.parse(text).render!(@assigns, :filters => [FunnyFilter])
108
108
  end
109
109
 
110
110
  def test_link_to
111
111
  text = %( {{ 'Typo' | link_to: 'http://typo.leetsoft.com' }} )
112
112
  expected = %| <a href="http://typo.leetsoft.com">Typo</a> |
113
113
 
114
- assert_equal expected, Template.parse(text).render(@assigns, :filters => [FunnyFilter])
114
+ assert_equal expected, Template.parse(text).render!(@assigns, :filters => [FunnyFilter])
115
115
  end
116
116
  end # OutputTest
@@ -0,0 +1,116 @@
1
+ require 'test_helper'
2
+
3
+ class ParsingQuirksTest < Minitest::Test
4
+ include Liquid
5
+
6
+ def test_parsing_css
7
+ text = " div { font-weight: bold; } "
8
+ assert_equal text, Template.parse(text).render!
9
+ end
10
+
11
+ def test_raise_on_single_close_bracet
12
+ assert_raises(SyntaxError) do
13
+ Template.parse("text {{method} oh nos!")
14
+ end
15
+ end
16
+
17
+ def test_raise_on_label_and_no_close_bracets
18
+ assert_raises(SyntaxError) do
19
+ Template.parse("TEST {{ ")
20
+ end
21
+ end
22
+
23
+ def test_raise_on_label_and_no_close_bracets_percent
24
+ assert_raises(SyntaxError) do
25
+ Template.parse("TEST {% ")
26
+ end
27
+ end
28
+
29
+ def test_error_on_empty_filter
30
+ assert Template.parse("{{test}}")
31
+ assert Template.parse("{{|test}}")
32
+ with_error_mode(:strict) do
33
+ assert_raises(SyntaxError) do
34
+ Template.parse("{{test |a|b|}}")
35
+ end
36
+ end
37
+ end
38
+
39
+ def test_meaningless_parens_error
40
+ with_error_mode(:strict) do
41
+ assert_raises(SyntaxError) do
42
+ markup = "a == 'foo' or (b == 'bar' and c == 'baz') or false"
43
+ Template.parse("{% if #{markup} %} YES {% endif %}")
44
+ end
45
+ end
46
+ end
47
+
48
+ def test_unexpected_characters_syntax_error
49
+ with_error_mode(:strict) do
50
+ assert_raises(SyntaxError) do
51
+ markup = "true && false"
52
+ Template.parse("{% if #{markup} %} YES {% endif %}")
53
+ end
54
+ assert_raises(SyntaxError) do
55
+ markup = "false || true"
56
+ Template.parse("{% if #{markup} %} YES {% endif %}")
57
+ end
58
+ end
59
+ end
60
+
61
+ def test_no_error_on_lax_empty_filter
62
+ assert Template.parse("{{test |a|b|}}", :error_mode => :lax)
63
+ assert Template.parse("{{test}}", :error_mode => :lax)
64
+ assert Template.parse("{{|test|}}", :error_mode => :lax)
65
+ end
66
+
67
+ def test_meaningless_parens_lax
68
+ with_error_mode(:lax) do
69
+ assigns = {'b' => 'bar', 'c' => 'baz'}
70
+ markup = "a == 'foo' or (b == 'bar' and c == 'baz') or false"
71
+ assert_template_result(' YES ',"{% if #{markup} %} YES {% endif %}", assigns)
72
+ end
73
+ end
74
+
75
+ def test_unexpected_characters_silently_eat_logic_lax
76
+ with_error_mode(:lax) do
77
+ markup = "true && false"
78
+ assert_template_result(' YES ',"{% if #{markup} %} YES {% endif %}")
79
+ markup = "false || true"
80
+ assert_template_result('',"{% if #{markup} %} YES {% endif %}")
81
+ end
82
+ end
83
+
84
+ def test_raise_on_invalid_tag_delimiter
85
+ assert_raises(Liquid::SyntaxError) do
86
+ Template.new.parse('{% end %}')
87
+ end
88
+ end
89
+
90
+ def test_unanchored_filter_arguments
91
+ with_error_mode(:lax) do
92
+ assert_template_result('hi',"{{ 'hi there' | split$$$:' ' | first }}")
93
+
94
+ assert_template_result('x', "{{ 'X' | downcase) }}")
95
+
96
+ # After the messed up quotes a filter without parameters (reverse) should work
97
+ # but one with parameters (remove) shouldn't be detected.
98
+ assert_template_result('here', "{{ 'hi there' | split:\"t\"\" | reverse | first}}")
99
+ assert_template_result('hi ', "{{ 'hi there' | split:\"t\"\" | remove:\"i\" | first}}")
100
+ end
101
+ end
102
+
103
+ def test_invalid_variables_work
104
+ with_error_mode(:lax) do
105
+ assert_template_result('bar', "{% assign 123foo = 'bar' %}{{ 123foo }}")
106
+ assert_template_result('123', "{% assign 123 = 'bar' %}{{ 123 }}")
107
+ end
108
+ end
109
+
110
+ def test_extra_dots_in_ranges
111
+ with_error_mode(:lax) do
112
+ assert_template_result('12345', "{% for i in (1...5) %}{{ i }}{% endfor %}")
113
+ end
114
+ end
115
+
116
+ end # ParsingQuirksTest
@@ -0,0 +1,154 @@
1
+ require 'test_helper'
2
+
3
+ class RenderProfilingTest < Minitest::Test
4
+ include Liquid
5
+
6
+ class ProfilingFileSystem
7
+ def read_template_file(template_path, context)
8
+ "Rendering template {% assign template_name = '#{template_path}'%}\n{{ template_name }}"
9
+ end
10
+ end
11
+
12
+ def setup
13
+ Liquid::Template.file_system = ProfilingFileSystem.new
14
+ end
15
+
16
+ def test_template_allows_flagging_profiling
17
+ t = Template.parse("{{ 'a string' | upcase }}")
18
+ t.render!
19
+
20
+ assert_nil t.profiler
21
+ end
22
+
23
+ def test_parse_makes_available_simple_profiling
24
+ t = Template.parse("{{ 'a string' | upcase }}", :profile => true)
25
+ t.render!
26
+
27
+ assert_equal 1, t.profiler.length
28
+
29
+ node = t.profiler[0]
30
+ assert_equal " 'a string' | upcase ", node.code
31
+ end
32
+
33
+ def test_render_ignores_raw_strings_when_profiling
34
+ t = Template.parse("This is raw string\nstuff\nNewline", :profile => true)
35
+ t.render!
36
+
37
+ assert_equal 0, t.profiler.length
38
+ end
39
+
40
+ def test_profiling_includes_line_numbers_of_liquid_nodes
41
+ t = Template.parse("{{ 'a string' | upcase }}\n{% increment test %}", :profile => true)
42
+ t.render!
43
+ assert_equal 2, t.profiler.length
44
+
45
+ # {{ 'a string' | upcase }}
46
+ assert_equal 1, t.profiler[0].line_number
47
+ # {{ increment test }}
48
+ assert_equal 2, t.profiler[1].line_number
49
+ end
50
+
51
+ def test_profiling_includes_line_numbers_of_included_partials
52
+ t = Template.parse("{% include 'a_template' %}", :profile => true)
53
+ t.render!
54
+
55
+ included_children = t.profiler[0].children
56
+
57
+ # {% assign template_name = 'a_template' %}
58
+ assert_equal 1, included_children[0].line_number
59
+ # {{ template_name }}
60
+ assert_equal 2, included_children[1].line_number
61
+ end
62
+
63
+ def test_profiling_times_the_rendering_of_tokens
64
+ t = Template.parse("{% include 'a_template' %}", :profile => true)
65
+ t.render!
66
+
67
+ node = t.profiler[0]
68
+ refute_nil node.render_time
69
+ end
70
+
71
+ def test_profiling_times_the_entire_render
72
+ t = Template.parse("{% include 'a_template' %}", :profile => true)
73
+ t.render!
74
+
75
+ assert t.profiler.total_render_time >= 0, "Total render time was not calculated"
76
+ end
77
+
78
+ def test_profiling_uses_include_to_mark_children
79
+ t = Template.parse("{{ 'a string' | upcase }}\n{% include 'a_template' %}", :profile => true)
80
+ t.render!
81
+
82
+ include_node = t.profiler[1]
83
+ assert_equal 2, include_node.children.length
84
+ end
85
+
86
+ def test_profiling_marks_children_with_the_name_of_included_partial
87
+ t = Template.parse("{{ 'a string' | upcase }}\n{% include 'a_template' %}", :profile => true)
88
+ t.render!
89
+
90
+ include_node = t.profiler[1]
91
+ include_node.children.each do |child|
92
+ assert_equal "'a_template'", child.partial
93
+ end
94
+ end
95
+
96
+ def test_profiling_supports_multiple_templates
97
+ t = Template.parse("{{ 'a string' | upcase }}\n{% include 'a_template' %}\n{% include 'b_template' %}", :profile => true)
98
+ t.render!
99
+
100
+ a_template = t.profiler[1]
101
+ a_template.children.each do |child|
102
+ assert_equal "'a_template'", child.partial
103
+ end
104
+
105
+ b_template = t.profiler[2]
106
+ b_template.children.each do |child|
107
+ assert_equal "'b_template'", child.partial
108
+ end
109
+ end
110
+
111
+ def test_profiling_supports_rendering_the_same_partial_multiple_times
112
+ t = Template.parse("{{ 'a string' | upcase }}\n{% include 'a_template' %}\n{% include 'a_template' %}", :profile => true)
113
+ t.render!
114
+
115
+ a_template1 = t.profiler[1]
116
+ a_template1.children.each do |child|
117
+ assert_equal "'a_template'", child.partial
118
+ end
119
+
120
+ a_template2 = t.profiler[2]
121
+ a_template2.children.each do |child|
122
+ assert_equal "'a_template'", child.partial
123
+ end
124
+ end
125
+
126
+ def test_can_iterate_over_each_profiling_entry
127
+ t = Template.parse("{{ 'a string' | upcase }}\n{% increment test %}", :profile => true)
128
+ t.render!
129
+
130
+ timing_count = 0
131
+ t.profiler.each do |timing|
132
+ timing_count += 1
133
+ end
134
+
135
+ assert_equal 2, timing_count
136
+ end
137
+
138
+ def test_profiling_marks_children_of_if_blocks
139
+ t = Template.parse("{% if true %} {% increment test %} {{ test }} {% endif %}", :profile => true)
140
+ t.render!
141
+
142
+ assert_equal 1, t.profiler.length
143
+ assert_equal 2, t.profiler[0].children.length
144
+ end
145
+
146
+ def test_profiling_marks_children_of_for_blocks
147
+ t = Template.parse("{% for item in collection %} {{ item }} {% endfor %}", :profile => true)
148
+ t.render!({"collection" => ["one", "two"]})
149
+
150
+ assert_equal 1, t.profiler.length
151
+ # Will profile each invocation of the for block
152
+ assert_equal 2, t.profiler[0].children.length
153
+ end
154
+ end