liquid 3.0.6 → 4.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.
- checksums.yaml +4 -4
- data/History.md +98 -58
- data/README.md +31 -0
- data/lib/liquid/block.rb +31 -124
- data/lib/liquid/block_body.rb +75 -59
- data/lib/liquid/condition.rb +23 -22
- data/lib/liquid/context.rb +50 -46
- data/lib/liquid/document.rb +19 -9
- data/lib/liquid/drop.rb +17 -16
- data/lib/liquid/errors.rb +20 -24
- data/lib/liquid/expression.rb +15 -3
- data/lib/liquid/extensions.rb +13 -7
- data/lib/liquid/file_system.rb +11 -11
- data/lib/liquid/forloop_drop.rb +42 -0
- data/lib/liquid/i18n.rb +5 -5
- data/lib/liquid/interrupts.rb +1 -2
- data/lib/liquid/lexer.rb +6 -4
- data/lib/liquid/locales/en.yml +5 -1
- data/lib/liquid/parse_context.rb +37 -0
- data/lib/liquid/parser_switching.rb +4 -4
- data/lib/liquid/profiler/hooks.rb +7 -7
- data/lib/liquid/profiler.rb +18 -19
- data/lib/liquid/range_lookup.rb +16 -1
- data/lib/liquid/resource_limits.rb +23 -0
- data/lib/liquid/standardfilters.rb +121 -61
- data/lib/liquid/strainer.rb +14 -7
- data/lib/liquid/tablerowloop_drop.rb +62 -0
- data/lib/liquid/tag.rb +9 -8
- data/lib/liquid/tags/assign.rb +17 -4
- data/lib/liquid/tags/break.rb +0 -3
- data/lib/liquid/tags/capture.rb +1 -1
- data/lib/liquid/tags/case.rb +19 -12
- data/lib/liquid/tags/comment.rb +2 -2
- data/lib/liquid/tags/cycle.rb +6 -6
- data/lib/liquid/tags/decrement.rb +1 -4
- data/lib/liquid/tags/for.rb +95 -75
- data/lib/liquid/tags/if.rb +49 -44
- data/lib/liquid/tags/ifchanged.rb +0 -2
- data/lib/liquid/tags/include.rb +61 -52
- data/lib/liquid/tags/raw.rb +32 -4
- data/lib/liquid/tags/table_row.rb +12 -30
- data/lib/liquid/tags/unless.rb +3 -4
- data/lib/liquid/template.rb +42 -54
- data/lib/liquid/tokenizer.rb +31 -0
- data/lib/liquid/utils.rb +52 -8
- data/lib/liquid/variable.rb +46 -45
- data/lib/liquid/variable_lookup.rb +7 -5
- data/lib/liquid/version.rb +1 -1
- data/lib/liquid.rb +9 -7
- data/test/integration/assign_test.rb +8 -8
- data/test/integration/blank_test.rb +14 -14
- data/test/integration/context_test.rb +2 -2
- data/test/integration/document_test.rb +19 -0
- data/test/integration/drop_test.rb +42 -40
- data/test/integration/error_handling_test.rb +99 -46
- data/test/integration/filter_test.rb +60 -20
- data/test/integration/hash_ordering_test.rb +9 -9
- data/test/integration/output_test.rb +26 -27
- data/test/integration/parsing_quirks_test.rb +15 -13
- data/test/integration/render_profiling_test.rb +20 -20
- data/test/integration/security_test.rb +9 -7
- data/test/integration/standard_filter_test.rb +179 -40
- data/test/integration/tags/break_tag_test.rb +1 -2
- data/test/integration/tags/continue_tag_test.rb +0 -1
- data/test/integration/tags/for_tag_test.rb +133 -98
- data/test/integration/tags/if_else_tag_test.rb +75 -77
- data/test/integration/tags/include_tag_test.rb +34 -30
- data/test/integration/tags/increment_tag_test.rb +10 -11
- data/test/integration/tags/raw_tag_test.rb +7 -1
- data/test/integration/tags/standard_tag_test.rb +121 -122
- data/test/integration/tags/statements_test.rb +3 -5
- data/test/integration/tags/table_row_test.rb +20 -19
- data/test/integration/tags/unless_else_tag_test.rb +6 -6
- data/test/integration/template_test.rb +190 -49
- data/test/integration/trim_mode_test.rb +525 -0
- data/test/integration/variable_test.rb +23 -13
- data/test/test_helper.rb +33 -5
- data/test/unit/block_unit_test.rb +8 -5
- data/test/unit/condition_unit_test.rb +86 -77
- data/test/unit/context_unit_test.rb +48 -57
- data/test/unit/file_system_unit_test.rb +3 -3
- data/test/unit/i18n_unit_test.rb +2 -2
- data/test/unit/lexer_unit_test.rb +11 -8
- data/test/unit/parser_unit_test.rb +2 -2
- data/test/unit/regexp_unit_test.rb +1 -1
- data/test/unit/strainer_unit_test.rb +80 -1
- data/test/unit/tag_unit_test.rb +7 -2
- data/test/unit/tags/case_tag_unit_test.rb +1 -1
- data/test/unit/tags/for_tag_unit_test.rb +2 -2
- data/test/unit/tags/if_tag_unit_test.rb +1 -1
- data/test/unit/template_unit_test.rb +14 -5
- data/test/unit/tokenizer_unit_test.rb +24 -7
- data/test/unit/variable_unit_test.rb +60 -43
- metadata +19 -14
- data/lib/liquid/module_ex.rb +0 -62
- data/lib/liquid/token.rb +0 -18
- data/test/unit/module_ex_unit_test.rb +0 -87
- /data/{MIT-LICENSE → LICENSE} +0 -0
@@ -4,7 +4,7 @@ class RenderProfilingTest < Minitest::Test
|
|
4
4
|
include Liquid
|
5
5
|
|
6
6
|
class ProfilingFileSystem
|
7
|
-
def read_template_file(template_path
|
7
|
+
def read_template_file(template_path)
|
8
8
|
"Rendering template {% assign template_name = '#{template_path}'%}\n{{ template_name }}"
|
9
9
|
end
|
10
10
|
end
|
@@ -21,7 +21,7 @@ class RenderProfilingTest < Minitest::Test
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def test_parse_makes_available_simple_profiling
|
24
|
-
t = Template.parse("{{ 'a string' | upcase }}", :
|
24
|
+
t = Template.parse("{{ 'a string' | upcase }}", profile: true)
|
25
25
|
t.render!
|
26
26
|
|
27
27
|
assert_equal 1, t.profiler.length
|
@@ -31,14 +31,14 @@ class RenderProfilingTest < Minitest::Test
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def test_render_ignores_raw_strings_when_profiling
|
34
|
-
t = Template.parse("This is raw string\nstuff\nNewline", :
|
34
|
+
t = Template.parse("This is raw string\nstuff\nNewline", profile: true)
|
35
35
|
t.render!
|
36
36
|
|
37
37
|
assert_equal 0, t.profiler.length
|
38
38
|
end
|
39
39
|
|
40
40
|
def test_profiling_includes_line_numbers_of_liquid_nodes
|
41
|
-
t = Template.parse("{{ 'a string' | upcase }}\n{% increment test %}", :
|
41
|
+
t = Template.parse("{{ 'a string' | upcase }}\n{% increment test %}", profile: true)
|
42
42
|
t.render!
|
43
43
|
assert_equal 2, t.profiler.length
|
44
44
|
|
@@ -49,7 +49,7 @@ class RenderProfilingTest < Minitest::Test
|
|
49
49
|
end
|
50
50
|
|
51
51
|
def test_profiling_includes_line_numbers_of_included_partials
|
52
|
-
t = Template.parse("{% include 'a_template' %}", :
|
52
|
+
t = Template.parse("{% include 'a_template' %}", profile: true)
|
53
53
|
t.render!
|
54
54
|
|
55
55
|
included_children = t.profiler[0].children
|
@@ -61,7 +61,7 @@ class RenderProfilingTest < Minitest::Test
|
|
61
61
|
end
|
62
62
|
|
63
63
|
def test_profiling_times_the_rendering_of_tokens
|
64
|
-
t = Template.parse("{% include 'a_template' %}", :
|
64
|
+
t = Template.parse("{% include 'a_template' %}", profile: true)
|
65
65
|
t.render!
|
66
66
|
|
67
67
|
node = t.profiler[0]
|
@@ -69,14 +69,14 @@ class RenderProfilingTest < Minitest::Test
|
|
69
69
|
end
|
70
70
|
|
71
71
|
def test_profiling_times_the_entire_render
|
72
|
-
t = Template.parse("{% include 'a_template' %}", :
|
72
|
+
t = Template.parse("{% include 'a_template' %}", profile: true)
|
73
73
|
t.render!
|
74
74
|
|
75
75
|
assert t.profiler.total_render_time >= 0, "Total render time was not calculated"
|
76
76
|
end
|
77
77
|
|
78
78
|
def test_profiling_uses_include_to_mark_children
|
79
|
-
t = Template.parse("{{ 'a string' | upcase }}\n{% include 'a_template' %}", :
|
79
|
+
t = Template.parse("{{ 'a string' | upcase }}\n{% include 'a_template' %}", profile: true)
|
80
80
|
t.render!
|
81
81
|
|
82
82
|
include_node = t.profiler[1]
|
@@ -84,47 +84,47 @@ class RenderProfilingTest < Minitest::Test
|
|
84
84
|
end
|
85
85
|
|
86
86
|
def test_profiling_marks_children_with_the_name_of_included_partial
|
87
|
-
t = Template.parse("{{ 'a string' | upcase }}\n{% include 'a_template' %}", :
|
87
|
+
t = Template.parse("{{ 'a string' | upcase }}\n{% include 'a_template' %}", profile: true)
|
88
88
|
t.render!
|
89
89
|
|
90
90
|
include_node = t.profiler[1]
|
91
91
|
include_node.children.each do |child|
|
92
|
-
assert_equal "
|
92
|
+
assert_equal "a_template", child.partial
|
93
93
|
end
|
94
94
|
end
|
95
95
|
|
96
96
|
def test_profiling_supports_multiple_templates
|
97
|
-
t = Template.parse("{{ 'a string' | upcase }}\n{% include 'a_template' %}\n{% include 'b_template' %}", :
|
97
|
+
t = Template.parse("{{ 'a string' | upcase }}\n{% include 'a_template' %}\n{% include 'b_template' %}", profile: true)
|
98
98
|
t.render!
|
99
99
|
|
100
100
|
a_template = t.profiler[1]
|
101
101
|
a_template.children.each do |child|
|
102
|
-
assert_equal "
|
102
|
+
assert_equal "a_template", child.partial
|
103
103
|
end
|
104
104
|
|
105
105
|
b_template = t.profiler[2]
|
106
106
|
b_template.children.each do |child|
|
107
|
-
assert_equal "
|
107
|
+
assert_equal "b_template", child.partial
|
108
108
|
end
|
109
109
|
end
|
110
110
|
|
111
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' %}", :
|
112
|
+
t = Template.parse("{{ 'a string' | upcase }}\n{% include 'a_template' %}\n{% include 'a_template' %}", profile: true)
|
113
113
|
t.render!
|
114
114
|
|
115
115
|
a_template1 = t.profiler[1]
|
116
116
|
a_template1.children.each do |child|
|
117
|
-
assert_equal "
|
117
|
+
assert_equal "a_template", child.partial
|
118
118
|
end
|
119
119
|
|
120
120
|
a_template2 = t.profiler[2]
|
121
121
|
a_template2.children.each do |child|
|
122
|
-
assert_equal "
|
122
|
+
assert_equal "a_template", child.partial
|
123
123
|
end
|
124
124
|
end
|
125
125
|
|
126
126
|
def test_can_iterate_over_each_profiling_entry
|
127
|
-
t = Template.parse("{{ 'a string' | upcase }}\n{% increment test %}", :
|
127
|
+
t = Template.parse("{{ 'a string' | upcase }}\n{% increment test %}", profile: true)
|
128
128
|
t.render!
|
129
129
|
|
130
130
|
timing_count = 0
|
@@ -136,7 +136,7 @@ class RenderProfilingTest < Minitest::Test
|
|
136
136
|
end
|
137
137
|
|
138
138
|
def test_profiling_marks_children_of_if_blocks
|
139
|
-
t = Template.parse("{% if true %} {% increment test %} {{ test }} {% endif %}", :
|
139
|
+
t = Template.parse("{% if true %} {% increment test %} {{ test }} {% endif %}", profile: true)
|
140
140
|
t.render!
|
141
141
|
|
142
142
|
assert_equal 1, t.profiler.length
|
@@ -144,8 +144,8 @@ class RenderProfilingTest < Minitest::Test
|
|
144
144
|
end
|
145
145
|
|
146
146
|
def test_profiling_marks_children_of_for_blocks
|
147
|
-
t = Template.parse("{% for item in collection %} {{ item }} {% endfor %}", :
|
148
|
-
t.render!({"collection" => ["one", "two"]})
|
147
|
+
t = Template.parse("{% for item in collection %} {{ item }} {% endfor %}", profile: true)
|
148
|
+
t.render!({ "collection" => ["one", "two"] })
|
149
149
|
|
150
150
|
assert_equal 1, t.profiler.length
|
151
151
|
# Will profile each invocation of the for block
|
@@ -9,34 +9,36 @@ end
|
|
9
9
|
class SecurityTest < Minitest::Test
|
10
10
|
include Liquid
|
11
11
|
|
12
|
+
def setup
|
13
|
+
@assigns = {}
|
14
|
+
end
|
15
|
+
|
12
16
|
def test_no_instance_eval
|
13
17
|
text = %( {{ '1+1' | instance_eval }} )
|
14
|
-
expected =
|
18
|
+
expected = %( 1+1 )
|
15
19
|
|
16
20
|
assert_equal expected, Template.parse(text).render!(@assigns)
|
17
21
|
end
|
18
22
|
|
19
23
|
def test_no_existing_instance_eval
|
20
24
|
text = %( {{ '1+1' | __instance_eval__ }} )
|
21
|
-
expected =
|
25
|
+
expected = %( 1+1 )
|
22
26
|
|
23
27
|
assert_equal expected, Template.parse(text).render!(@assigns)
|
24
28
|
end
|
25
29
|
|
26
|
-
|
27
30
|
def test_no_instance_eval_after_mixing_in_new_filter
|
28
31
|
text = %( {{ '1+1' | instance_eval }} )
|
29
|
-
expected =
|
32
|
+
expected = %( 1+1 )
|
30
33
|
|
31
34
|
assert_equal expected, Template.parse(text).render!(@assigns)
|
32
35
|
end
|
33
36
|
|
34
|
-
|
35
37
|
def test_no_instance_eval_later_in_chain
|
36
38
|
text = %( {{ '1+1' | add_one | instance_eval }} )
|
37
|
-
expected =
|
39
|
+
expected = %( 1+1 + 1 )
|
38
40
|
|
39
|
-
assert_equal expected, Template.parse(text).render!(@assigns, :
|
41
|
+
assert_equal expected, Template.parse(text).render!(@assigns, filters: SecurityFilter)
|
40
42
|
end
|
41
43
|
|
42
44
|
def test_does_not_add_filters_to_symbol_table
|
@@ -41,6 +41,16 @@ class TestEnumerable < Liquid::Drop
|
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
|
+
class NumberLikeThing < Liquid::Drop
|
45
|
+
def initialize(amount)
|
46
|
+
@amount = amount
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_number
|
50
|
+
@amount
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
44
54
|
class StandardFiltersTest < Minitest::Test
|
45
55
|
include Liquid
|
46
56
|
|
@@ -49,7 +59,7 @@ class StandardFiltersTest < Minitest::Test
|
|
49
59
|
end
|
50
60
|
|
51
61
|
def test_size
|
52
|
-
assert_equal 3, @filters.size([1,2,3])
|
62
|
+
assert_equal 3, @filters.size([1, 2, 3])
|
53
63
|
assert_equal 0, @filters.size([])
|
54
64
|
assert_equal 0, @filters.size(nil)
|
55
65
|
end
|
@@ -76,20 +86,27 @@ class StandardFiltersTest < Minitest::Test
|
|
76
86
|
assert_equal '', @filters.slice(nil, 0)
|
77
87
|
assert_equal '', @filters.slice('foobar', 100, 10)
|
78
88
|
assert_equal '', @filters.slice('foobar', -100, 10)
|
89
|
+
assert_equal 'oob', @filters.slice('foobar', '1', '3')
|
90
|
+
assert_raises(Liquid::ArgumentError) do
|
91
|
+
@filters.slice('foobar', nil)
|
92
|
+
end
|
93
|
+
assert_raises(Liquid::ArgumentError) do
|
94
|
+
@filters.slice('foobar', 0, "")
|
95
|
+
end
|
79
96
|
end
|
80
97
|
|
81
98
|
def test_slice_on_arrays
|
82
99
|
input = 'foobar'.split(//)
|
83
|
-
assert_equal %w
|
84
|
-
assert_equal %w
|
85
|
-
assert_equal %w
|
86
|
-
assert_equal %w
|
87
|
-
assert_equal %w
|
88
|
-
assert_equal %w
|
89
|
-
assert_equal %w
|
90
|
-
assert_equal %w
|
91
|
-
assert_equal %w
|
92
|
-
assert_equal %w
|
100
|
+
assert_equal %w(o o b), @filters.slice(input, 1, 3)
|
101
|
+
assert_equal %w(o o b a r), @filters.slice(input, 1, 1000)
|
102
|
+
assert_equal %w(), @filters.slice(input, 1, 0)
|
103
|
+
assert_equal %w(o), @filters.slice(input, 1, 1)
|
104
|
+
assert_equal %w(b a r), @filters.slice(input, 3, 3)
|
105
|
+
assert_equal %w(a r), @filters.slice(input, -2, 2)
|
106
|
+
assert_equal %w(a r), @filters.slice(input, -2, 1000)
|
107
|
+
assert_equal %w(r), @filters.slice(input, -1)
|
108
|
+
assert_equal %w(), @filters.slice(input, 100, 10)
|
109
|
+
assert_equal %w(), @filters.slice(input, -100, 10)
|
93
110
|
end
|
94
111
|
|
95
112
|
def test_truncate
|
@@ -98,19 +115,20 @@ class StandardFiltersTest < Minitest::Test
|
|
98
115
|
assert_equal '...', @filters.truncate('1234567890', 0)
|
99
116
|
assert_equal '1234567890', @filters.truncate('1234567890')
|
100
117
|
assert_equal "测试...", @filters.truncate("测试测试测试测试", 5)
|
118
|
+
assert_equal '12341', @filters.truncate("1234567890", 5, 1)
|
101
119
|
end
|
102
120
|
|
103
121
|
def test_split
|
104
|
-
assert_equal ['12','34'], @filters.split('12~34', '~')
|
105
|
-
assert_equal ['A? ',' ,Z'], @filters.split('A? ~ ~ ~ ,Z', '~ ~ ~')
|
122
|
+
assert_equal ['12', '34'], @filters.split('12~34', '~')
|
123
|
+
assert_equal ['A? ', ' ,Z'], @filters.split('A? ~ ~ ~ ,Z', '~ ~ ~')
|
106
124
|
assert_equal ['A?Z'], @filters.split('A?Z', '~')
|
107
|
-
# Regexp works although Liquid does not support.
|
108
|
-
assert_equal ['A','Z'], @filters.split('AxZ', /x/)
|
109
125
|
assert_equal [], @filters.split(nil, ' ')
|
126
|
+
assert_equal ['A', 'Z'], @filters.split('A1Z', 1)
|
110
127
|
end
|
111
128
|
|
112
129
|
def test_escape
|
113
130
|
assert_equal '<strong>', @filters.escape('<strong>')
|
131
|
+
assert_equal nil, @filters.escape(nil)
|
114
132
|
assert_equal '<strong>', @filters.h('<strong>')
|
115
133
|
end
|
116
134
|
|
@@ -123,12 +141,20 @@ class StandardFiltersTest < Minitest::Test
|
|
123
141
|
assert_equal nil, @filters.url_encode(nil)
|
124
142
|
end
|
125
143
|
|
144
|
+
def test_url_decode
|
145
|
+
assert_equal 'foo bar', @filters.url_decode('foo+bar')
|
146
|
+
assert_equal 'foo bar', @filters.url_decode('foo%20bar')
|
147
|
+
assert_equal 'foo+1@example.com', @filters.url_decode('foo%2B1%40example.com')
|
148
|
+
assert_equal nil, @filters.url_decode(nil)
|
149
|
+
end
|
150
|
+
|
126
151
|
def test_truncatewords
|
127
152
|
assert_equal 'one two three', @filters.truncatewords('one two three', 4)
|
128
153
|
assert_equal 'one two...', @filters.truncatewords('one two three', 2)
|
129
154
|
assert_equal 'one two three', @filters.truncatewords('one two three')
|
130
155
|
assert_equal 'Two small (13” x 5.5” x 10” high) baskets fit inside one large basket (13”...', @filters.truncatewords('Two small (13” x 5.5” x 10” high) baskets fit inside one large basket (13” x 16” x 10.5” high) with cover.', 15)
|
131
156
|
assert_equal "测试测试测试测试", @filters.truncatewords('测试测试测试测试', 5)
|
157
|
+
assert_equal 'one two1', @filters.truncatewords("one two three", 2, 1)
|
132
158
|
end
|
133
159
|
|
134
160
|
def test_strip_html
|
@@ -142,45 +168,80 @@ class StandardFiltersTest < Minitest::Test
|
|
142
168
|
end
|
143
169
|
|
144
170
|
def test_join
|
145
|
-
assert_equal '1 2 3 4', @filters.join([1,2,3,4])
|
146
|
-
assert_equal '1 - 2 - 3 - 4', @filters.join([1,2,3,4], ' - ')
|
171
|
+
assert_equal '1 2 3 4', @filters.join([1, 2, 3, 4])
|
172
|
+
assert_equal '1 - 2 - 3 - 4', @filters.join([1, 2, 3, 4], ' - ')
|
147
173
|
end
|
148
174
|
|
149
175
|
def test_sort
|
150
|
-
assert_equal [1,2,3,4], @filters.sort([4,3,2,1])
|
151
|
-
assert_equal [{"a" => 1}, {"a" => 2}, {"a" => 3}, {"a" => 4}], @filters.sort([{"a" => 4}, {"a" => 3}, {"a" => 1}, {"a" => 2}], "a")
|
176
|
+
assert_equal [1, 2, 3, 4], @filters.sort([4, 3, 2, 1])
|
177
|
+
assert_equal [{ "a" => 1 }, { "a" => 2 }, { "a" => 3 }, { "a" => 4 }], @filters.sort([{ "a" => 4 }, { "a" => 3 }, { "a" => 1 }, { "a" => 2 }], "a")
|
178
|
+
end
|
179
|
+
|
180
|
+
def test_sort_when_property_is_sometimes_missing_puts_nils_last
|
181
|
+
input = [
|
182
|
+
{ "price" => 4, "handle" => "alpha" },
|
183
|
+
{ "handle" => "beta" },
|
184
|
+
{ "price" => 1, "handle" => "gamma" },
|
185
|
+
{ "handle" => "delta" },
|
186
|
+
{ "price" => 2, "handle" => "epsilon" }
|
187
|
+
]
|
188
|
+
expectation = [
|
189
|
+
{ "price" => 1, "handle" => "gamma" },
|
190
|
+
{ "price" => 2, "handle" => "epsilon" },
|
191
|
+
{ "price" => 4, "handle" => "alpha" },
|
192
|
+
{ "handle" => "delta" },
|
193
|
+
{ "handle" => "beta" }
|
194
|
+
]
|
195
|
+
assert_equal expectation, @filters.sort(input, "price")
|
196
|
+
end
|
197
|
+
|
198
|
+
def test_sort_empty_array
|
199
|
+
assert_equal [], @filters.sort([], "a")
|
200
|
+
end
|
201
|
+
|
202
|
+
def test_sort_natural_empty_array
|
203
|
+
assert_equal [], @filters.sort_natural([], "a")
|
152
204
|
end
|
153
205
|
|
154
206
|
def test_legacy_sort_hash
|
155
|
-
assert_equal [{a:1, b:2}], @filters.sort({a:1, b:2})
|
207
|
+
assert_equal [{ a: 1, b: 2 }], @filters.sort({ a: 1, b: 2 })
|
156
208
|
end
|
157
209
|
|
158
210
|
def test_numerical_vs_lexicographical_sort
|
159
211
|
assert_equal [2, 10], @filters.sort([10, 2])
|
160
|
-
assert_equal [{"a" => 2}, {"a" => 10}], @filters.sort([{"a" => 10}, {"a" => 2}], "a")
|
212
|
+
assert_equal [{ "a" => 2 }, { "a" => 10 }], @filters.sort([{ "a" => 10 }, { "a" => 2 }], "a")
|
161
213
|
assert_equal ["10", "2"], @filters.sort(["10", "2"])
|
162
|
-
assert_equal [{"a" => "10"}, {"a" => "2"}], @filters.sort([{"a" => "10"}, {"a" => "2"}], "a")
|
214
|
+
assert_equal [{ "a" => "10" }, { "a" => "2" }], @filters.sort([{ "a" => "10" }, { "a" => "2" }], "a")
|
163
215
|
end
|
164
216
|
|
165
217
|
def test_uniq
|
166
|
-
assert_equal [
|
167
|
-
assert_equal [
|
218
|
+
assert_equal ["foo"], @filters.uniq("foo")
|
219
|
+
assert_equal [1, 3, 2, 4], @filters.uniq([1, 1, 3, 2, 3, 1, 4, 3, 2, 1])
|
220
|
+
assert_equal [{ "a" => 1 }, { "a" => 3 }, { "a" => 2 }], @filters.uniq([{ "a" => 1 }, { "a" => 3 }, { "a" => 1 }, { "a" => 2 }], "a")
|
168
221
|
testdrop = TestDrop.new
|
169
222
|
assert_equal [testdrop], @filters.uniq([testdrop, TestDrop.new], 'test')
|
170
223
|
end
|
171
224
|
|
225
|
+
def test_uniq_empty_array
|
226
|
+
assert_equal [], @filters.uniq([], "a")
|
227
|
+
end
|
228
|
+
|
229
|
+
def test_compact_empty_array
|
230
|
+
assert_equal [], @filters.compact([], "a")
|
231
|
+
end
|
232
|
+
|
172
233
|
def test_reverse
|
173
|
-
assert_equal [4,3,2,1], @filters.reverse([1,2,3,4])
|
234
|
+
assert_equal [4, 3, 2, 1], @filters.reverse([1, 2, 3, 4])
|
174
235
|
end
|
175
236
|
|
176
237
|
def test_legacy_reverse_hash
|
177
|
-
assert_equal [{a:1, b:2}], @filters.reverse(a:1, b:2)
|
238
|
+
assert_equal [{ a: 1, b: 2 }], @filters.reverse(a: 1, b: 2)
|
178
239
|
end
|
179
240
|
|
180
241
|
def test_map
|
181
|
-
assert_equal [1,2,3,4], @filters.map([{"a" => 1}, {"a" => 2}, {"a" => 3}, {"a" => 4}], 'a')
|
242
|
+
assert_equal [1, 2, 3, 4], @filters.map([{ "a" => 1 }, { "a" => 2 }, { "a" => 3 }, { "a" => 4 }], 'a')
|
182
243
|
assert_template_result 'abc', "{{ ary | map:'foo' | map:'bar' }}",
|
183
|
-
'ary' => [{'foo' => {'bar' => 'a'}}, {'foo' => {'bar' => 'b'}}, {'foo' => {'bar' => 'c'}}]
|
244
|
+
'ary' => [{ 'foo' => { 'bar' => 'a' } }, { 'foo' => { 'bar' => 'b' } }, { 'foo' => { 'bar' => 'c' } }]
|
184
245
|
end
|
185
246
|
|
186
247
|
def test_map_doesnt_call_arbitrary_stuff
|
@@ -212,11 +273,24 @@ class StandardFiltersTest < Minitest::Test
|
|
212
273
|
|
213
274
|
def test_map_over_proc
|
214
275
|
drop = TestDrop.new
|
215
|
-
p =
|
276
|
+
p = proc{ drop }
|
216
277
|
templ = '{{ procs | map: "test" }}'
|
217
278
|
assert_template_result "testfoo", templ, "procs" => [p]
|
218
279
|
end
|
219
280
|
|
281
|
+
def test_map_over_drops_returning_procs
|
282
|
+
drops = [
|
283
|
+
{
|
284
|
+
"proc" => ->{ "foo" },
|
285
|
+
},
|
286
|
+
{
|
287
|
+
"proc" => ->{ "bar" },
|
288
|
+
},
|
289
|
+
]
|
290
|
+
templ = '{{ drops | map: "proc" }}'
|
291
|
+
assert_template_result "foobar", templ, "drops" => drops
|
292
|
+
end
|
293
|
+
|
220
294
|
def test_map_works_on_enumerables
|
221
295
|
assert_template_result "123", '{{ foo | map: "foo" }}', "foo" => TestEnumerable.new
|
222
296
|
end
|
@@ -230,6 +304,10 @@ class StandardFiltersTest < Minitest::Test
|
|
230
304
|
assert_template_result 'foobar', '{{ foo | last }}', 'foo' => [ThingWithToLiquid.new]
|
231
305
|
end
|
232
306
|
|
307
|
+
def test_truncate_calls_to_liquid
|
308
|
+
assert_template_result "wo...", '{{ foo | truncate: 5 }}', "foo" => TestThing.new
|
309
|
+
end
|
310
|
+
|
233
311
|
def test_date
|
234
312
|
assert_equal 'May', @filters.date(Time.parse("2006-05-05 10:00:00"), "%B")
|
235
313
|
assert_equal 'June', @filters.date(Time.parse("2006-06-05 10:00:00"), "%B")
|
@@ -249,9 +327,12 @@ class StandardFiltersTest < Minitest::Test
|
|
249
327
|
assert_equal "07/16/2004", @filters.date("Fri Jul 16 01:00:00 2004", "%m/%d/%Y")
|
250
328
|
assert_equal "#{Date.today.year}", @filters.date('now', '%Y')
|
251
329
|
assert_equal "#{Date.today.year}", @filters.date('today', '%Y')
|
330
|
+
assert_equal "#{Date.today.year}", @filters.date('Today', '%Y')
|
252
331
|
|
253
332
|
assert_equal nil, @filters.date(nil, "%B")
|
254
333
|
|
334
|
+
assert_equal '', @filters.date('', "%B")
|
335
|
+
|
255
336
|
with_timezone("UTC") do
|
256
337
|
assert_equal "07/05/2006", @filters.date(1152098955, "%m/%d/%Y")
|
257
338
|
assert_equal "07/05/2006", @filters.date("1152098955", "%m/%d/%Y")
|
@@ -259,21 +340,25 @@ class StandardFiltersTest < Minitest::Test
|
|
259
340
|
end
|
260
341
|
|
261
342
|
def test_first_last
|
262
|
-
assert_equal 1, @filters.first([1,2,3])
|
263
|
-
assert_equal 3, @filters.last([1,2,3])
|
343
|
+
assert_equal 1, @filters.first([1, 2, 3])
|
344
|
+
assert_equal 3, @filters.last([1, 2, 3])
|
264
345
|
assert_equal nil, @filters.first([])
|
265
346
|
assert_equal nil, @filters.last([])
|
266
347
|
end
|
267
348
|
|
268
349
|
def test_replace
|
269
350
|
assert_equal '2 2 2 2', @filters.replace('1 1 1 1', '1', 2)
|
351
|
+
assert_equal '2 2 2 2', @filters.replace('1 1 1 1', 1, 2)
|
270
352
|
assert_equal '2 1 1 1', @filters.replace_first('1 1 1 1', '1', 2)
|
353
|
+
assert_equal '2 1 1 1', @filters.replace_first('1 1 1 1', 1, 2)
|
271
354
|
assert_template_result '2 1 1 1', "{{ '1 1 1 1' | replace_first: '1', 2 }}"
|
272
355
|
end
|
273
356
|
|
274
357
|
def test_remove
|
275
358
|
assert_equal ' ', @filters.remove("a a a a", 'a')
|
359
|
+
assert_equal ' ', @filters.remove("1 1 1 1", 1)
|
276
360
|
assert_equal 'a a a', @filters.remove_first("a a a a", 'a ')
|
361
|
+
assert_equal ' 1 1 1', @filters.remove_first("1 1 1 1", 1)
|
277
362
|
assert_template_result 'a a a', "{{ 'a a a a' | remove_first: 'a ' }}"
|
278
363
|
end
|
279
364
|
|
@@ -308,20 +393,38 @@ class StandardFiltersTest < Minitest::Test
|
|
308
393
|
def test_plus
|
309
394
|
assert_template_result "2", "{{ 1 | plus:1 }}"
|
310
395
|
assert_template_result "2.0", "{{ '1' | plus:'1.0' }}"
|
396
|
+
|
397
|
+
assert_template_result "5", "{{ price | plus:'2' }}", 'price' => NumberLikeThing.new(3)
|
311
398
|
end
|
312
399
|
|
313
400
|
def test_minus
|
314
401
|
assert_template_result "4", "{{ input | minus:operand }}", 'input' => 5, 'operand' => 1
|
315
402
|
assert_template_result "2.3", "{{ '4.3' | minus:'2' }}"
|
403
|
+
|
404
|
+
assert_template_result "5", "{{ price | minus:'2' }}", 'price' => NumberLikeThing.new(7)
|
405
|
+
end
|
406
|
+
|
407
|
+
def test_abs
|
408
|
+
assert_template_result "17", "{{ 17 | abs }}"
|
409
|
+
assert_template_result "17", "{{ -17 | abs }}"
|
410
|
+
assert_template_result "17", "{{ '17' | abs }}"
|
411
|
+
assert_template_result "17", "{{ '-17' | abs }}"
|
412
|
+
assert_template_result "0", "{{ 0 | abs }}"
|
413
|
+
assert_template_result "0", "{{ '0' | abs }}"
|
414
|
+
assert_template_result "17.42", "{{ 17.42 | abs }}"
|
415
|
+
assert_template_result "17.42", "{{ -17.42 | abs }}"
|
416
|
+
assert_template_result "17.42", "{{ '17.42' | abs }}"
|
417
|
+
assert_template_result "17.42", "{{ '-17.42' | abs }}"
|
316
418
|
end
|
317
419
|
|
318
420
|
def test_times
|
319
421
|
assert_template_result "12", "{{ 3 | times:4 }}"
|
320
422
|
assert_template_result "0", "{{ 'foo' | times:4 }}"
|
321
|
-
|
322
423
|
assert_template_result "6", "{{ '2.1' | times:3 | replace: '.','-' | plus:0}}"
|
323
|
-
|
324
424
|
assert_template_result "7.25", "{{ 0.0725 | times:100 }}"
|
425
|
+
assert_template_result "-7.25", '{{ "-0.0725" | times:100 }}'
|
426
|
+
assert_template_result "7.25", '{{ "-0.0725" | times: -100 }}'
|
427
|
+
assert_template_result "4", "{{ price | times:2 }}", 'price' => NumberLikeThing.new(2)
|
325
428
|
end
|
326
429
|
|
327
430
|
def test_divided_by
|
@@ -332,38 +435,74 @@ class StandardFiltersTest < Minitest::Test
|
|
332
435
|
assert_equal "Liquid error: divided by 0", Template.parse("{{ 5 | divided_by:0 }}").render
|
333
436
|
|
334
437
|
assert_template_result "0.5", "{{ 2.0 | divided_by:4 }}"
|
438
|
+
assert_raises(Liquid::ZeroDivisionError) do
|
439
|
+
assert_template_result "4", "{{ 1 | modulo: 0 }}"
|
440
|
+
end
|
441
|
+
|
442
|
+
assert_template_result "5", "{{ price | divided_by:2 }}", 'price' => NumberLikeThing.new(10)
|
335
443
|
end
|
336
444
|
|
337
445
|
def test_modulo
|
338
446
|
assert_template_result "1", "{{ 3 | modulo:2 }}"
|
447
|
+
assert_raises(Liquid::ZeroDivisionError) do
|
448
|
+
assert_template_result "4", "{{ 1 | modulo: 0 }}"
|
449
|
+
end
|
450
|
+
|
451
|
+
assert_template_result "1", "{{ price | modulo:2 }}", 'price' => NumberLikeThing.new(3)
|
339
452
|
end
|
340
453
|
|
341
454
|
def test_round
|
342
455
|
assert_template_result "5", "{{ input | round }}", 'input' => 4.6
|
343
456
|
assert_template_result "4", "{{ '4.3' | round }}"
|
344
457
|
assert_template_result "4.56", "{{ input | round: 2 }}", 'input' => 4.5612
|
458
|
+
assert_raises(Liquid::FloatDomainError) do
|
459
|
+
assert_template_result "4", "{{ 1.0 | divided_by: 0.0 | round }}"
|
460
|
+
end
|
461
|
+
|
462
|
+
assert_template_result "5", "{{ price | round }}", 'price' => NumberLikeThing.new(4.6)
|
463
|
+
assert_template_result "4", "{{ price | round }}", 'price' => NumberLikeThing.new(4.3)
|
345
464
|
end
|
346
465
|
|
347
466
|
def test_ceil
|
348
467
|
assert_template_result "5", "{{ input | ceil }}", 'input' => 4.6
|
349
468
|
assert_template_result "5", "{{ '4.3' | ceil }}"
|
469
|
+
assert_raises(Liquid::FloatDomainError) do
|
470
|
+
assert_template_result "4", "{{ 1.0 | divided_by: 0.0 | ceil }}"
|
471
|
+
end
|
472
|
+
|
473
|
+
assert_template_result "5", "{{ price | ceil }}", 'price' => NumberLikeThing.new(4.6)
|
350
474
|
end
|
351
475
|
|
352
476
|
def test_floor
|
353
477
|
assert_template_result "4", "{{ input | floor }}", 'input' => 4.6
|
354
478
|
assert_template_result "4", "{{ '4.3' | floor }}"
|
479
|
+
assert_raises(Liquid::FloatDomainError) do
|
480
|
+
assert_template_result "4", "{{ 1.0 | divided_by: 0.0 | floor }}"
|
481
|
+
end
|
482
|
+
|
483
|
+
assert_template_result "5", "{{ price | floor }}", 'price' => NumberLikeThing.new(5.4)
|
355
484
|
end
|
356
485
|
|
357
486
|
def test_append
|
358
|
-
assigns = {'a' => 'bc', 'b' => 'd' }
|
359
|
-
assert_template_result('bcd',"{{ a | append: 'd'}}",assigns)
|
360
|
-
assert_template_result('bcd',"{{ a | append: b}}",assigns)
|
487
|
+
assigns = { 'a' => 'bc', 'b' => 'd' }
|
488
|
+
assert_template_result('bcd', "{{ a | append: 'd'}}", assigns)
|
489
|
+
assert_template_result('bcd', "{{ a | append: b}}", assigns)
|
490
|
+
end
|
491
|
+
|
492
|
+
def test_concat
|
493
|
+
assert_equal [1, 2, 3, 4], @filters.concat([1, 2], [3, 4])
|
494
|
+
assert_equal [1, 2, 'a'], @filters.concat([1, 2], ['a'])
|
495
|
+
assert_equal [1, 2, 10], @filters.concat([1, 2], [10])
|
496
|
+
|
497
|
+
assert_raises(Liquid::ArgumentError, "concat filter requires an array argument") do
|
498
|
+
@filters.concat([1, 2], 10)
|
499
|
+
end
|
361
500
|
end
|
362
501
|
|
363
502
|
def test_prepend
|
364
|
-
assigns = {'a' => 'bc', 'b' => 'a' }
|
365
|
-
assert_template_result('abc',"{{ a | prepend: 'a'}}",assigns)
|
366
|
-
assert_template_result('abc',"{{ a | prepend: b}}",assigns)
|
503
|
+
assigns = { 'a' => 'bc', 'b' => 'a' }
|
504
|
+
assert_template_result('abc', "{{ a | prepend: 'a'}}", assigns)
|
505
|
+
assert_template_result('abc', "{{ a | prepend: b}}", assigns)
|
367
506
|
end
|
368
507
|
|
369
508
|
def test_default
|
@@ -376,7 +515,7 @@ class StandardFiltersTest < Minitest::Test
|
|
376
515
|
end
|
377
516
|
|
378
517
|
def test_cannot_access_private_methods
|
379
|
-
assert_template_result('a',"{{ 'a' | to_number }}")
|
518
|
+
assert_template_result('a', "{{ 'a' | to_number }}")
|
380
519
|
end
|
381
520
|
|
382
521
|
def test_date_raises_nothing
|
@@ -6,11 +6,10 @@ class BreakTagTest < Minitest::Test
|
|
6
6
|
# tests that no weird errors are raised if break is called outside of a
|
7
7
|
# block
|
8
8
|
def test_break_with_no_block
|
9
|
-
assigns = {'i' => 1}
|
9
|
+
assigns = { 'i' => 1 }
|
10
10
|
markup = '{% break %}'
|
11
11
|
expected = ''
|
12
12
|
|
13
13
|
assert_template_result(expected, markup, assigns)
|
14
14
|
end
|
15
|
-
|
16
15
|
end
|