diff --git a/app/concerns/liquid_interpolatable.rb b/app/concerns/liquid_interpolatable.rb index dc8cb56c02..772d78f708 100644 --- a/app/concerns/liquid_interpolatable.rb +++ b/app/concerns/liquid_interpolatable.rb @@ -283,6 +283,27 @@ def as_object(object) throw :as_object, object.as_json end + # Group an array of items by a property + # + # Example usage: + # + # {% assign posts_by_author = site.posts | group_by: "author" %} + # {% for author in posts_by_author %} + #
{{author.name}}
+ # {% for post in author.items %} + #
{{post.title}}
+ # {% endfor %} + # {% endfor %} + def group_by(input, property) + if input.respond_to?(:group_by) + input.group_by { |item| item[property] }.map do |value, items| + { 'name' => value, 'items' => items } + end + else + input + end + end + private def logger diff --git a/spec/concerns/liquid_interpolatable_spec.rb b/spec/concerns/liquid_interpolatable_spec.rb index 6db0a66ca9..711c09566d 100644 --- a/spec/concerns/liquid_interpolatable_spec.rb +++ b/spec/concerns/liquid_interpolatable_spec.rb @@ -86,11 +86,11 @@ def @filter.to_xpath_roundtrip(string) @agent.interpolation_context['s'] = 'http://example.com/dir/1?q=test' end - it 'should parse an abosule URI' do + it 'should parse an absolute URI' do expect(@filter.to_uri('http://example.net/index.html', 'http://example.com/dir/1')).to eq(URI('http://example.net/index.html')) end - it 'should parse an abosule URI with a base URI specified' do + it 'should parse an absolute URI with a base URI specified' do expect(@filter.to_uri('http://example.net/index.html', 'http://example.com/dir/1')).to eq(URI('http://example.net/index.html')) end @@ -98,7 +98,7 @@ def @filter.to_xpath_roundtrip(string) expect(@filter.to_uri('foo/index.html', 'http://example.com/dir/1')).to eq(URI('http://example.com/dir/foo/index.html')) end - it 'should parse an abosule URI with a base URI specified' do + it 'should parse an absolute URI with a base URI specified' do expect(@filter.to_uri('http://example.net/index.html', 'http://example.com/dir/1')).to eq(URI('http://example.net/index.html')) end @@ -394,4 +394,40 @@ def ensure_safety(obj) expect(agent.interpolated['template']).to eq('38b98bc2625a8cac33369f6204e784482be5e172b242699406270856a841d1ec') end end + + describe 'group_by' do + let(:events) do + [ + { "date" => "2019-07-30", "type" => "Snap" }, + { "date" => "2019-07-30", "type" => "Crackle" }, + { "date" => "2019-07-29", "type" => "Pop" }, + { "date" => "2019-07-29", "type" => "Bam" }, + { "date" => "2019-07-29", "type" => "Pow" }, + ] + end + + it "should group an enumerable by the given attribute" do + expect(@filter.group_by(events, "date")).to eq( + [ + { + "name" => "2019-07-30", "items" => [ + { "date" => "2019-07-30", "type" => "Snap" }, + { "date" => "2019-07-30", "type" => "Crackle" } + ] + }, + { + "name" => "2019-07-29", "items" => [ + { "date" => "2019-07-29", "type" => "Pop" }, + { "date" => "2019-07-29", "type" => "Bam" }, + { "date" => "2019-07-29", "type" => "Pow" } + ] + } + ] + ) + end + + it "should leave non-groupables alone" do + expect(@filter.group_by("some string", "anything")).to eq("some string") + end + end end