liquid 4.0.1 → 5.4.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 (112) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +142 -0
  3. data/README.md +10 -4
  4. data/lib/liquid/block.rb +31 -14
  5. data/lib/liquid/block_body.rb +169 -56
  6. data/lib/liquid/condition.rb +59 -23
  7. data/lib/liquid/context.rb +111 -52
  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 -33
  12. data/lib/liquid/extensions.rb +2 -0
  13. data/lib/liquid/file_system.rb +6 -4
  14. data/lib/liquid/forloop_drop.rb +54 -4
  15. data/lib/liquid/i18n.rb +5 -3
  16. data/lib/liquid/interrupts.rb +3 -1
  17. data/lib/liquid/lexer.rb +31 -24
  18. data/lib/liquid/locales/en.yml +8 -5
  19. data/lib/liquid/parse_context.rb +20 -4
  20. data/lib/liquid/parse_tree_visitor.rb +42 -0
  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 +13 -3
  27. data/lib/liquid/registers.rb +51 -0
  28. data/lib/liquid/resource_limits.rb +47 -8
  29. data/lib/liquid/standardfilters.rb +616 -129
  30. data/lib/liquid/strainer_factory.rb +41 -0
  31. data/lib/liquid/strainer_template.rb +62 -0
  32. data/lib/liquid/tablerowloop_drop.rb +64 -5
  33. data/lib/liquid/tag/disableable.rb +22 -0
  34. data/lib/liquid/tag/disabler.rb +21 -0
  35. data/lib/liquid/tag.rb +28 -6
  36. data/lib/liquid/tags/assign.rb +44 -18
  37. data/lib/liquid/tags/break.rb +16 -3
  38. data/lib/liquid/tags/capture.rb +24 -18
  39. data/lib/liquid/tags/case.rb +69 -27
  40. data/lib/liquid/tags/comment.rb +18 -3
  41. data/lib/liquid/tags/continue.rb +16 -12
  42. data/lib/liquid/tags/cycle.rb +45 -25
  43. data/lib/liquid/tags/decrement.rb +22 -20
  44. data/lib/liquid/tags/echo.rb +41 -0
  45. data/lib/liquid/tags/for.rb +97 -89
  46. data/lib/liquid/tags/if.rb +61 -35
  47. data/lib/liquid/tags/ifchanged.rb +11 -10
  48. data/lib/liquid/tags/include.rb +56 -56
  49. data/lib/liquid/tags/increment.rb +23 -17
  50. data/lib/liquid/tags/inline_comment.rb +43 -0
  51. data/lib/liquid/tags/raw.rb +25 -11
  52. data/lib/liquid/tags/render.rb +109 -0
  53. data/lib/liquid/tags/table_row.rb +53 -19
  54. data/lib/liquid/tags/unless.rb +38 -19
  55. data/lib/liquid/template.rb +52 -72
  56. data/lib/liquid/template_factory.rb +9 -0
  57. data/lib/liquid/tokenizer.rb +18 -10
  58. data/lib/liquid/usage.rb +8 -0
  59. data/lib/liquid/utils.rb +13 -3
  60. data/lib/liquid/variable.rb +52 -41
  61. data/lib/liquid/variable_lookup.rb +24 -10
  62. data/lib/liquid/version.rb +3 -1
  63. data/lib/liquid.rb +19 -6
  64. metadata +21 -104
  65. data/lib/liquid/strainer.rb +0 -66
  66. data/test/fixtures/en_locale.yml +0 -9
  67. data/test/integration/assign_test.rb +0 -48
  68. data/test/integration/blank_test.rb +0 -106
  69. data/test/integration/block_test.rb +0 -12
  70. data/test/integration/capture_test.rb +0 -50
  71. data/test/integration/context_test.rb +0 -32
  72. data/test/integration/document_test.rb +0 -19
  73. data/test/integration/drop_test.rb +0 -273
  74. data/test/integration/error_handling_test.rb +0 -260
  75. data/test/integration/filter_test.rb +0 -178
  76. data/test/integration/hash_ordering_test.rb +0 -23
  77. data/test/integration/output_test.rb +0 -123
  78. data/test/integration/parsing_quirks_test.rb +0 -122
  79. data/test/integration/render_profiling_test.rb +0 -154
  80. data/test/integration/security_test.rb +0 -80
  81. data/test/integration/standard_filter_test.rb +0 -626
  82. data/test/integration/tags/break_tag_test.rb +0 -15
  83. data/test/integration/tags/continue_tag_test.rb +0 -15
  84. data/test/integration/tags/for_tag_test.rb +0 -410
  85. data/test/integration/tags/if_else_tag_test.rb +0 -188
  86. data/test/integration/tags/include_tag_test.rb +0 -245
  87. data/test/integration/tags/increment_tag_test.rb +0 -23
  88. data/test/integration/tags/raw_tag_test.rb +0 -31
  89. data/test/integration/tags/standard_tag_test.rb +0 -296
  90. data/test/integration/tags/statements_test.rb +0 -111
  91. data/test/integration/tags/table_row_test.rb +0 -64
  92. data/test/integration/tags/unless_else_tag_test.rb +0 -26
  93. data/test/integration/template_test.rb +0 -332
  94. data/test/integration/trim_mode_test.rb +0 -529
  95. data/test/integration/variable_test.rb +0 -96
  96. data/test/test_helper.rb +0 -116
  97. data/test/unit/block_unit_test.rb +0 -58
  98. data/test/unit/condition_unit_test.rb +0 -166
  99. data/test/unit/context_unit_test.rb +0 -489
  100. data/test/unit/file_system_unit_test.rb +0 -35
  101. data/test/unit/i18n_unit_test.rb +0 -37
  102. data/test/unit/lexer_unit_test.rb +0 -51
  103. data/test/unit/parser_unit_test.rb +0 -82
  104. data/test/unit/regexp_unit_test.rb +0 -44
  105. data/test/unit/strainer_unit_test.rb +0 -164
  106. data/test/unit/tag_unit_test.rb +0 -21
  107. data/test/unit/tags/case_tag_unit_test.rb +0 -10
  108. data/test/unit/tags/for_tag_unit_test.rb +0 -13
  109. data/test/unit/tags/if_tag_unit_test.rb +0 -8
  110. data/test/unit/template_unit_test.rb +0 -78
  111. data/test/unit/tokenizer_unit_test.rb +0 -55
  112. data/test/unit/variable_unit_test.rb +0 -162
@@ -1,178 +0,0 @@
1
- require 'test_helper'
2
-
3
- module MoneyFilter
4
- def money(input)
5
- sprintf(' %d$ ', input)
6
- end
7
-
8
- def money_with_underscore(input)
9
- sprintf(' %d$ ', input)
10
- end
11
- end
12
-
13
- module CanadianMoneyFilter
14
- def money(input)
15
- sprintf(' %d$ CAD ', input)
16
- end
17
- end
18
-
19
- module SubstituteFilter
20
- def substitute(input, params = {})
21
- input.gsub(/%\{(\w+)\}/) { |match| params[$1] }
22
- end
23
- end
24
-
25
- class FiltersTest < Minitest::Test
26
- include Liquid
27
-
28
- module OverrideObjectMethodFilter
29
- def tap(input)
30
- "tap overridden"
31
- end
32
- end
33
-
34
- def setup
35
- @context = Context.new
36
- end
37
-
38
- def test_local_filter
39
- @context['var'] = 1000
40
- @context.add_filters(MoneyFilter)
41
-
42
- assert_equal ' 1000$ ', Template.parse("{{var | money}}").render(@context)
43
- end
44
-
45
- def test_underscore_in_filter_name
46
- @context['var'] = 1000
47
- @context.add_filters(MoneyFilter)
48
- assert_equal ' 1000$ ', Template.parse("{{var | money_with_underscore}}").render(@context)
49
- end
50
-
51
- def test_second_filter_overwrites_first
52
- @context['var'] = 1000
53
- @context.add_filters(MoneyFilter)
54
- @context.add_filters(CanadianMoneyFilter)
55
-
56
- assert_equal ' 1000$ CAD ', Template.parse("{{var | money}}").render(@context)
57
- end
58
-
59
- def test_size
60
- @context['var'] = 'abcd'
61
- @context.add_filters(MoneyFilter)
62
-
63
- assert_equal '4', Template.parse("{{var | size}}").render(@context)
64
- end
65
-
66
- def test_join
67
- @context['var'] = [1, 2, 3, 4]
68
-
69
- assert_equal "1 2 3 4", Template.parse("{{var | join}}").render(@context)
70
- end
71
-
72
- def test_sort
73
- @context['value'] = 3
74
- @context['numbers'] = [2, 1, 4, 3]
75
- @context['words'] = ['expected', 'as', 'alphabetic']
76
- @context['arrays'] = ['flower', 'are']
77
- @context['case_sensitive'] = ['sensitive', 'Expected', 'case']
78
-
79
- assert_equal '1 2 3 4', Template.parse("{{numbers | sort | join}}").render(@context)
80
- assert_equal 'alphabetic as expected', Template.parse("{{words | sort | join}}").render(@context)
81
- assert_equal '3', Template.parse("{{value | sort}}").render(@context)
82
- assert_equal 'are flower', Template.parse("{{arrays | sort | join}}").render(@context)
83
- assert_equal 'Expected case sensitive', Template.parse("{{case_sensitive | sort | join}}").render(@context)
84
- end
85
-
86
- def test_sort_natural
87
- @context['words'] = ['case', 'Assert', 'Insensitive']
88
- @context['hashes'] = [{ 'a' => 'A' }, { 'a' => 'b' }, { 'a' => 'C' }]
89
- @context['objects'] = [TestObject.new('A'), TestObject.new('b'), TestObject.new('C')]
90
-
91
- # Test strings
92
- assert_equal 'Assert case Insensitive', Template.parse("{{words | sort_natural | join}}").render(@context)
93
-
94
- # Test hashes
95
- assert_equal 'A b C', Template.parse("{{hashes | sort_natural: 'a' | map: 'a' | join}}").render(@context)
96
-
97
- # Test objects
98
- assert_equal 'A b C', Template.parse("{{objects | sort_natural: 'a' | map: 'a' | join}}").render(@context)
99
- end
100
-
101
- def test_compact
102
- @context['words'] = ['a', nil, 'b', nil, 'c']
103
- @context['hashes'] = [{ 'a' => 'A' }, { 'a' => nil }, { 'a' => 'C' }]
104
- @context['objects'] = [TestObject.new('A'), TestObject.new(nil), TestObject.new('C')]
105
-
106
- # Test strings
107
- assert_equal 'a b c', Template.parse("{{words | compact | join}}").render(@context)
108
-
109
- # Test hashes
110
- assert_equal 'A C', Template.parse("{{hashes | compact: 'a' | map: 'a' | join}}").render(@context)
111
-
112
- # Test objects
113
- assert_equal 'A C', Template.parse("{{objects | compact: 'a' | map: 'a' | join}}").render(@context)
114
- end
115
-
116
- def test_strip_html
117
- @context['var'] = "<b>bla blub</a>"
118
-
119
- assert_equal "bla blub", Template.parse("{{ var | strip_html }}").render(@context)
120
- end
121
-
122
- def test_strip_html_ignore_comments_with_html
123
- @context['var'] = "<!-- split and some <ul> tag --><b>bla blub</a>"
124
-
125
- assert_equal "bla blub", Template.parse("{{ var | strip_html }}").render(@context)
126
- end
127
-
128
- def test_capitalize
129
- @context['var'] = "blub"
130
-
131
- assert_equal "Blub", Template.parse("{{ var | capitalize }}").render(@context)
132
- end
133
-
134
- def test_nonexistent_filter_is_ignored
135
- @context['var'] = 1000
136
-
137
- assert_equal '1000', Template.parse("{{ var | xyzzy }}").render(@context)
138
- end
139
-
140
- def test_filter_with_keyword_arguments
141
- @context['surname'] = 'john'
142
- @context['input'] = 'hello %{first_name}, %{last_name}'
143
- @context.add_filters(SubstituteFilter)
144
- output = Template.parse(%({{ input | substitute: first_name: surname, last_name: 'doe' }})).render(@context)
145
- assert_equal 'hello john, doe', output
146
- end
147
-
148
- def test_override_object_method_in_filter
149
- assert_equal "tap overridden", Template.parse("{{var | tap}}").render!({ 'var' => 1000 }, filters: [OverrideObjectMethodFilter])
150
-
151
- # tap still treated as a non-existent filter
152
- assert_equal "1000", Template.parse("{{var | tap}}").render!({ 'var' => 1000 })
153
- end
154
- end
155
-
156
- class FiltersInTemplate < Minitest::Test
157
- include Liquid
158
-
159
- def test_local_global
160
- with_global_filter(MoneyFilter) do
161
- assert_equal " 1000$ ", Template.parse("{{1000 | money}}").render!(nil, nil)
162
- assert_equal " 1000$ CAD ", Template.parse("{{1000 | money}}").render!(nil, filters: CanadianMoneyFilter)
163
- assert_equal " 1000$ CAD ", Template.parse("{{1000 | money}}").render!(nil, filters: [CanadianMoneyFilter])
164
- end
165
- end
166
-
167
- def test_local_filter_with_deprecated_syntax
168
- assert_equal " 1000$ CAD ", Template.parse("{{1000 | money}}").render!(nil, CanadianMoneyFilter)
169
- assert_equal " 1000$ CAD ", Template.parse("{{1000 | money}}").render!(nil, [CanadianMoneyFilter])
170
- end
171
- end # FiltersTest
172
-
173
- class TestObject < Liquid::Drop
174
- attr_accessor :a
175
- def initialize(a)
176
- @a = a
177
- end
178
- end
@@ -1,23 +0,0 @@
1
- require 'test_helper'
2
-
3
- class HashOrderingTest < Minitest::Test
4
- module MoneyFilter
5
- def money(input)
6
- sprintf(' %d$ ', input)
7
- end
8
- end
9
-
10
- module CanadianMoneyFilter
11
- def money(input)
12
- sprintf(' %d$ CAD ', input)
13
- end
14
- end
15
-
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
@@ -1,123 +0,0 @@
1
- require 'test_helper'
2
-
3
- module FunnyFilter
4
- def make_funny(input)
5
- 'LOL'
6
- end
7
-
8
- def cite_funny(input)
9
- "LOL: #{input}"
10
- end
11
-
12
- def add_smiley(input, smiley = ":-)")
13
- "#{input} #{smiley}"
14
- end
15
-
16
- def add_tag(input, tag = "p", id = "foo")
17
- %(<#{tag} id="#{id}">#{input}</#{tag}>)
18
- end
19
-
20
- def paragraph(input)
21
- "<p>#{input}</p>"
22
- end
23
-
24
- def link_to(name, url)
25
- %(<a href="#{url}">#{name}</a>)
26
- end
27
- end
28
-
29
- class OutputTest < Minitest::Test
30
- include Liquid
31
-
32
- def setup
33
- @assigns = {
34
- 'best_cars' => 'bmw',
35
- 'car' => { 'bmw' => 'good', 'gm' => 'bad' }
36
- }
37
- end
38
-
39
- def test_variable
40
- text = %( {{best_cars}} )
41
-
42
- expected = %( bmw )
43
- assert_equal expected, Template.parse(text).render!(@assigns)
44
- end
45
-
46
- def test_variable_traversing_with_two_brackets
47
- text = %({{ site.data.menu[include.menu][include.locale] }})
48
- assert_equal "it works!", Template.parse(text).render!(
49
- "site" => { "data" => { "menu" => { "foo" => { "bar" => "it works!" } } } },
50
- "include" => { "menu" => "foo", "locale" => "bar" }
51
- )
52
- end
53
-
54
- def test_variable_traversing
55
- text = %( {{car.bmw}} {{car.gm}} {{car.bmw}} )
56
-
57
- expected = %( good bad good )
58
- assert_equal expected, Template.parse(text).render!(@assigns)
59
- end
60
-
61
- def test_variable_piping
62
- text = %( {{ car.gm | make_funny }} )
63
- expected = %( LOL )
64
-
65
- assert_equal expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter])
66
- end
67
-
68
- def test_variable_piping_with_input
69
- text = %( {{ car.gm | cite_funny }} )
70
- expected = %( LOL: bad )
71
-
72
- assert_equal expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter])
73
- end
74
-
75
- def test_variable_piping_with_args
76
- text = %! {{ car.gm | add_smiley : ':-(' }} !
77
- expected = %| bad :-( |
78
-
79
- assert_equal expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter])
80
- end
81
-
82
- def test_variable_piping_with_no_args
83
- text = %( {{ car.gm | add_smiley }} )
84
- expected = %| bad :-) |
85
-
86
- assert_equal expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter])
87
- end
88
-
89
- def test_multiple_variable_piping_with_args
90
- text = %! {{ car.gm | add_smiley : ':-(' | add_smiley : ':-('}} !
91
- expected = %| bad :-( :-( |
92
-
93
- assert_equal expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter])
94
- end
95
-
96
- def test_variable_piping_with_multiple_args
97
- text = %( {{ car.gm | add_tag : 'span', 'bar'}} )
98
- expected = %( <span id="bar">bad</span> )
99
-
100
- assert_equal expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter])
101
- end
102
-
103
- def test_variable_piping_with_variable_args
104
- text = %( {{ car.gm | add_tag : 'span', car.bmw}} )
105
- expected = %( <span id="good">bad</span> )
106
-
107
- assert_equal expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter])
108
- end
109
-
110
- def test_multiple_pipings
111
- text = %( {{ best_cars | cite_funny | paragraph }} )
112
- expected = %( <p>LOL: bmw</p> )
113
-
114
- assert_equal expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter])
115
- end
116
-
117
- def test_link_to
118
- text = %( {{ 'Typo' | link_to: 'http://typo.leetsoft.com' }} )
119
- expected = %( <a href="http://typo.leetsoft.com">Typo</a> )
120
-
121
- assert_equal expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter])
122
- end
123
- end # OutputTest
@@ -1,122 +0,0 @@
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
-
32
- with_error_mode(:lax) do
33
- assert Template.parse("{{|test}}")
34
- end
35
-
36
- with_error_mode(:strict) do
37
- assert_raises(SyntaxError) { Template.parse("{{|test}}") }
38
- assert_raises(SyntaxError) { Template.parse("{{test |a|b|}}") }
39
- end
40
- end
41
-
42
- def test_meaningless_parens_error
43
- with_error_mode(:strict) do
44
- assert_raises(SyntaxError) do
45
- markup = "a == 'foo' or (b == 'bar' and c == 'baz') or false"
46
- Template.parse("{% if #{markup} %} YES {% endif %}")
47
- end
48
- end
49
- end
50
-
51
- def test_unexpected_characters_syntax_error
52
- with_error_mode(:strict) do
53
- assert_raises(SyntaxError) do
54
- markup = "true && false"
55
- Template.parse("{% if #{markup} %} YES {% endif %}")
56
- end
57
- assert_raises(SyntaxError) do
58
- markup = "false || true"
59
- Template.parse("{% if #{markup} %} YES {% endif %}")
60
- end
61
- end
62
- end
63
-
64
- def test_no_error_on_lax_empty_filter
65
- assert Template.parse("{{test |a|b|}}", error_mode: :lax)
66
- assert Template.parse("{{test}}", error_mode: :lax)
67
- assert Template.parse("{{|test|}}", error_mode: :lax)
68
- end
69
-
70
- def test_meaningless_parens_lax
71
- with_error_mode(:lax) do
72
- assigns = { 'b' => 'bar', 'c' => 'baz' }
73
- markup = "a == 'foo' or (b == 'bar' and c == 'baz') or false"
74
- assert_template_result(' YES ', "{% if #{markup} %} YES {% endif %}", assigns)
75
- end
76
- end
77
-
78
- def test_unexpected_characters_silently_eat_logic_lax
79
- with_error_mode(:lax) do
80
- markup = "true && false"
81
- assert_template_result(' YES ', "{% if #{markup} %} YES {% endif %}")
82
- markup = "false || true"
83
- assert_template_result('', "{% if #{markup} %} YES {% endif %}")
84
- end
85
- end
86
-
87
- def test_raise_on_invalid_tag_delimiter
88
- assert_raises(Liquid::SyntaxError) do
89
- Template.new.parse('{% end %}')
90
- end
91
- end
92
-
93
- def test_unanchored_filter_arguments
94
- with_error_mode(:lax) do
95
- assert_template_result('hi', "{{ 'hi there' | split$$$:' ' | first }}")
96
-
97
- assert_template_result('x', "{{ 'X' | downcase) }}")
98
-
99
- # After the messed up quotes a filter without parameters (reverse) should work
100
- # but one with parameters (remove) shouldn't be detected.
101
- assert_template_result('here', "{{ 'hi there' | split:\"t\"\" | reverse | first}}")
102
- assert_template_result('hi ', "{{ 'hi there' | split:\"t\"\" | remove:\"i\" | first}}")
103
- end
104
- end
105
-
106
- def test_invalid_variables_work
107
- with_error_mode(:lax) do
108
- assert_template_result('bar', "{% assign 123foo = 'bar' %}{{ 123foo }}")
109
- assert_template_result('123', "{% assign 123 = 'bar' %}{{ 123 }}")
110
- end
111
- end
112
-
113
- def test_extra_dots_in_ranges
114
- with_error_mode(:lax) do
115
- assert_template_result('12345', "{% for i in (1...5) %}{{ i }}{% endfor %}")
116
- end
117
- end
118
-
119
- def test_contains_in_id
120
- assert_template_result(' YES ', '{% if containsallshipments == true %} YES {% endif %}', 'containsallshipments' => true)
121
- end
122
- end # ParsingQuirksTest
@@ -1,154 +0,0 @@
1
- require 'test_helper'
2
-
3
- class RenderProfilingTest < Minitest::Test
4
- include Liquid
5
-
6
- class ProfilingFileSystem
7
- def read_template_file(template_path)
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
@@ -1,80 +0,0 @@
1
- require 'test_helper'
2
-
3
- module SecurityFilter
4
- def add_one(input)
5
- "#{input} + 1"
6
- end
7
- end
8
-
9
- class SecurityTest < Minitest::Test
10
- include Liquid
11
-
12
- def setup
13
- @assigns = {}
14
- end
15
-
16
- def test_no_instance_eval
17
- text = %( {{ '1+1' | instance_eval }} )
18
- expected = %( 1+1 )
19
-
20
- assert_equal expected, Template.parse(text).render!(@assigns)
21
- end
22
-
23
- def test_no_existing_instance_eval
24
- text = %( {{ '1+1' | __instance_eval__ }} )
25
- expected = %( 1+1 )
26
-
27
- assert_equal expected, Template.parse(text).render!(@assigns)
28
- end
29
-
30
- def test_no_instance_eval_after_mixing_in_new_filter
31
- text = %( {{ '1+1' | instance_eval }} )
32
- expected = %( 1+1 )
33
-
34
- assert_equal expected, Template.parse(text).render!(@assigns)
35
- end
36
-
37
- def test_no_instance_eval_later_in_chain
38
- text = %( {{ '1+1' | add_one | instance_eval }} )
39
- expected = %( 1+1 + 1 )
40
-
41
- assert_equal expected, Template.parse(text).render!(@assigns, filters: SecurityFilter)
42
- end
43
-
44
- def test_does_not_add_filters_to_symbol_table
45
- current_symbols = Symbol.all_symbols
46
-
47
- test = %( {{ "some_string" | a_bad_filter }} )
48
-
49
- template = Template.parse(test)
50
- assert_equal [], (Symbol.all_symbols - current_symbols)
51
-
52
- template.render!
53
- assert_equal [], (Symbol.all_symbols - current_symbols)
54
- end
55
-
56
- def test_does_not_add_drop_methods_to_symbol_table
57
- current_symbols = Symbol.all_symbols
58
-
59
- assigns = { 'drop' => Drop.new }
60
- assert_equal "", Template.parse("{{ drop.custom_method_1 }}", assigns).render!
61
- assert_equal "", Template.parse("{{ drop.custom_method_2 }}", assigns).render!
62
- assert_equal "", Template.parse("{{ drop.custom_method_3 }}", assigns).render!
63
-
64
- assert_equal [], (Symbol.all_symbols - current_symbols)
65
- end
66
-
67
- def test_max_depth_nested_blocks_does_not_raise_exception
68
- depth = Liquid::Block::MAX_DEPTH
69
- code = "{% if true %}" * depth + "rendered" + "{% endif %}" * depth
70
- assert_equal "rendered", Template.parse(code).render!
71
- end
72
-
73
- def test_more_than_max_depth_nested_blocks_raises_exception
74
- depth = Liquid::Block::MAX_DEPTH + 1
75
- code = "{% if true %}" * depth + "rendered" + "{% endif %}" * depth
76
- assert_raises(Liquid::StackLevelError) do
77
- Template.parse(code).render!
78
- end
79
- end
80
- end # SecurityTest