Fix "sort" filter on empty array to return empty array

When sorting an empty array with the "sort" filter, it returns nil
instead of [].  This confuses subsequent filters in the chain that
expect an array.  For example, when followed by the "map" filter, it
produces an array containing one nil element: [nil].

I could special-case the nil return value, but that would be more
cumbersome than making sure "sort" always returns an array.

Add a case to the "sort" method to return [] if the array is empty,
before performing any checks on ary.first that assume a non-empty array.

There is still a danger of returning nil if the first item in the array
is nil and it is non-empty, but I'm not sure how better to handle that
case.

Apply a similar fix to sort_natural, uniq, and compact filters.
This commit is contained in:
Marcel M. Cary
2015-09-29 10:24:31 -07:00
parent d8bda2c892
commit 4100f8d641
2 changed files with 29 additions and 0 deletions
+13
View File
@@ -116,6 +116,8 @@ module Liquid
ary = InputIterator.new(input)
if property.nil?
ary.sort
elsif ary.empty? # The next two cases assume a non-empty array.
[]
elsif ary.first.respond_to?(:[]) && !ary.first[property].nil?
ary.sort { |a, b| a[property] <=> b[property] }
elsif ary.first.respond_to?(property)
@@ -130,6 +132,8 @@ module Liquid
if property.nil?
ary.sort { |a, b| a.casecmp(b) }
elsif ary.empty? # The next two cases assume a non-empty array.
[]
elsif ary.first.respond_to?(:[]) && !ary.first[property].nil?
ary.sort { |a, b| a[property].casecmp(b[property]) }
elsif ary.first.respond_to?(property)
@@ -144,6 +148,8 @@ module Liquid
if property.nil?
ary.uniq
elsif ary.empty? # The next two cases assume a non-empty array.
[]
elsif ary.first.respond_to?(:[])
ary.uniq{ |a| a[property] }
end
@@ -175,6 +181,8 @@ module Liquid
if property.nil?
ary.compact
elsif ary.empty? # The next two cases assume a non-empty array.
[]
elsif ary.first.respond_to?(:[])
ary.reject{ |a| a[property].nil? }
elsif ary.first.respond_to?(property)
@@ -374,6 +382,11 @@ module Liquid
to_a.compact
end
def empty?
@input.each { return false }
true
end
def each
@input.each do |e|
yield(e.respond_to?(:to_liquid) ? e.to_liquid : e)