liquid 5.1.0 → 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 (98) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +35 -0
  3. data/README.md +4 -4
  4. data/lib/liquid/block_body.rb +6 -6
  5. data/lib/liquid/condition.rb +7 -1
  6. data/lib/liquid/context.rb +6 -2
  7. data/lib/liquid/expression.rb +11 -10
  8. data/lib/liquid/forloop_drop.rb +44 -1
  9. data/lib/liquid/locales/en.yml +6 -5
  10. data/lib/liquid/partial_cache.rb +3 -3
  11. data/lib/liquid/registers.rb +51 -0
  12. data/lib/liquid/standardfilters.rb +463 -75
  13. data/lib/liquid/strainer_factory.rb +15 -10
  14. data/lib/liquid/strainer_template.rb +9 -0
  15. data/lib/liquid/tablerowloop_drop.rb +58 -1
  16. data/lib/liquid/tags/assign.rb +12 -8
  17. data/lib/liquid/tags/break.rb +8 -0
  18. data/lib/liquid/tags/capture.rb +13 -10
  19. data/lib/liquid/tags/case.rb +21 -0
  20. data/lib/liquid/tags/comment.rb +13 -0
  21. data/lib/liquid/tags/continue.rb +8 -9
  22. data/lib/liquid/tags/cycle.rb +12 -11
  23. data/lib/liquid/tags/decrement.rb +16 -17
  24. data/lib/liquid/tags/echo.rb +16 -9
  25. data/lib/liquid/tags/for.rb +22 -43
  26. data/lib/liquid/tags/if.rb +11 -9
  27. data/lib/liquid/tags/include.rb +15 -13
  28. data/lib/liquid/tags/increment.rb +16 -14
  29. data/lib/liquid/tags/inline_comment.rb +43 -0
  30. data/lib/liquid/tags/raw.rb +11 -0
  31. data/lib/liquid/tags/render.rb +29 -4
  32. data/lib/liquid/tags/table_row.rb +22 -0
  33. data/lib/liquid/tags/unless.rb +15 -4
  34. data/lib/liquid/template.rb +2 -3
  35. data/lib/liquid/variable.rb +4 -4
  36. data/lib/liquid/variable_lookup.rb +10 -7
  37. data/lib/liquid/version.rb +1 -1
  38. data/lib/liquid.rb +4 -4
  39. metadata +7 -121
  40. data/lib/liquid/register.rb +0 -6
  41. data/lib/liquid/static_registers.rb +0 -44
  42. data/test/fixtures/en_locale.yml +0 -9
  43. data/test/integration/assign_test.rb +0 -117
  44. data/test/integration/blank_test.rb +0 -109
  45. data/test/integration/block_test.rb +0 -58
  46. data/test/integration/capture_test.rb +0 -58
  47. data/test/integration/context_test.rb +0 -636
  48. data/test/integration/document_test.rb +0 -21
  49. data/test/integration/drop_test.rb +0 -257
  50. data/test/integration/error_handling_test.rb +0 -272
  51. data/test/integration/expression_test.rb +0 -46
  52. data/test/integration/filter_test.rb +0 -189
  53. data/test/integration/hash_ordering_test.rb +0 -25
  54. data/test/integration/output_test.rb +0 -125
  55. data/test/integration/parsing_quirks_test.rb +0 -134
  56. data/test/integration/profiler_test.rb +0 -213
  57. data/test/integration/security_test.rb +0 -89
  58. data/test/integration/standard_filter_test.rb +0 -880
  59. data/test/integration/tag/disableable_test.rb +0 -59
  60. data/test/integration/tag_test.rb +0 -45
  61. data/test/integration/tags/break_tag_test.rb +0 -17
  62. data/test/integration/tags/continue_tag_test.rb +0 -17
  63. data/test/integration/tags/echo_test.rb +0 -13
  64. data/test/integration/tags/for_tag_test.rb +0 -466
  65. data/test/integration/tags/if_else_tag_test.rb +0 -190
  66. data/test/integration/tags/include_tag_test.rb +0 -269
  67. data/test/integration/tags/increment_tag_test.rb +0 -25
  68. data/test/integration/tags/liquid_tag_test.rb +0 -116
  69. data/test/integration/tags/raw_tag_test.rb +0 -34
  70. data/test/integration/tags/render_tag_test.rb +0 -213
  71. data/test/integration/tags/standard_tag_test.rb +0 -303
  72. data/test/integration/tags/statements_test.rb +0 -113
  73. data/test/integration/tags/table_row_test.rb +0 -66
  74. data/test/integration/tags/unless_else_tag_test.rb +0 -28
  75. data/test/integration/template_test.rb +0 -340
  76. data/test/integration/trim_mode_test.rb +0 -563
  77. data/test/integration/variable_test.rb +0 -138
  78. data/test/test_helper.rb +0 -207
  79. data/test/unit/block_unit_test.rb +0 -53
  80. data/test/unit/condition_unit_test.rb +0 -168
  81. data/test/unit/file_system_unit_test.rb +0 -37
  82. data/test/unit/i18n_unit_test.rb +0 -39
  83. data/test/unit/lexer_unit_test.rb +0 -53
  84. data/test/unit/parse_tree_visitor_test.rb +0 -261
  85. data/test/unit/parser_unit_test.rb +0 -84
  86. data/test/unit/partial_cache_unit_test.rb +0 -128
  87. data/test/unit/regexp_unit_test.rb +0 -46
  88. data/test/unit/static_registers_unit_test.rb +0 -156
  89. data/test/unit/strainer_factory_unit_test.rb +0 -100
  90. data/test/unit/strainer_template_unit_test.rb +0 -82
  91. data/test/unit/tag_unit_test.rb +0 -23
  92. data/test/unit/tags/case_tag_unit_test.rb +0 -12
  93. data/test/unit/tags/for_tag_unit_test.rb +0 -15
  94. data/test/unit/tags/if_tag_unit_test.rb +0 -10
  95. data/test/unit/template_factory_unit_test.rb +0 -12
  96. data/test/unit/template_unit_test.rb +0 -87
  97. data/test/unit/tokenizer_unit_test.rb +0 -62
  98. data/test/unit/variable_unit_test.rb +0 -164
@@ -1,189 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'test_helper'
4
-
5
- module MoneyFilter
6
- def money(input)
7
- format(' %d$ ', input)
8
- end
9
-
10
- def money_with_underscore(input)
11
- format(' %d$ ', input)
12
- end
13
- end
14
-
15
- module CanadianMoneyFilter
16
- def money(input)
17
- format(' %d$ CAD ', input)
18
- end
19
- end
20
-
21
- module SubstituteFilter
22
- def substitute(input, params = {})
23
- input.gsub(/%\{(\w+)\}/) { |_match| params[Regexp.last_match(1)] }
24
- end
25
- end
26
-
27
- class FiltersTest < Minitest::Test
28
- include Liquid
29
-
30
- module OverrideObjectMethodFilter
31
- def tap(_input)
32
- "tap overridden"
33
- end
34
- end
35
-
36
- def setup
37
- @context = Context.new
38
- end
39
-
40
- def test_local_filter
41
- @context['var'] = 1000
42
- @context.add_filters(MoneyFilter)
43
-
44
- assert_equal(' 1000$ ', Template.parse("{{var | money}}").render(@context))
45
- end
46
-
47
- def test_underscore_in_filter_name
48
- @context['var'] = 1000
49
- @context.add_filters(MoneyFilter)
50
- assert_equal(' 1000$ ', Template.parse("{{var | money_with_underscore}}").render(@context))
51
- end
52
-
53
- def test_second_filter_overwrites_first
54
- @context['var'] = 1000
55
- @context.add_filters(MoneyFilter)
56
- @context.add_filters(CanadianMoneyFilter)
57
-
58
- assert_equal(' 1000$ CAD ', Template.parse("{{var | money}}").render(@context))
59
- end
60
-
61
- def test_size
62
- @context['var'] = 'abcd'
63
- @context.add_filters(MoneyFilter)
64
-
65
- assert_equal('4', Template.parse("{{var | size}}").render(@context))
66
- end
67
-
68
- def test_join
69
- @context['var'] = [1, 2, 3, 4]
70
-
71
- assert_equal("1 2 3 4", Template.parse("{{var | join}}").render(@context))
72
- end
73
-
74
- def test_sort
75
- @context['value'] = 3
76
- @context['numbers'] = [2, 1, 4, 3]
77
- @context['words'] = ['expected', 'as', 'alphabetic']
78
- @context['arrays'] = ['flower', 'are']
79
- @context['case_sensitive'] = ['sensitive', 'Expected', 'case']
80
-
81
- assert_equal('1 2 3 4', Template.parse("{{numbers | sort | join}}").render(@context))
82
- assert_equal('alphabetic as expected', Template.parse("{{words | sort | join}}").render(@context))
83
- assert_equal('3', Template.parse("{{value | sort}}").render(@context))
84
- assert_equal('are flower', Template.parse("{{arrays | sort | join}}").render(@context))
85
- assert_equal('Expected case sensitive', Template.parse("{{case_sensitive | sort | join}}").render(@context))
86
- end
87
-
88
- def test_sort_natural
89
- @context['words'] = ['case', 'Assert', 'Insensitive']
90
- @context['hashes'] = [{ 'a' => 'A' }, { 'a' => 'b' }, { 'a' => 'C' }]
91
- @context['objects'] = [TestObject.new('A'), TestObject.new('b'), TestObject.new('C')]
92
-
93
- # Test strings
94
- assert_equal('Assert case Insensitive', Template.parse("{{words | sort_natural | join}}").render(@context))
95
-
96
- # Test hashes
97
- assert_equal('A b C', Template.parse("{{hashes | sort_natural: 'a' | map: 'a' | join}}").render(@context))
98
-
99
- # Test objects
100
- assert_equal('A b C', Template.parse("{{objects | sort_natural: 'a' | map: 'a' | join}}").render(@context))
101
- end
102
-
103
- def test_compact
104
- @context['words'] = ['a', nil, 'b', nil, 'c']
105
- @context['hashes'] = [{ 'a' => 'A' }, { 'a' => nil }, { 'a' => 'C' }]
106
- @context['objects'] = [TestObject.new('A'), TestObject.new(nil), TestObject.new('C')]
107
-
108
- # Test strings
109
- assert_equal('a b c', Template.parse("{{words | compact | join}}").render(@context))
110
-
111
- # Test hashes
112
- assert_equal('A C', Template.parse("{{hashes | compact: 'a' | map: 'a' | join}}").render(@context))
113
-
114
- # Test objects
115
- assert_equal('A C', Template.parse("{{objects | compact: 'a' | map: 'a' | join}}").render(@context))
116
- end
117
-
118
- def test_strip_html
119
- @context['var'] = "<b>bla blub</a>"
120
-
121
- assert_equal("bla blub", Template.parse("{{ var | strip_html }}").render(@context))
122
- end
123
-
124
- def test_strip_html_ignore_comments_with_html
125
- @context['var'] = "<!-- split and some <ul> tag --><b>bla blub</a>"
126
-
127
- assert_equal("bla blub", Template.parse("{{ var | strip_html }}").render(@context))
128
- end
129
-
130
- def test_capitalize
131
- @context['var'] = "blub"
132
-
133
- assert_equal("Blub", Template.parse("{{ var | capitalize }}").render(@context))
134
- end
135
-
136
- def test_nonexistent_filter_is_ignored
137
- @context['var'] = 1000
138
-
139
- assert_equal('1000', Template.parse("{{ var | xyzzy }}").render(@context))
140
- end
141
-
142
- def test_filter_with_keyword_arguments
143
- @context['surname'] = 'john'
144
- @context['input'] = 'hello %{first_name}, %{last_name}'
145
- @context.add_filters(SubstituteFilter)
146
- output = Template.parse(%({{ input | substitute: first_name: surname, last_name: 'doe' }})).render(@context)
147
- assert_equal('hello john, doe', output)
148
- end
149
-
150
- def test_override_object_method_in_filter
151
- assert_equal("tap overridden", Template.parse("{{var | tap}}").render!({ 'var' => 1000 }, filters: [OverrideObjectMethodFilter]))
152
-
153
- # tap still treated as a non-existent filter
154
- assert_equal("1000", Template.parse("{{var | tap}}").render!('var' => 1000))
155
- end
156
-
157
- def test_liquid_argument_error
158
- source = "{{ '' | size: 'too many args' }}"
159
- exc = assert_raises(Liquid::ArgumentError) do
160
- Template.parse(source).render!
161
- end
162
- assert_match(/\ALiquid error: wrong number of arguments /, exc.message)
163
- assert_equal(exc.message, Template.parse(source).render)
164
- end
165
- end
166
-
167
- class FiltersInTemplate < Minitest::Test
168
- include Liquid
169
-
170
- def test_local_global
171
- with_global_filter(MoneyFilter) do
172
- assert_equal(" 1000$ ", Template.parse("{{1000 | money}}").render!(nil, nil))
173
- assert_equal(" 1000$ CAD ", Template.parse("{{1000 | money}}").render!(nil, filters: CanadianMoneyFilter))
174
- assert_equal(" 1000$ CAD ", Template.parse("{{1000 | money}}").render!(nil, filters: [CanadianMoneyFilter]))
175
- end
176
- end
177
-
178
- def test_local_filter_with_deprecated_syntax
179
- assert_equal(" 1000$ CAD ", Template.parse("{{1000 | money}}").render!(nil, CanadianMoneyFilter))
180
- assert_equal(" 1000$ CAD ", Template.parse("{{1000 | money}}").render!(nil, [CanadianMoneyFilter]))
181
- end
182
- end # FiltersTest
183
-
184
- class TestObject < Liquid::Drop
185
- attr_accessor :a
186
- def initialize(a)
187
- @a = a
188
- end
189
- end
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'test_helper'
4
-
5
- class HashOrderingTest < Minitest::Test
6
- module MoneyFilter
7
- def money(input)
8
- format(' %d$ ', input)
9
- end
10
- end
11
-
12
- module CanadianMoneyFilter
13
- def money(input)
14
- format(' %d$ CAD ', input)
15
- end
16
- end
17
-
18
- include Liquid
19
-
20
- def test_global_register_order
21
- with_global_filter(MoneyFilter, CanadianMoneyFilter) do
22
- assert_equal(" 1000$ CAD ", Template.parse("{{1000 | money}}").render(nil, nil))
23
- end
24
- end
25
- end
@@ -1,125 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'test_helper'
4
-
5
- module FunnyFilter
6
- def make_funny(_input)
7
- 'LOL'
8
- end
9
-
10
- def cite_funny(input)
11
- "LOL: #{input}"
12
- end
13
-
14
- def add_smiley(input, smiley = ":-)")
15
- "#{input} #{smiley}"
16
- end
17
-
18
- def add_tag(input, tag = "p", id = "foo")
19
- %(<#{tag} id="#{id}">#{input}</#{tag}>)
20
- end
21
-
22
- def paragraph(input)
23
- "<p>#{input}</p>"
24
- end
25
-
26
- def link_to(name, url)
27
- %(<a href="#{url}">#{name}</a>)
28
- end
29
- end
30
-
31
- class OutputTest < Minitest::Test
32
- include Liquid
33
-
34
- def setup
35
- @assigns = {
36
- 'best_cars' => 'bmw',
37
- 'car' => { 'bmw' => 'good', 'gm' => 'bad' },
38
- }
39
- end
40
-
41
- def test_variable
42
- text = %( {{best_cars}} )
43
-
44
- expected = %( bmw )
45
- assert_equal(expected, Template.parse(text).render!(@assigns))
46
- end
47
-
48
- def test_variable_traversing_with_two_brackets
49
- text = %({{ site.data.menu[include.menu][include.locale] }})
50
- assert_equal("it works!", Template.parse(text).render!(
51
- "site" => { "data" => { "menu" => { "foo" => { "bar" => "it works!" } } } },
52
- "include" => { "menu" => "foo", "locale" => "bar" }
53
- ))
54
- end
55
-
56
- def test_variable_traversing
57
- text = %( {{car.bmw}} {{car.gm}} {{car.bmw}} )
58
-
59
- expected = %( good bad good )
60
- assert_equal(expected, Template.parse(text).render!(@assigns))
61
- end
62
-
63
- def test_variable_piping
64
- text = %( {{ car.gm | make_funny }} )
65
- expected = %( LOL )
66
-
67
- assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter]))
68
- end
69
-
70
- def test_variable_piping_with_input
71
- text = %( {{ car.gm | cite_funny }} )
72
- expected = %( LOL: bad )
73
-
74
- assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter]))
75
- end
76
-
77
- def test_variable_piping_with_args
78
- text = %! {{ car.gm | add_smiley : ':-(' }} !
79
- expected = %| bad :-( |
80
-
81
- assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter]))
82
- end
83
-
84
- def test_variable_piping_with_no_args
85
- text = %( {{ car.gm | add_smiley }} )
86
- expected = %| bad :-) |
87
-
88
- assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter]))
89
- end
90
-
91
- def test_multiple_variable_piping_with_args
92
- text = %! {{ car.gm | add_smiley : ':-(' | add_smiley : ':-('}} !
93
- expected = %| bad :-( :-( |
94
-
95
- assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter]))
96
- end
97
-
98
- def test_variable_piping_with_multiple_args
99
- text = %( {{ car.gm | add_tag : 'span', 'bar'}} )
100
- expected = %( <span id="bar">bad</span> )
101
-
102
- assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter]))
103
- end
104
-
105
- def test_variable_piping_with_variable_args
106
- text = %( {{ car.gm | add_tag : 'span', car.bmw}} )
107
- expected = %( <span id="good">bad</span> )
108
-
109
- assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter]))
110
- end
111
-
112
- def test_multiple_pipings
113
- text = %( {{ best_cars | cite_funny | paragraph }} )
114
- expected = %( <p>LOL: bmw</p> )
115
-
116
- assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter]))
117
- end
118
-
119
- def test_link_to
120
- text = %( {{ 'Typo' | link_to: 'http://typo.leetsoft.com' }} )
121
- expected = %( <a href="http://typo.leetsoft.com">Typo</a> )
122
-
123
- assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter]))
124
- end
125
- end # OutputTest
@@ -1,134 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'test_helper'
4
-
5
- class ParsingQuirksTest < Minitest::Test
6
- include Liquid
7
-
8
- def test_parsing_css
9
- text = " div { font-weight: bold; } "
10
- assert_equal(text, Template.parse(text).render!)
11
- end
12
-
13
- def test_raise_on_single_close_bracet
14
- assert_raises(SyntaxError) do
15
- Template.parse("text {{method} oh nos!")
16
- end
17
- end
18
-
19
- def test_raise_on_label_and_no_close_bracets
20
- assert_raises(SyntaxError) do
21
- Template.parse("TEST {{ ")
22
- end
23
- end
24
-
25
- def test_raise_on_label_and_no_close_bracets_percent
26
- assert_raises(SyntaxError) do
27
- Template.parse("TEST {% ")
28
- end
29
- end
30
-
31
- def test_error_on_empty_filter
32
- assert(Template.parse("{{test}}"))
33
-
34
- with_error_mode(:lax) do
35
- assert(Template.parse("{{|test}}"))
36
- end
37
-
38
- with_error_mode(:strict) do
39
- assert_raises(SyntaxError) { Template.parse("{{|test}}") }
40
- assert_raises(SyntaxError) { Template.parse("{{test |a|b|}}") }
41
- end
42
- end
43
-
44
- def test_meaningless_parens_error
45
- with_error_mode(:strict) do
46
- assert_raises(SyntaxError) do
47
- markup = "a == 'foo' or (b == 'bar' and c == 'baz') or false"
48
- Template.parse("{% if #{markup} %} YES {% endif %}")
49
- end
50
- end
51
- end
52
-
53
- def test_unexpected_characters_syntax_error
54
- with_error_mode(:strict) do
55
- assert_raises(SyntaxError) do
56
- markup = "true && false"
57
- Template.parse("{% if #{markup} %} YES {% endif %}")
58
- end
59
- assert_raises(SyntaxError) do
60
- markup = "false || true"
61
- Template.parse("{% if #{markup} %} YES {% endif %}")
62
- end
63
- end
64
- end
65
-
66
- def test_no_error_on_lax_empty_filter
67
- assert(Template.parse("{{test |a|b|}}", error_mode: :lax))
68
- assert(Template.parse("{{test}}", error_mode: :lax))
69
- assert(Template.parse("{{|test|}}", error_mode: :lax))
70
- end
71
-
72
- def test_meaningless_parens_lax
73
- with_error_mode(:lax) do
74
- assigns = { 'b' => 'bar', 'c' => 'baz' }
75
- markup = "a == 'foo' or (b == 'bar' and c == 'baz') or false"
76
- assert_template_result(' YES ', "{% if #{markup} %} YES {% endif %}", assigns)
77
- end
78
- end
79
-
80
- def test_unexpected_characters_silently_eat_logic_lax
81
- with_error_mode(:lax) do
82
- markup = "true && false"
83
- assert_template_result(' YES ', "{% if #{markup} %} YES {% endif %}")
84
- markup = "false || true"
85
- assert_template_result('', "{% if #{markup} %} YES {% endif %}")
86
- end
87
- end
88
-
89
- def test_raise_on_invalid_tag_delimiter
90
- assert_raises(Liquid::SyntaxError) do
91
- Template.new.parse('{% end %}')
92
- end
93
- end
94
-
95
- def test_unanchored_filter_arguments
96
- with_error_mode(:lax) do
97
- assert_template_result('hi', "{{ 'hi there' | split$$$:' ' | first }}")
98
-
99
- assert_template_result('x', "{{ 'X' | downcase) }}")
100
-
101
- # After the messed up quotes a filter without parameters (reverse) should work
102
- # but one with parameters (remove) shouldn't be detected.
103
- assert_template_result('here', "{{ 'hi there' | split:\"t\"\" | reverse | first}}")
104
- assert_template_result('hi ', "{{ 'hi there' | split:\"t\"\" | remove:\"i\" | first}}")
105
- end
106
- end
107
-
108
- def test_invalid_variables_work
109
- with_error_mode(:lax) do
110
- assert_template_result('bar', "{% assign 123foo = 'bar' %}{{ 123foo }}")
111
- assert_template_result('123', "{% assign 123 = 'bar' %}{{ 123 }}")
112
- end
113
- end
114
-
115
- def test_extra_dots_in_ranges
116
- with_error_mode(:lax) do
117
- assert_template_result('12345', "{% for i in (1...5) %}{{ i }}{% endfor %}")
118
- end
119
- end
120
-
121
- def test_blank_variable_markup
122
- assert_template_result('', "{{}}")
123
- end
124
-
125
- def test_lookup_on_var_with_literal_name
126
- assigns = { "blank" => { "x" => "result" } }
127
- assert_template_result('result', "{{ blank.x }}", assigns)
128
- assert_template_result('result', "{{ blank['x'] }}", assigns)
129
- end
130
-
131
- def test_contains_in_id
132
- assert_template_result(' YES ', '{% if containsallshipments == true %} YES {% endif %}', 'containsallshipments' => true)
133
- end
134
- end # ParsingQuirksTest
@@ -1,213 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'test_helper'
4
-
5
- class ProfilerTest < Minitest::Test
6
- include Liquid
7
-
8
- class ProfilingFileSystem
9
- def read_template_file(template_path)
10
- "Rendering template {% assign template_name = '#{template_path}'%}\n{{ template_name }}"
11
- end
12
- end
13
-
14
- def setup
15
- Liquid::Template.file_system = ProfilingFileSystem.new
16
- end
17
-
18
- def test_template_allows_flagging_profiling
19
- t = Template.parse("{{ 'a string' | upcase }}")
20
- t.render!
21
-
22
- assert_nil(t.profiler)
23
- end
24
-
25
- def test_parse_makes_available_simple_profiling
26
- t = Template.parse("{{ 'a string' | upcase }}", profile: true)
27
- t.render!
28
-
29
- assert_equal(1, t.profiler.length)
30
-
31
- node = t.profiler[0]
32
- assert_equal(" 'a string' | upcase ", node.code)
33
- end
34
-
35
- def test_render_ignores_raw_strings_when_profiling
36
- t = Template.parse("This is raw string\nstuff\nNewline", profile: true)
37
- t.render!
38
-
39
- assert_equal(0, t.profiler.length)
40
- end
41
-
42
- def test_profiling_includes_line_numbers_of_liquid_nodes
43
- t = Template.parse("{{ 'a string' | upcase }}\n{% increment test %}", profile: true)
44
- t.render!
45
- assert_equal(2, t.profiler.length)
46
-
47
- # {{ 'a string' | upcase }}
48
- assert_equal(1, t.profiler[0].line_number)
49
- # {{ increment test }}
50
- assert_equal(2, t.profiler[1].line_number)
51
- end
52
-
53
- def test_profiling_includes_line_numbers_of_included_partials
54
- t = Template.parse("{% include 'a_template' %}", profile: true)
55
- t.render!
56
-
57
- included_children = t.profiler[0].children
58
-
59
- # {% assign template_name = 'a_template' %}
60
- assert_equal(1, included_children[0].line_number)
61
- # {{ template_name }}
62
- assert_equal(2, included_children[1].line_number)
63
- end
64
-
65
- def test_profiling_render_tag
66
- t = Template.parse("{% render 'a_template' %}", profile: true)
67
- t.render!
68
-
69
- render_children = t.profiler[0].children
70
- render_children.each do |timing|
71
- assert_equal('a_template', timing.partial)
72
- end
73
- assert_equal([1, 2], render_children.map(&:line_number))
74
- end
75
-
76
- def test_profiling_times_the_rendering_of_tokens
77
- t = Template.parse("{% include 'a_template' %}", profile: true)
78
- t.render!
79
-
80
- node = t.profiler[0]
81
- refute_nil(node.render_time)
82
- end
83
-
84
- def test_profiling_times_the_entire_render
85
- t = Template.parse("{% include 'a_template' %}", profile: true)
86
- t.render!
87
-
88
- assert(t.profiler.total_render_time >= 0, "Total render time was not calculated")
89
- end
90
-
91
- class SleepTag < Liquid::Tag
92
- def initialize(tag_name, markup, parse_context)
93
- super
94
- @duration = Float(markup)
95
- end
96
-
97
- def render_to_output_buffer(_context, _output)
98
- sleep(@duration)
99
- end
100
- end
101
-
102
- def test_profiling_multiple_renders
103
- with_custom_tag('sleep', SleepTag) do
104
- context = Liquid::Context.new
105
- t = Liquid::Template.parse("{% sleep 0.001 %}", profile: true)
106
- context.template_name = 'index'
107
- t.render!(context)
108
- context.template_name = 'layout'
109
- first_render_time = context.profiler.total_time
110
- t.render!(context)
111
-
112
- profiler = context.profiler
113
- children = profiler.children
114
- assert_operator(first_render_time, :>=, 0.001)
115
- assert_operator(profiler.total_time, :>=, 0.001 + first_render_time)
116
- assert_equal(["index", "layout"], children.map(&:template_name))
117
- assert_equal([nil, nil], children.map(&:code))
118
- assert_equal(profiler.total_time, children.map(&:total_time).reduce(&:+))
119
- end
120
- end
121
-
122
- def test_profiling_uses_include_to_mark_children
123
- t = Template.parse("{{ 'a string' | upcase }}\n{% include 'a_template' %}", profile: true)
124
- t.render!
125
-
126
- include_node = t.profiler[1]
127
- assert_equal(2, include_node.children.length)
128
- end
129
-
130
- def test_profiling_marks_children_with_the_name_of_included_partial
131
- t = Template.parse("{{ 'a string' | upcase }}\n{% include 'a_template' %}", profile: true)
132
- t.render!
133
-
134
- include_node = t.profiler[1]
135
- include_node.children.each do |child|
136
- assert_equal("a_template", child.partial)
137
- end
138
- end
139
-
140
- def test_profiling_supports_multiple_templates
141
- t = Template.parse("{{ 'a string' | upcase }}\n{% include 'a_template' %}\n{% include 'b_template' %}", profile: true)
142
- t.render!
143
-
144
- a_template = t.profiler[1]
145
- a_template.children.each do |child|
146
- assert_equal("a_template", child.partial)
147
- end
148
-
149
- b_template = t.profiler[2]
150
- b_template.children.each do |child|
151
- assert_equal("b_template", child.partial)
152
- end
153
- end
154
-
155
- def test_profiling_supports_rendering_the_same_partial_multiple_times
156
- t = Template.parse("{{ 'a string' | upcase }}\n{% include 'a_template' %}\n{% include 'a_template' %}", profile: true)
157
- t.render!
158
-
159
- a_template1 = t.profiler[1]
160
- a_template1.children.each do |child|
161
- assert_equal("a_template", child.partial)
162
- end
163
-
164
- a_template2 = t.profiler[2]
165
- a_template2.children.each do |child|
166
- assert_equal("a_template", child.partial)
167
- end
168
- end
169
-
170
- def test_can_iterate_over_each_profiling_entry
171
- t = Template.parse("{{ 'a string' | upcase }}\n{% increment test %}", profile: true)
172
- t.render!
173
-
174
- timing_count = 0
175
- t.profiler.each do |_timing|
176
- timing_count += 1
177
- end
178
-
179
- assert_equal(2, timing_count)
180
- end
181
-
182
- def test_profiling_marks_children_of_if_blocks
183
- t = Template.parse("{% if true %} {% increment test %} {{ test }} {% endif %}", profile: true)
184
- t.render!
185
-
186
- assert_equal(1, t.profiler.length)
187
- assert_equal(2, t.profiler[0].children.length)
188
- end
189
-
190
- def test_profiling_marks_children_of_for_blocks
191
- t = Template.parse("{% for item in collection %} {{ item }} {% endfor %}", profile: true)
192
- t.render!("collection" => ["one", "two"])
193
-
194
- assert_equal(1, t.profiler.length)
195
- # Will profile each invocation of the for block
196
- assert_equal(2, t.profiler[0].children.length)
197
- end
198
-
199
- def test_profiling_supports_self_time
200
- t = Template.parse("{% for item in collection %} {{ item }} {% endfor %}", profile: true)
201
- t.render!("collection" => ["one", "two"])
202
- leaf = t.profiler[0].children[0]
203
-
204
- assert_operator(leaf.self_time, :>, 0)
205
- end
206
-
207
- def test_profiling_supports_total_time
208
- t = Template.parse("{% if true %} {% increment test %} {{ test }} {% endif %}", profile: true)
209
- t.render!
210
-
211
- assert_operator(t.profiler[0].total_time, :>, 0)
212
- end
213
- end