From 7f18ac8f9943a88f29a9f8e61bc6dc7cd3fa2e20 Mon Sep 17 00:00:00 2001
From: Thiago Arrais
Group By Expression
+Group an array's items using a Liquid expression.
+
+ {% raw %}{{ site.members | group_by_exp:"item",
+"item.graduation_year | truncate: 3, \"\"" }}{% endraw %}
+
+ [{"name"=>"201...", "items"=>[...]},
+{"name"=>"200...", "items"=>[...]}]
+
XML Escape
diff --git a/lib/jekyll/filters.rb b/lib/jekyll/filters.rb index 29f2a2b5..f1467fa5 100644 --- a/lib/jekyll/filters.rb +++ b/lib/jekyll/filters.rb @@ -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 diff --git a/lib/jekyll/filters/grouping_filters.rb b/lib/jekyll/filters/grouping_filters.rb new file mode 100644 index 00000000..372b1e7b --- /dev/null +++ b/lib/jekyll/filters/grouping_filters.rb @@ -0,0 +1,56 @@ +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 } + make_grouped_array(groups) + else + input + end + end + + 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 + make_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 make_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 diff --git a/test/test_filters.rb b/test/test_filters.rb index 9a0f87d4..9a2909ef 100644 --- a/test/test_filters.rb +++ b/test/test_filters.rb @@ -747,6 +747,88 @@ 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." + ) + assert_equal 5, 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." + ) + assert_equal 15, 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| + p 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 From 7ac9653f4efe61f12b27adb0a7261547c17e266c Mon Sep 17 00:00:00 2001 From: Thiago Arrais