Merge pull request #5513 from thiagoarrais/5415-group_by_exp-filter

Merge pull request 5513
This commit is contained in:
jekyllbot 2016-12-09 16:01:21 -08:00 committed by GitHub
commit 8ed324007a
4 changed files with 167 additions and 28 deletions

View File

@ -147,6 +147,22 @@ common tasks easier.
</p>
</td>
</tr>
<tr>
<td>
<p class="name"><strong>Group By Expression</strong></p>
<p>Group an array's items using a Liquid expression.</p>
</td>
<td class="align-center">
<p>
<code class="filter">{% raw %}{{ site.members | group_by_exp:"item",
"item.graduation_year | truncate: 3, \"\"" }}{% endraw %}</code>
</p>
<p>
<code class="output">[{"name"=>"201...", "items"=>[...]},
{"name"=>"200...", "items"=>[...]}]</code>
</p>
</td>
</tr>
<tr>
<td>
<p class="name"><strong>XML Escape</strong></p>

View File

@ -8,6 +8,8 @@ require_all "jekyll/filters"
module Jekyll
module Filters
include URLFilters
include GroupingFilters
# Convert a Markdown string into HTML output.
#
# input - The Markdown String to convert.
@ -205,29 +207,6 @@ module Jekyll
as_liquid(input).to_json
end
# Group an array of items by a property
#
# input - the inputted Enumerable
# property - the property
#
# Returns an array of Hashes, each looking something like this:
# {"name" => "larry"
# "items" => [...] } # all the items where `property` == "larry"
def group_by(input, property)
if groupable?(input)
input.group_by { |item| item_property(item, property).to_s }
.each_with_object([]) do |item, array|
array << {
"name" => item.first,
"items" => item.last,
"size" => item.last.size
}
end
else
input
end
end
# Filter an array of objects
#
# input - the object array
@ -381,11 +360,6 @@ module Jekyll
end.localtime
end
private
def groupable?(element)
element.respond_to?(:group_by)
end
private
def item_property(item, property)
if item.respond_to?(:to_liquid)
@ -436,6 +410,7 @@ module Jekyll
condition
end
end
end

View File

@ -0,0 +1,63 @@
module Jekyll
module Filters
module GroupingFilters
# Group an array of items by a property
#
# input - the inputted Enumerable
# property - the property
#
# Returns an array of Hashes, each looking something like this:
# {"name" => "larry"
# "items" => [...] } # all the items where `property` == "larry"
def group_by(input, property)
if groupable?(input)
groups = input.group_by { |item| item_property(item, property).to_s }
grouped_array(groups)
else
input
end
end
# Group an array of items by an expression
#
# input - the object array
# variable - the variable to assign each item to in the expression
# expression -a Liquid comparison expression passed in as a string
#
# Returns the filtered array of objects
def group_by_exp(input, variable, expression)
return input unless groupable?(input)
parsed_expr = parse_expression(expression)
@context.stack do
groups = input.group_by do |item|
@context[variable] = item
parsed_expr.render(@context)
end
grouped_array(groups)
end
end
private
def parse_expression(str)
Liquid::Variable.new(str, {})
end
private
def groupable?(element)
element.respond_to?(:group_by)
end
private
def grouped_array(groups)
groups.each_with_object([]) do |item, array|
array << {
"name" => item.first,
"items" => item.last,
"size" => item.last.size
}
end
end
end
end
end

View File

@ -756,6 +756,91 @@ class TestFilters < JekyllUnitTest
end
end
context "group_by_exp filter" do
should "successfully group array of Jekyll::Page's" do
@filter.site.process
groups = @filter.group_by_exp(@filter.site.pages, "page", "page.layout | upcase")
groups.each do |g|
assert(
["DEFAULT", "NIL", ""].include?(g["name"]),
"#{g["name"]} isn't a valid grouping."
)
case g["name"]
when "DEFAULT"
assert(
g["items"].is_a?(Array),
"The list of grouped items for 'default' is not an Array."
)
# adjust array.size to ignore symlinked page in Windows
qty = Utils::Platforms.really_windows? ? 4 : 5
assert_equal qty, g["items"].size
when "nil"
assert(
g["items"].is_a?(Array),
"The list of grouped items for 'nil' is not an Array."
)
assert_equal 2, g["items"].size
when ""
assert(
g["items"].is_a?(Array),
"The list of grouped items for '' is not an Array."
)
# adjust array.size to ignore symlinked page in Windows
qty = Utils::Platforms.really_windows? ? 14 : 15
assert_equal qty, g["items"].size
end
end
end
should "include the size of each grouping" do
groups = @filter.group_by_exp(@filter.site.pages, "page", "page.layout")
groups.each do |g|
assert_equal(
g["items"].size,
g["size"],
"The size property for '#{g["name"]}' doesn't match the size of the Array."
)
end
end
should "allow more complex filters" do
items = [
{ "version"=>"1.0", "result"=>"slow" },
{ "version"=>"1.1.5", "result"=>"medium" },
{ "version"=>"2.7.3", "result"=>"fast" }
]
result = @filter.group_by_exp(items, "item", "item.version | split: '.' | first")
assert_equal 2, result.size
end
should "be equivalent of group_by" do
actual = @filter.group_by_exp(@filter.site.pages, "page", "page.layout")
expected = @filter.group_by(@filter.site.pages, "layout")
assert_equal expected, actual
end
should "return any input that is not an array" do
assert_equal "some string", @filter.group_by_exp("some string", "la", "le")
end
should "group by full element (as opposed to a field of the element)" do
items = %w(a b c d)
result = @filter.group_by_exp(items, "item", "item")
assert_equal 4, result.length
assert_equal ["a"], result.first["items"]
end
should "accept hashes" do
hash = { 1 => "a", 2 => "b", 3 => "c", 4 => "d" }
result = @filter.group_by_exp(hash, "item", "item")
assert_equal 4, result.length
end
end
context "sort filter" do
should "raise Exception when input is nil" do
err = assert_raises ArgumentError do