liquid 2.6.3 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/History.md +46 -13
- data/README.md +27 -2
- data/lib/liquid/block.rb +85 -51
- data/lib/liquid/block_body.rb +123 -0
- data/lib/liquid/condition.rb +26 -15
- data/lib/liquid/context.rb +106 -140
- data/lib/liquid/document.rb +3 -3
- data/lib/liquid/drop.rb +17 -1
- data/lib/liquid/errors.rb +50 -2
- data/lib/liquid/expression.rb +33 -0
- data/lib/liquid/file_system.rb +17 -6
- data/lib/liquid/i18n.rb +39 -0
- data/lib/liquid/interrupts.rb +1 -1
- data/lib/liquid/lexer.rb +51 -0
- data/lib/liquid/locales/en.yml +22 -0
- data/lib/liquid/parser.rb +90 -0
- data/lib/liquid/parser_switching.rb +31 -0
- data/lib/liquid/profiler/hooks.rb +23 -0
- data/lib/liquid/profiler.rb +159 -0
- data/lib/liquid/range_lookup.rb +22 -0
- data/lib/liquid/standardfilters.rb +143 -55
- data/lib/liquid/strainer.rb +14 -4
- data/lib/liquid/tag.rb +25 -9
- data/lib/liquid/tags/assign.rb +12 -9
- data/lib/liquid/tags/break.rb +1 -1
- data/lib/liquid/tags/capture.rb +10 -8
- data/lib/liquid/tags/case.rb +13 -13
- data/lib/liquid/tags/comment.rb +9 -2
- data/lib/liquid/tags/continue.rb +1 -4
- data/lib/liquid/tags/cycle.rb +5 -7
- data/lib/liquid/tags/decrement.rb +3 -4
- data/lib/liquid/tags/for.rb +69 -36
- data/lib/liquid/tags/if.rb +52 -25
- data/lib/liquid/tags/ifchanged.rb +3 -3
- data/lib/liquid/tags/include.rb +19 -8
- data/lib/liquid/tags/increment.rb +4 -8
- data/lib/liquid/tags/raw.rb +4 -7
- data/lib/liquid/tags/table_row.rb +73 -0
- data/lib/liquid/tags/unless.rb +2 -4
- data/lib/liquid/template.rb +124 -14
- data/lib/liquid/token.rb +18 -0
- data/lib/liquid/utils.rb +13 -4
- data/lib/liquid/variable.rb +103 -25
- data/lib/liquid/variable_lookup.rb +78 -0
- data/lib/liquid/version.rb +1 -1
- data/lib/liquid.rb +19 -11
- data/test/fixtures/en_locale.yml +9 -0
- data/test/{liquid → integration}/assign_test.rb +18 -1
- data/test/integration/blank_test.rb +106 -0
- data/test/{liquid → integration}/capture_test.rb +3 -3
- data/test/integration/context_test.rb +32 -0
- data/test/integration/drop_test.rb +271 -0
- data/test/integration/error_handling_test.rb +207 -0
- data/test/{liquid → integration}/filter_test.rb +11 -11
- data/test/integration/hash_ordering_test.rb +23 -0
- data/test/{liquid → integration}/output_test.rb +13 -13
- data/test/integration/parsing_quirks_test.rb +116 -0
- data/test/integration/render_profiling_test.rb +154 -0
- data/test/{liquid → integration}/security_test.rb +10 -10
- data/test/{liquid → integration}/standard_filter_test.rb +148 -32
- data/test/{liquid → integration}/tags/break_tag_test.rb +1 -1
- data/test/{liquid → integration}/tags/continue_tag_test.rb +1 -1
- data/test/{liquid → integration}/tags/for_tag_test.rb +80 -2
- data/test/{liquid → integration}/tags/if_else_tag_test.rb +24 -21
- data/test/integration/tags/include_tag_test.rb +234 -0
- data/test/{liquid → integration}/tags/increment_tag_test.rb +1 -1
- data/test/{liquid → integration}/tags/raw_tag_test.rb +2 -1
- data/test/{liquid → integration}/tags/standard_tag_test.rb +28 -26
- data/test/integration/tags/statements_test.rb +113 -0
- data/test/{liquid/tags/html_tag_test.rb → integration/tags/table_row_test.rb} +5 -5
- data/test/{liquid → integration}/tags/unless_else_tag_test.rb +1 -1
- data/test/{liquid → integration}/template_test.rb +81 -45
- data/test/integration/variable_test.rb +82 -0
- data/test/test_helper.rb +73 -20
- data/test/{liquid/block_test.rb → unit/block_unit_test.rb} +2 -5
- data/test/{liquid/condition_test.rb → unit/condition_unit_test.rb} +23 -1
- data/test/{liquid/context_test.rb → unit/context_unit_test.rb} +39 -25
- data/test/{liquid/file_system_test.rb → unit/file_system_unit_test.rb} +11 -5
- data/test/unit/i18n_unit_test.rb +37 -0
- data/test/unit/lexer_unit_test.rb +48 -0
- data/test/{liquid/module_ex_test.rb → unit/module_ex_unit_test.rb} +7 -7
- data/test/unit/parser_unit_test.rb +82 -0
- data/test/{liquid/regexp_test.rb → unit/regexp_unit_test.rb} +3 -3
- data/test/{liquid/strainer_test.rb → unit/strainer_unit_test.rb} +20 -1
- data/test/unit/tag_unit_test.rb +16 -0
- data/test/unit/tags/case_tag_unit_test.rb +10 -0
- data/test/unit/tags/for_tag_unit_test.rb +13 -0
- data/test/unit/tags/if_tag_unit_test.rb +8 -0
- data/test/unit/template_unit_test.rb +69 -0
- data/test/unit/tokenizer_unit_test.rb +38 -0
- data/test/unit/variable_unit_test.rb +139 -0
- metadata +135 -67
- data/lib/extras/liquid_view.rb +0 -51
- data/lib/liquid/htmltags.rb +0 -73
- data/test/liquid/drop_test.rb +0 -180
- data/test/liquid/error_handling_test.rb +0 -81
- data/test/liquid/hash_ordering_test.rb +0 -25
- data/test/liquid/parsing_quirks_test.rb +0 -52
- data/test/liquid/tags/include_tag_test.rb +0 -166
- data/test/liquid/tags/statements_test.rb +0 -134
- data/test/liquid/variable_test.rb +0 -186
@@ -6,21 +6,21 @@ module SecurityFilter
|
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
9
|
-
class SecurityTest < Test
|
9
|
+
class SecurityTest < Minitest::Test
|
10
10
|
include Liquid
|
11
11
|
|
12
12
|
def test_no_instance_eval
|
13
13
|
text = %( {{ '1+1' | instance_eval }} )
|
14
14
|
expected = %| 1+1 |
|
15
15
|
|
16
|
-
assert_equal expected, Template.parse(text).render(@assigns)
|
16
|
+
assert_equal expected, Template.parse(text).render!(@assigns)
|
17
17
|
end
|
18
18
|
|
19
19
|
def test_no_existing_instance_eval
|
20
20
|
text = %( {{ '1+1' | __instance_eval__ }} )
|
21
21
|
expected = %| 1+1 |
|
22
22
|
|
23
|
-
assert_equal expected, Template.parse(text).render(@assigns)
|
23
|
+
assert_equal expected, Template.parse(text).render!(@assigns)
|
24
24
|
end
|
25
25
|
|
26
26
|
|
@@ -28,7 +28,7 @@ class SecurityTest < Test::Unit::TestCase
|
|
28
28
|
text = %( {{ '1+1' | instance_eval }} )
|
29
29
|
expected = %| 1+1 |
|
30
30
|
|
31
|
-
assert_equal expected, Template.parse(text).render(@assigns)
|
31
|
+
assert_equal expected, Template.parse(text).render!(@assigns)
|
32
32
|
end
|
33
33
|
|
34
34
|
|
@@ -36,7 +36,7 @@ class SecurityTest < Test::Unit::TestCase
|
|
36
36
|
text = %( {{ '1+1' | add_one | instance_eval }} )
|
37
37
|
expected = %| 1+1 + 1 |
|
38
38
|
|
39
|
-
assert_equal expected, Template.parse(text).render(@assigns, :filters => SecurityFilter)
|
39
|
+
assert_equal expected, Template.parse(text).render!(@assigns, :filters => SecurityFilter)
|
40
40
|
end
|
41
41
|
|
42
42
|
def test_does_not_add_filters_to_symbol_table
|
@@ -47,17 +47,17 @@ class SecurityTest < Test::Unit::TestCase
|
|
47
47
|
template = Template.parse(test)
|
48
48
|
assert_equal [], (Symbol.all_symbols - current_symbols)
|
49
49
|
|
50
|
-
template.render
|
50
|
+
template.render!
|
51
51
|
assert_equal [], (Symbol.all_symbols - current_symbols)
|
52
52
|
end
|
53
53
|
|
54
54
|
def test_does_not_add_drop_methods_to_symbol_table
|
55
55
|
current_symbols = Symbol.all_symbols
|
56
56
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
57
|
+
assigns = { 'drop' => Drop.new }
|
58
|
+
assert_equal "", Template.parse("{{ drop.custom_method_1 }}", assigns).render!
|
59
|
+
assert_equal "", Template.parse("{{ drop.custom_method_2 }}", assigns).render!
|
60
|
+
assert_equal "", Template.parse("{{ drop.custom_method_3 }}", assigns).render!
|
61
61
|
|
62
62
|
assert_equal [], (Symbol.all_symbols - current_symbols)
|
63
63
|
end
|
@@ -7,6 +7,8 @@ class Filters
|
|
7
7
|
end
|
8
8
|
|
9
9
|
class TestThing
|
10
|
+
attr_reader :foo
|
11
|
+
|
10
12
|
def initialize
|
11
13
|
@foo = 0
|
12
14
|
end
|
@@ -15,6 +17,10 @@ class TestThing
|
|
15
17
|
"woot: #{@foo}"
|
16
18
|
end
|
17
19
|
|
20
|
+
def [](whatever)
|
21
|
+
to_s
|
22
|
+
end
|
23
|
+
|
18
24
|
def to_liquid
|
19
25
|
@foo += 1
|
20
26
|
self
|
@@ -27,7 +33,15 @@ class TestDrop < Liquid::Drop
|
|
27
33
|
end
|
28
34
|
end
|
29
35
|
|
30
|
-
class
|
36
|
+
class TestEnumerable < Liquid::Drop
|
37
|
+
include Enumerable
|
38
|
+
|
39
|
+
def each(&block)
|
40
|
+
[ { "foo" => 1, "bar" => 2 }, { "foo" => 2, "bar" => 1 }, { "foo" => 3, "bar" => 3 } ].each(&block)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class StandardFiltersTest < Minitest::Test
|
31
45
|
include Liquid
|
32
46
|
|
33
47
|
def setup
|
@@ -50,9 +64,32 @@ class StandardFiltersTest < Test::Unit::TestCase
|
|
50
64
|
assert_equal '', @filters.upcase(nil)
|
51
65
|
end
|
52
66
|
|
53
|
-
def
|
54
|
-
assert_equal '
|
55
|
-
assert_equal '', @filters.
|
67
|
+
def test_slice
|
68
|
+
assert_equal 'oob', @filters.slice('foobar', 1, 3)
|
69
|
+
assert_equal 'oobar', @filters.slice('foobar', 1, 1000)
|
70
|
+
assert_equal '', @filters.slice('foobar', 1, 0)
|
71
|
+
assert_equal 'o', @filters.slice('foobar', 1, 1)
|
72
|
+
assert_equal 'bar', @filters.slice('foobar', 3, 3)
|
73
|
+
assert_equal 'ar', @filters.slice('foobar', -2, 2)
|
74
|
+
assert_equal 'ar', @filters.slice('foobar', -2, 1000)
|
75
|
+
assert_equal 'r', @filters.slice('foobar', -1)
|
76
|
+
assert_equal '', @filters.slice(nil, 0)
|
77
|
+
assert_equal '', @filters.slice('foobar', 100, 10)
|
78
|
+
assert_equal '', @filters.slice('foobar', -100, 10)
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_slice_on_arrays
|
82
|
+
input = 'foobar'.split(//)
|
83
|
+
assert_equal %w{o o b}, @filters.slice(input, 1, 3)
|
84
|
+
assert_equal %w{o o b a r}, @filters.slice(input, 1, 1000)
|
85
|
+
assert_equal %w{}, @filters.slice(input, 1, 0)
|
86
|
+
assert_equal %w{o}, @filters.slice(input, 1, 1)
|
87
|
+
assert_equal %w{b a r}, @filters.slice(input, 3, 3)
|
88
|
+
assert_equal %w{a r}, @filters.slice(input, -2, 2)
|
89
|
+
assert_equal %w{a r}, @filters.slice(input, -2, 1000)
|
90
|
+
assert_equal %w{r}, @filters.slice(input, -1)
|
91
|
+
assert_equal %w{}, @filters.slice(input, 100, 10)
|
92
|
+
assert_equal %w{}, @filters.slice(input, -100, 10)
|
56
93
|
end
|
57
94
|
|
58
95
|
def test_truncate
|
@@ -63,12 +100,13 @@ class StandardFiltersTest < Test::Unit::TestCase
|
|
63
100
|
assert_equal "测试...", @filters.truncate("测试测试测试测试", 5)
|
64
101
|
end
|
65
102
|
|
66
|
-
def
|
103
|
+
def test_split
|
67
104
|
assert_equal ['12','34'], @filters.split('12~34', '~')
|
68
105
|
assert_equal ['A? ',' ,Z'], @filters.split('A? ~ ~ ~ ,Z', '~ ~ ~')
|
69
106
|
assert_equal ['A?Z'], @filters.split('A?Z', '~')
|
70
107
|
# Regexp works although Liquid does not support.
|
71
108
|
assert_equal ['A','Z'], @filters.split('AxZ', /x/)
|
109
|
+
assert_equal [], @filters.split(nil, ' ')
|
72
110
|
end
|
73
111
|
|
74
112
|
def test_escape
|
@@ -77,7 +115,12 @@ class StandardFiltersTest < Test::Unit::TestCase
|
|
77
115
|
end
|
78
116
|
|
79
117
|
def test_escape_once
|
80
|
-
assert_equal '<strong>', @filters.escape_once(
|
118
|
+
assert_equal '<strong>Hulk</strong>', @filters.escape_once('<strong>Hulk</strong>')
|
119
|
+
end
|
120
|
+
|
121
|
+
def test_url_encode
|
122
|
+
assert_equal 'foo%2B1%40example.com', @filters.url_encode('foo+1@example.com')
|
123
|
+
assert_equal nil, @filters.url_encode(nil)
|
81
124
|
end
|
82
125
|
|
83
126
|
def test_truncatewords
|
@@ -108,10 +151,32 @@ class StandardFiltersTest < Test::Unit::TestCase
|
|
108
151
|
assert_equal [{"a" => 1}, {"a" => 2}, {"a" => 3}, {"a" => 4}], @filters.sort([{"a" => 4}, {"a" => 3}, {"a" => 1}, {"a" => 2}], "a")
|
109
152
|
end
|
110
153
|
|
154
|
+
def test_legacy_sort_hash
|
155
|
+
assert_equal [{a:1, b:2}], @filters.sort({a:1, b:2})
|
156
|
+
end
|
157
|
+
|
158
|
+
def test_numerical_vs_lexicographical_sort
|
159
|
+
assert_equal [2, 10], @filters.sort([10, 2])
|
160
|
+
assert_equal [{"a" => 2}, {"a" => 10}], @filters.sort([{"a" => 10}, {"a" => 2}], "a")
|
161
|
+
assert_equal ["10", "2"], @filters.sort(["10", "2"])
|
162
|
+
assert_equal [{"a" => "10"}, {"a" => "2"}], @filters.sort([{"a" => "10"}, {"a" => "2"}], "a")
|
163
|
+
end
|
164
|
+
|
165
|
+
def test_uniq
|
166
|
+
assert_equal [1,3,2,4], @filters.uniq([1,1,3,2,3,1,4,3,2,1])
|
167
|
+
assert_equal [{"a" => 1}, {"a" => 3}, {"a" => 2}], @filters.uniq([{"a" => 1}, {"a" => 3}, {"a" => 1}, {"a" => 2}], "a")
|
168
|
+
testdrop = TestDrop.new
|
169
|
+
assert_equal [testdrop], @filters.uniq([testdrop, TestDrop.new], 'test')
|
170
|
+
end
|
171
|
+
|
111
172
|
def test_reverse
|
112
173
|
assert_equal [4,3,2,1], @filters.reverse([1,2,3,4])
|
113
174
|
end
|
114
175
|
|
176
|
+
def test_legacy_reverse_hash
|
177
|
+
assert_equal [{a:1, b:2}], @filters.reverse(a:1, b:2)
|
178
|
+
end
|
179
|
+
|
115
180
|
def test_map
|
116
181
|
assert_equal [1,2,3,4], @filters.map([{"a" => 1}, {"a" => 2}, {"a" => 3}, {"a" => 4}], 'a')
|
117
182
|
assert_template_result 'abc', "{{ ary | map:'foo' | map:'bar' }}",
|
@@ -119,20 +184,50 @@ class StandardFiltersTest < Test::Unit::TestCase
|
|
119
184
|
end
|
120
185
|
|
121
186
|
def test_map_doesnt_call_arbitrary_stuff
|
122
|
-
|
123
|
-
|
187
|
+
assert_template_result "", '{{ "foo" | map: "__id__" }}'
|
188
|
+
assert_template_result "", '{{ "foo" | map: "inspect" }}'
|
124
189
|
end
|
125
190
|
|
126
191
|
def test_map_calls_to_liquid
|
127
192
|
t = TestThing.new
|
128
|
-
|
193
|
+
assert_template_result "woot: 1", '{{ foo | map: "whatever" }}', "foo" => [t]
|
194
|
+
end
|
195
|
+
|
196
|
+
def test_map_on_hashes
|
197
|
+
assert_template_result "4217", '{{ thing | map: "foo" | map: "bar" }}',
|
198
|
+
"thing" => { "foo" => [ { "bar" => 42 }, { "bar" => 17 } ] }
|
199
|
+
end
|
200
|
+
|
201
|
+
def test_legacy_map_on_hashes_with_dynamic_key
|
202
|
+
template = "{% assign key = 'foo' %}{{ thing | map: key | map: 'bar' }}"
|
203
|
+
hash = { "foo" => { "bar" => 42 } }
|
204
|
+
assert_template_result "42", template, "thing" => hash
|
205
|
+
end
|
206
|
+
|
207
|
+
def test_sort_calls_to_liquid
|
208
|
+
t = TestThing.new
|
209
|
+
Liquid::Template.parse('{{ foo | sort: "whatever" }}').render("foo" => [t])
|
210
|
+
assert t.foo > 0
|
129
211
|
end
|
130
212
|
|
131
213
|
def test_map_over_proc
|
132
214
|
drop = TestDrop.new
|
133
215
|
p = Proc.new{ drop }
|
134
216
|
templ = '{{ procs | map: "test" }}'
|
135
|
-
|
217
|
+
assert_template_result "testfoo", templ, "procs" => [p]
|
218
|
+
end
|
219
|
+
|
220
|
+
def test_map_works_on_enumerables
|
221
|
+
assert_template_result "123", '{{ foo | map: "foo" }}', "foo" => TestEnumerable.new
|
222
|
+
end
|
223
|
+
|
224
|
+
def test_sort_works_on_enumerables
|
225
|
+
assert_template_result "213", '{{ foo | sort: "bar" | map: "foo" }}', "foo" => TestEnumerable.new
|
226
|
+
end
|
227
|
+
|
228
|
+
def test_first_and_last_call_to_liquid
|
229
|
+
assert_template_result 'foobar', '{{ foo | first }}', 'foo' => [ThingWithToLiquid.new]
|
230
|
+
assert_template_result 'foobar', '{{ foo | last }}', 'foo' => [ThingWithToLiquid.new]
|
136
231
|
end
|
137
232
|
|
138
233
|
def test_date
|
@@ -157,13 +252,10 @@ class StandardFiltersTest < Test::Unit::TestCase
|
|
157
252
|
|
158
253
|
assert_equal nil, @filters.date(nil, "%B")
|
159
254
|
|
160
|
-
|
161
|
-
|
162
|
-
assert_equal "07/05/2006", @filters.date("1152098955", "%m/%d/%Y")
|
163
|
-
end
|
255
|
+
assert_equal "07/05/2006", @filters.date(1152098955, "%m/%d/%Y")
|
256
|
+
assert_equal "07/05/2006", @filters.date("1152098955", "%m/%d/%Y")
|
164
257
|
end
|
165
258
|
|
166
|
-
|
167
259
|
def test_first_last
|
168
260
|
assert_equal 1, @filters.first([1,2,3])
|
169
261
|
assert_equal 3, @filters.last([1,2,3])
|
@@ -187,6 +279,21 @@ class StandardFiltersTest < Test::Unit::TestCase
|
|
187
279
|
assert_template_result 'foobar', "{{ 'foo|bar' | remove: '|' }}"
|
188
280
|
end
|
189
281
|
|
282
|
+
def test_strip
|
283
|
+
assert_template_result 'ab c', "{{ source | strip }}", 'source' => " ab c "
|
284
|
+
assert_template_result 'ab c', "{{ source | strip }}", 'source' => " \tab c \n \t"
|
285
|
+
end
|
286
|
+
|
287
|
+
def test_lstrip
|
288
|
+
assert_template_result 'ab c ', "{{ source | lstrip }}", 'source' => " ab c "
|
289
|
+
assert_template_result "ab c \n \t", "{{ source | lstrip }}", 'source' => " \tab c \n \t"
|
290
|
+
end
|
291
|
+
|
292
|
+
def test_rstrip
|
293
|
+
assert_template_result " ab c", "{{ source | rstrip }}", 'source' => " ab c "
|
294
|
+
assert_template_result " \tab c", "{{ source | rstrip }}", 'source' => " \tab c \n \t"
|
295
|
+
end
|
296
|
+
|
190
297
|
def test_strip_newlines
|
191
298
|
assert_template_result 'abc', "{{ source | strip_newlines }}", 'source' => "a\nb\nc"
|
192
299
|
assert_template_result 'abc', "{{ source | strip_newlines }}", 'source' => "a\r\nb\nc"
|
@@ -210,9 +317,6 @@ class StandardFiltersTest < Test::Unit::TestCase
|
|
210
317
|
assert_template_result "12", "{{ 3 | times:4 }}"
|
211
318
|
assert_template_result "0", "{{ 'foo' | times:4 }}"
|
212
319
|
|
213
|
-
# Ruby v1.9.2-rc1, or higher, backwards compatible Float test
|
214
|
-
assert_match(/(6\.3)|(6\.(0{13})1)/, Template.parse("{{ '2.1' | times:3 }}").render)
|
215
|
-
|
216
320
|
assert_template_result "6", "{{ '2.1' | times:3 | replace: '.','-' | plus:0}}"
|
217
321
|
|
218
322
|
assert_template_result "7.25", "{{ 0.0725 | times:100 }}"
|
@@ -222,11 +326,8 @@ class StandardFiltersTest < Test::Unit::TestCase
|
|
222
326
|
assert_template_result "4", "{{ 12 | divided_by:3 }}"
|
223
327
|
assert_template_result "4", "{{ 14 | divided_by:3 }}"
|
224
328
|
|
225
|
-
# Ruby v1.9.2-rc1, or higher, backwards compatible Float test
|
226
|
-
assert_match(/4\.(6{13,14})7/, Template.parse("{{ 14 | divided_by:'3.0' }}").render)
|
227
|
-
|
228
329
|
assert_template_result "5", "{{ 15 | divided_by:3 }}"
|
229
|
-
|
330
|
+
assert_equal "Liquid error: divided by 0", Template.parse("{{ 5 | divided_by:0 }}").render
|
230
331
|
|
231
332
|
assert_template_result "0.5", "{{ 2.0 | divided_by:4 }}"
|
232
333
|
end
|
@@ -235,6 +336,22 @@ class StandardFiltersTest < Test::Unit::TestCase
|
|
235
336
|
assert_template_result "1", "{{ 3 | modulo:2 }}"
|
236
337
|
end
|
237
338
|
|
339
|
+
def test_round
|
340
|
+
assert_template_result "5", "{{ input | round }}", 'input' => 4.6
|
341
|
+
assert_template_result "4", "{{ '4.3' | round }}"
|
342
|
+
assert_template_result "4.56", "{{ input | round: 2 }}", 'input' => 4.5612
|
343
|
+
end
|
344
|
+
|
345
|
+
def test_ceil
|
346
|
+
assert_template_result "5", "{{ input | ceil }}", 'input' => 4.6
|
347
|
+
assert_template_result "5", "{{ '4.3' | ceil }}"
|
348
|
+
end
|
349
|
+
|
350
|
+
def test_floor
|
351
|
+
assert_template_result "4", "{{ input | floor }}", 'input' => 4.6
|
352
|
+
assert_template_result "4", "{{ '4.3' | floor }}"
|
353
|
+
end
|
354
|
+
|
238
355
|
def test_append
|
239
356
|
assigns = {'a' => 'bc', 'b' => 'd' }
|
240
357
|
assert_template_result('bcd',"{{ a | append: 'd'}}",assigns)
|
@@ -247,17 +364,16 @@ class StandardFiltersTest < Test::Unit::TestCase
|
|
247
364
|
assert_template_result('abc',"{{ a | prepend: b}}",assigns)
|
248
365
|
end
|
249
366
|
|
250
|
-
def
|
251
|
-
|
367
|
+
def test_default
|
368
|
+
assert_equal "foo", @filters.default("foo", "bar")
|
369
|
+
assert_equal "bar", @filters.default(nil, "bar")
|
370
|
+
assert_equal "bar", @filters.default("", "bar")
|
371
|
+
assert_equal "bar", @filters.default(false, "bar")
|
372
|
+
assert_equal "bar", @filters.default([], "bar")
|
373
|
+
assert_equal "bar", @filters.default({}, "bar")
|
252
374
|
end
|
253
375
|
|
254
|
-
|
255
|
-
|
256
|
-
def with_timezone(tz)
|
257
|
-
old_tz = ENV['TZ']
|
258
|
-
ENV['TZ'] = tz
|
259
|
-
yield
|
260
|
-
ensure
|
261
|
-
ENV['TZ'] = old_tz
|
376
|
+
def test_cannot_access_private_methods
|
377
|
+
assert_template_result('a',"{{ 'a' | to_number }}")
|
262
378
|
end
|
263
379
|
end # StandardFiltersTest
|
@@ -1,6 +1,12 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
-
class
|
3
|
+
class ThingWithValue < Liquid::Drop
|
4
|
+
def value
|
5
|
+
3
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class ForTagTest < Minitest::Test
|
4
10
|
include Liquid
|
5
11
|
|
6
12
|
def test_for
|
@@ -34,6 +40,20 @@ HERE
|
|
34
40
|
assert_template_result(' 1 2 3 ','{%for item in (1..3) %} {{item}} {%endfor%}')
|
35
41
|
end
|
36
42
|
|
43
|
+
def test_for_with_variable_range
|
44
|
+
assert_template_result(' 1 2 3 ','{%for item in (1..foobar) %} {{item}} {%endfor%}', "foobar" => 3)
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_for_with_hash_value_range
|
48
|
+
foobar = { "value" => 3 }
|
49
|
+
assert_template_result(' 1 2 3 ','{%for item in (1..foobar.value) %} {{item}} {%endfor%}', "foobar" => foobar)
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_for_with_drop_value_range
|
53
|
+
foobar = ThingWithValue.new
|
54
|
+
assert_template_result(' 1 2 3 ','{%for item in (1..foobar.value) %} {{item}} {%endfor%}', "foobar" => foobar)
|
55
|
+
end
|
56
|
+
|
37
57
|
def test_for_with_variable
|
38
58
|
assert_template_result(' 1 2 3 ','{%for item in array%} {{item}} {%endfor%}','array' => [1,2,3])
|
39
59
|
assert_template_result('123','{%for item in array%}{{item}}{%endfor%}','array' => [1,2,3])
|
@@ -283,7 +303,7 @@ HERE
|
|
283
303
|
end
|
284
304
|
|
285
305
|
def test_bad_variable_naming_in_for_loop
|
286
|
-
|
306
|
+
assert_raises(Liquid::SyntaxError) do
|
287
307
|
Liquid::Template.parse('{% for a/b in x %}{% endfor %}')
|
288
308
|
end
|
289
309
|
end
|
@@ -294,4 +314,62 @@ HERE
|
|
294
314
|
assigns = {'items' => [1,2,3,4,5]}
|
295
315
|
assert_template_result(expected, template, assigns)
|
296
316
|
end
|
317
|
+
|
318
|
+
class LoaderDrop < Liquid::Drop
|
319
|
+
attr_accessor :each_called, :load_slice_called
|
320
|
+
|
321
|
+
def initialize(data)
|
322
|
+
@data = data
|
323
|
+
end
|
324
|
+
|
325
|
+
def each
|
326
|
+
@each_called = true
|
327
|
+
@data.each { |el| yield el }
|
328
|
+
end
|
329
|
+
|
330
|
+
def load_slice(from, to)
|
331
|
+
@load_slice_called = true
|
332
|
+
@data[(from..to-1)]
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
def test_iterate_with_each_when_no_limit_applied
|
337
|
+
loader = LoaderDrop.new([1,2,3,4,5])
|
338
|
+
assigns = {'items' => loader}
|
339
|
+
expected = '12345'
|
340
|
+
template = '{% for item in items %}{{item}}{% endfor %}'
|
341
|
+
assert_template_result(expected, template, assigns)
|
342
|
+
assert loader.each_called
|
343
|
+
assert !loader.load_slice_called
|
344
|
+
end
|
345
|
+
|
346
|
+
def test_iterate_with_load_slice_when_limit_applied
|
347
|
+
loader = LoaderDrop.new([1,2,3,4,5])
|
348
|
+
assigns = {'items' => loader}
|
349
|
+
expected = '1'
|
350
|
+
template = '{% for item in items limit:1 %}{{item}}{% endfor %}'
|
351
|
+
assert_template_result(expected, template, assigns)
|
352
|
+
assert !loader.each_called
|
353
|
+
assert loader.load_slice_called
|
354
|
+
end
|
355
|
+
|
356
|
+
def test_iterate_with_load_slice_when_limit_and_offset_applied
|
357
|
+
loader = LoaderDrop.new([1,2,3,4,5])
|
358
|
+
assigns = {'items' => loader}
|
359
|
+
expected = '34'
|
360
|
+
template = '{% for item in items offset:2 limit:2 %}{{item}}{% endfor %}'
|
361
|
+
assert_template_result(expected, template, assigns)
|
362
|
+
assert !loader.each_called
|
363
|
+
assert loader.load_slice_called
|
364
|
+
end
|
365
|
+
|
366
|
+
def test_iterate_with_load_slice_returns_same_results_as_without
|
367
|
+
loader = LoaderDrop.new([1,2,3,4,5])
|
368
|
+
loader_assigns = {'items' => loader}
|
369
|
+
array_assigns = {'items' => [1,2,3,4,5]}
|
370
|
+
expected = '34'
|
371
|
+
template = '{% for item in items offset:2 limit:2 %}{{item}}{% endfor %}'
|
372
|
+
assert_template_result(expected, template, loader_assigns)
|
373
|
+
assert_template_result(expected, template, array_assigns)
|
374
|
+
end
|
297
375
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
-
class IfElseTagTest < Test
|
3
|
+
class IfElseTagTest < Minitest::Test
|
4
4
|
include Liquid
|
5
5
|
|
6
6
|
def test_if
|
@@ -10,6 +10,11 @@ class IfElseTagTest < Test::Unit::TestCase
|
|
10
10
|
assert_template_result(' you rock ?','{% if false %} you suck {% endif %} {% if true %} you rock {% endif %}?')
|
11
11
|
end
|
12
12
|
|
13
|
+
def test_literal_comparisons
|
14
|
+
assert_template_result(' NO ','{% assign v = false %}{% if v %} YES {% else %} NO {% endif %}')
|
15
|
+
assert_template_result(' YES ','{% assign v = nil %}{% if v == nil %} YES {% else %} NO {% endif %}')
|
16
|
+
end
|
17
|
+
|
13
18
|
def test_if_else
|
14
19
|
assert_template_result(' YES ','{% if false %} NO {% else %} YES {% endif %}')
|
15
20
|
assert_template_result(' YES ','{% if true %} YES {% else %} NO {% endif %}')
|
@@ -37,25 +42,19 @@ class IfElseTagTest < Test::Unit::TestCase
|
|
37
42
|
end
|
38
43
|
|
39
44
|
def test_comparison_of_strings_containing_and_or_or
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
assert_template_result(' YES ',"{% if #{awful_markup} %} YES {% endif %}", assigns)
|
44
|
-
end
|
45
|
+
awful_markup = "a == 'and' and b == 'or' and c == 'foo and bar' and d == 'bar or baz' and e == 'foo' and foo and bar"
|
46
|
+
assigns = {'a' => 'and', 'b' => 'or', 'c' => 'foo and bar', 'd' => 'bar or baz', 'e' => 'foo', 'foo' => true, 'bar' => true}
|
47
|
+
assert_template_result(' YES ',"{% if #{awful_markup} %} YES {% endif %}", assigns)
|
45
48
|
end
|
46
49
|
|
47
50
|
def test_comparison_of_expressions_starting_with_and_or_or
|
48
51
|
assigns = {'order' => {'items_count' => 0}, 'android' => {'name' => 'Roy'}}
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
assert_template_result( "YES",
|
56
|
-
"{% if order.items_count == 0 %}YES{% endif %}",
|
57
|
-
assigns)
|
58
|
-
end
|
52
|
+
assert_template_result( "YES",
|
53
|
+
"{% if android.name == 'Roy' %}YES{% endif %}",
|
54
|
+
assigns)
|
55
|
+
assert_template_result( "YES",
|
56
|
+
"{% if order.items_count == 0 %}YES{% endif %}",
|
57
|
+
assigns)
|
59
58
|
end
|
60
59
|
|
61
60
|
def test_if_and
|
@@ -135,32 +134,36 @@ class IfElseTagTest < Test::Unit::TestCase
|
|
135
134
|
end
|
136
135
|
|
137
136
|
def test_syntax_error_no_variable
|
138
|
-
|
137
|
+
assert_raises(SyntaxError){ assert_template_result('', '{% if jerry == 1 %}')}
|
139
138
|
end
|
140
139
|
|
141
140
|
def test_syntax_error_no_expression
|
142
|
-
|
141
|
+
assert_raises(SyntaxError) { assert_template_result('', '{% if %}') }
|
143
142
|
end
|
144
143
|
|
145
144
|
def test_if_with_custom_condition
|
145
|
+
original_op = Condition.operators['contains']
|
146
146
|
Condition.operators['contains'] = :[]
|
147
147
|
|
148
148
|
assert_template_result('yes', %({% if 'bob' contains 'o' %}yes{% endif %}))
|
149
149
|
assert_template_result('no', %({% if 'bob' contains 'f' %}yes{% else %}no{% endif %}))
|
150
150
|
ensure
|
151
|
-
Condition.operators
|
151
|
+
Condition.operators['contains'] = original_op
|
152
152
|
end
|
153
153
|
|
154
154
|
def test_operators_are_ignored_unless_isolated
|
155
|
+
original_op = Condition.operators['contains']
|
155
156
|
Condition.operators['contains'] = :[]
|
156
157
|
|
157
158
|
assert_template_result('yes',
|
158
159
|
%({% if 'gnomeslab-and-or-liquid' contains 'gnomeslab-and-or-liquid' %}yes{% endif %}))
|
160
|
+
ensure
|
161
|
+
Condition.operators['contains'] = original_op
|
159
162
|
end
|
160
163
|
|
161
164
|
def test_operators_are_whitelisted
|
162
|
-
|
165
|
+
assert_raises(SyntaxError) do
|
163
166
|
assert_template_result('', %({% if 1 or throw or or 1 %}yes{% endif %}))
|
164
167
|
end
|
165
168
|
end
|
166
|
-
end
|
169
|
+
end
|