Skip to content

Commit 74c185d

Browse files
committed
array filters should not crash on strings
Official Shopidyf Liquid also can run array filters on strings, considering them to be one element array.
1 parent 8783931 commit 74c185d

2 files changed

Lines changed: 201 additions & 16 deletions

File tree

lib/liquid.lua

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2777,33 +2777,52 @@ function FilterSet:find_filter( filter_name )
27772777
return self.filterset[filter_name]
27782778
end
27792779
end
2780+
2781+
local function is_iterator(o)
2782+
local mt = getmetatable(o)
2783+
2784+
return mt and mt.__ipairs
2785+
end
2786+
2787+
local function iterator(o)
2788+
if type(o) == 'table' or is_iterator(o) then
2789+
return o
2790+
else
2791+
return { o }
2792+
end
2793+
end
2794+
27802795
--=== array filter begin
27812796
local function join( a, b)
27822797
-- body
2783-
return Interpreter:safe_concat(a, b)
2798+
return Interpreter:safe_concat(iterator(a), b or ' ')
27842799
end
27852800
local function first( a )
27862801
-- body
2787-
return a[1]
2802+
return iterator(a)[1]
2803+
end
2804+
local function size( a )
2805+
-- body
2806+
return(#iterator(a))
27882807
end
27892808
local function last( a )
27902809
-- body
2791-
return a[#a]
2810+
return iterator(a)[size(a)]
27922811
end
27932812
local function concat( a, b)
27942813
-- body
27952814
local temp = {}
2796-
for i,v in ipairs(a) do
2815+
for i,v in ipairs(iterator(a)) do
27972816
table.insert(temp, v)
27982817
end
2799-
for i,v in ipairs(b) do
2818+
for i,v in ipairs(iterator(b)) do
28002819
table.insert(temp, v)
28012820
end
28022821
return temp
28032822
end
28042823
local function index( a, b)
28052824
-- body
2806-
return a[(b + 1)]
2825+
return iterator(a)[(b + 1)]
28072826
end
28082827
local function map( a, map_field)
28092828
-- body
@@ -2816,20 +2835,18 @@ end
28162835
local function reverse( a )
28172836
-- body
28182837
local temp = {}
2819-
local num = #a
2838+
local it = iterator(a)
2839+
local num = size(a)
28202840
for k = num, 1, -1 do
2821-
table.insert(temp, a[k])
2841+
table.insert(temp, it[k])
28222842
end
28232843
return temp
28242844
end
2825-
local function size( a )
2826-
-- body
2827-
return(#a)
2828-
end
2845+
28292846
local function sort( a, sort_field)
28302847
-- body
28312848
local t = {}
2832-
for i,v in ipairs(a) do
2849+
for i,v in ipairs(iterator(a)) do
28332850
table.insert(t, v)
28342851
end
28352852
if not sort_field then
@@ -2845,7 +2862,7 @@ local function uniq( a )
28452862
-- body
28462863
local t = {}
28472864
local result = {}
2848-
for i,v in ipairs(a) do
2865+
for i,v in ipairs(iterator(a)) do
28492866
local k = cjson.encode(v)
28502867
if not t[k] then
28512868
t[k] = true

t/array_filters.t

Lines changed: 170 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ plan tests => repeat_each() * (3 * blocks());
77

88
my $pwd = cwd();
99

10-
our $HttpConfig = qq{
10+
our $HttpConfig = qq|
1111
lua_package_path "$pwd/lib/?.lua;;;";
1212
lua_package_cpath "/usr/local/openresty/lualib/?.so;;";
13-
};
13+
init_by_lua_block { Liquid = require 'liquid' }
14+
|;
1415

1516

1617
no_long_string();
@@ -264,3 +265,170 @@ GET /t
264265
--- no_error_log
265266
[error]
266267
268+
269+
270+
=== TEST 11: 'join' filter works on strings
271+
--- http_config eval: $::HttpConfig
272+
--- config
273+
location /t {
274+
content_by_lua_block {
275+
ngx.say( Liquid.Template:parse([[
276+
{{- str | join: ' - ' -}}
277+
]]):render(Liquid.InterpreterContext:new({ str = "string" })) )
278+
}
279+
}
280+
--- request
281+
GET /t
282+
--- response_body
283+
string
284+
--- no_error_log
285+
[error]
286+
287+
=== TEST 12: 'first' filter works strings
288+
--- http_config eval: $::HttpConfig
289+
--- config
290+
location /t {
291+
content_by_lua_block {
292+
ngx.say( Liquid.Template:parse([[
293+
{{- str | first -}}
294+
]]):render(Liquid.InterpreterContext:new({ str = "string" })) )
295+
}
296+
}
297+
--- request
298+
GET /t
299+
--- response_body
300+
string
301+
--- no_error_log
302+
[error]
303+
304+
305+
306+
=== TEST 13: 'last' filter works on strings
307+
--- http_config eval: $::HttpConfig
308+
--- config
309+
location /t {
310+
content_by_lua_block {
311+
ngx.say( Liquid.Template:parse([[
312+
{{- str | last -}}
313+
]]):render( Liquid.InterpreterContext:new({ str = "string" })) )
314+
}
315+
}
316+
--- request
317+
GET /t
318+
--- response_body
319+
string
320+
--- no_error_log
321+
[error]
322+
323+
=== TEST 14: 'concat' filter works on strings
324+
--- http_config eval: $::HttpConfig
325+
--- config
326+
location /t {
327+
content_by_lua_block {
328+
ngx.say( Liquid.Template:parse([[
329+
{%- assign a = "string" | concat:(1..3) -%}
330+
{%- assign b = "string" | concat: "another" -%}
331+
{%- for k in a %} {{k}} {%- endfor -%}
332+
{%- for k in b %} {{k}} {%- endfor -%}
333+
]]):render())
334+
}
335+
}
336+
--- request
337+
GET /t
338+
--- response_body
339+
string 1 2 3 string another
340+
--- no_error_log
341+
[error]
342+
343+
=== TEST 5: 'index' filter works on strings
344+
--- http_config eval: $::HttpConfig
345+
--- config
346+
location /t {
347+
content_by_lua_block {
348+
ngx.say( Liquid.Template:parse([[
349+
{{- "string"| index: 0 -}}
350+
]]):render() )
351+
}
352+
}
353+
--- request
354+
GET /t
355+
--- response_body
356+
string
357+
--- no_error_log
358+
[error]
359+
360+
361+
=== TEST 16: 'reverse' filter works on strings
362+
--- http_config eval: $::HttpConfig
363+
--- config
364+
location /t {
365+
content_by_lua_block {
366+
ngx.say( Liquid.Template:parse([[
367+
{{- "string" | reverse | join -}}
368+
]]):render() )
369+
}
370+
}
371+
--- request
372+
GET /t
373+
--- response_body
374+
string
375+
--- no_error_log
376+
[error]
377+
378+
379+
380+
=== TEST 17: 'size' filter works on strings
381+
--- http_config eval: $::HttpConfig
382+
--- config
383+
location /t {
384+
content_by_lua_block {
385+
ngx.say( Liquid.Template:parse([[
386+
{{- "string" | size -}}
387+
]]):render() )
388+
}
389+
}
390+
--- request
391+
GET /t
392+
--- response_body
393+
1
394+
--- no_error_log
395+
[error]
396+
397+
398+
399+
=== TEST 18: 'sort' filter works on strings
400+
--- http_config eval: $::HttpConfig
401+
--- config
402+
location /t {
403+
content_by_lua_block {
404+
ngx.say( Liquid.Template:parse([[
405+
{{- "string" | sort | join -}}
406+
]]):render() )
407+
}
408+
}
409+
--- request
410+
GET /t
411+
--- response_body
412+
string
413+
414+
--- no_error_log
415+
[error]
416+
417+
418+
419+
=== TEST 19: 'uniq' filter works on strings
420+
--- http_config eval: $::HttpConfig
421+
--- config
422+
location /t {
423+
content_by_lua_block {
424+
ngx.say( Liquid.Template:parse([[
425+
{{- "string" | uniq | join -}}
426+
]]):render() )
427+
}
428+
}
429+
--- request
430+
GET /t
431+
--- response_body
432+
string
433+
--- no_error_log
434+
[error]

0 commit comments

Comments
 (0)