liquid 5.3.0 → 5.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +16 -1
  3. data/README.md +2 -2
  4. data/lib/liquid/block_body.rb +4 -4
  5. data/lib/liquid/context.rb +6 -2
  6. data/lib/liquid/forloop_drop.rb +44 -1
  7. data/lib/liquid/locales/en.yml +6 -5
  8. data/lib/liquid/partial_cache.rb +3 -3
  9. data/lib/liquid/{static_registers.rb → registers.rb} +13 -10
  10. data/lib/liquid/standardfilters.rb +406 -57
  11. data/lib/liquid/strainer_factory.rb +4 -0
  12. data/lib/liquid/strainer_template.rb +4 -0
  13. data/lib/liquid/tablerowloop_drop.rb +58 -1
  14. data/lib/liquid/tags/assign.rb +12 -8
  15. data/lib/liquid/tags/break.rb +8 -0
  16. data/lib/liquid/tags/capture.rb +13 -10
  17. data/lib/liquid/tags/case.rb +21 -0
  18. data/lib/liquid/tags/comment.rb +13 -0
  19. data/lib/liquid/tags/continue.rb +8 -9
  20. data/lib/liquid/tags/cycle.rb +12 -11
  21. data/lib/liquid/tags/decrement.rb +16 -17
  22. data/lib/liquid/tags/echo.rb +16 -9
  23. data/lib/liquid/tags/for.rb +22 -43
  24. data/lib/liquid/tags/if.rb +11 -9
  25. data/lib/liquid/tags/include.rb +15 -13
  26. data/lib/liquid/tags/increment.rb +16 -14
  27. data/lib/liquid/tags/inline_comment.rb +43 -0
  28. data/lib/liquid/tags/raw.rb +11 -0
  29. data/lib/liquid/tags/render.rb +29 -4
  30. data/lib/liquid/tags/table_row.rb +22 -0
  31. data/lib/liquid/tags/unless.rb +15 -4
  32. data/lib/liquid/template.rb +2 -3
  33. data/lib/liquid/variable.rb +4 -4
  34. data/lib/liquid/variable_lookup.rb +10 -7
  35. data/lib/liquid/version.rb +1 -1
  36. data/lib/liquid.rb +2 -2
  37. metadata +7 -123
  38. data/lib/liquid/register.rb +0 -6
  39. data/test/fixtures/en_locale.yml +0 -9
  40. data/test/integration/assign_test.rb +0 -117
  41. data/test/integration/blank_test.rb +0 -109
  42. data/test/integration/block_test.rb +0 -58
  43. data/test/integration/capture_test.rb +0 -58
  44. data/test/integration/context_test.rb +0 -634
  45. data/test/integration/document_test.rb +0 -21
  46. data/test/integration/drop_test.rb +0 -257
  47. data/test/integration/error_handling_test.rb +0 -272
  48. data/test/integration/expression_test.rb +0 -46
  49. data/test/integration/filter_kwarg_test.rb +0 -24
  50. data/test/integration/filter_test.rb +0 -189
  51. data/test/integration/hash_ordering_test.rb +0 -25
  52. data/test/integration/output_test.rb +0 -125
  53. data/test/integration/parsing_quirks_test.rb +0 -134
  54. data/test/integration/profiler_test.rb +0 -240
  55. data/test/integration/security_test.rb +0 -89
  56. data/test/integration/standard_filter_test.rb +0 -925
  57. data/test/integration/tag/disableable_test.rb +0 -59
  58. data/test/integration/tag_test.rb +0 -45
  59. data/test/integration/tags/break_tag_test.rb +0 -17
  60. data/test/integration/tags/continue_tag_test.rb +0 -17
  61. data/test/integration/tags/echo_test.rb +0 -13
  62. data/test/integration/tags/for_tag_test.rb +0 -466
  63. data/test/integration/tags/if_else_tag_test.rb +0 -190
  64. data/test/integration/tags/include_tag_test.rb +0 -269
  65. data/test/integration/tags/increment_tag_test.rb +0 -25
  66. data/test/integration/tags/liquid_tag_test.rb +0 -116
  67. data/test/integration/tags/raw_tag_test.rb +0 -34
  68. data/test/integration/tags/render_tag_test.rb +0 -213
  69. data/test/integration/tags/standard_tag_test.rb +0 -303
  70. data/test/integration/tags/statements_test.rb +0 -113
  71. data/test/integration/tags/table_row_test.rb +0 -66
  72. data/test/integration/tags/unless_else_tag_test.rb +0 -28
  73. data/test/integration/template_test.rb +0 -340
  74. data/test/integration/trim_mode_test.rb +0 -563
  75. data/test/integration/variable_test.rb +0 -138
  76. data/test/test_helper.rb +0 -207
  77. data/test/unit/block_unit_test.rb +0 -53
  78. data/test/unit/condition_unit_test.rb +0 -181
  79. data/test/unit/file_system_unit_test.rb +0 -37
  80. data/test/unit/i18n_unit_test.rb +0 -39
  81. data/test/unit/lexer_unit_test.rb +0 -53
  82. data/test/unit/parse_tree_visitor_test.rb +0 -261
  83. data/test/unit/parser_unit_test.rb +0 -84
  84. data/test/unit/partial_cache_unit_test.rb +0 -128
  85. data/test/unit/regexp_unit_test.rb +0 -46
  86. data/test/unit/static_registers_unit_test.rb +0 -156
  87. data/test/unit/strainer_factory_unit_test.rb +0 -101
  88. data/test/unit/strainer_template_unit_test.rb +0 -82
  89. data/test/unit/tag_unit_test.rb +0 -23
  90. data/test/unit/tags/case_tag_unit_test.rb +0 -12
  91. data/test/unit/tags/for_tag_unit_test.rb +0 -15
  92. data/test/unit/tags/if_tag_unit_test.rb +0 -10
  93. data/test/unit/template_factory_unit_test.rb +0 -12
  94. data/test/unit/template_unit_test.rb +0 -87
  95. data/test/unit/tokenizer_unit_test.rb +0 -62
  96. 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