Merge pull request #4478 from edgemaster/where-exp
Merge pull request 4478
This commit is contained in:
		
						commit
						fc0d440201
					
				|  | @ -1,6 +1,7 @@ | ||||||
| require 'uri' | require 'uri' | ||||||
| require 'json' | require 'json' | ||||||
| require 'date' | require 'date' | ||||||
|  | require 'liquid' | ||||||
| 
 | 
 | ||||||
| module Jekyll | module Jekyll | ||||||
|   module Filters |   module Filters | ||||||
|  | @ -225,6 +226,26 @@ module Jekyll | ||||||
|       input.select { |object| Array(item_property(object, property)).map(&:to_s).include?(value.to_s) } |       input.select { |object| Array(item_property(object, property)).map(&:to_s).include?(value.to_s) } | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|  |     # Filters an array of objects against 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 where_exp(input, variable, expression) | ||||||
|  |       return input unless input.is_a?(Enumerable) | ||||||
|  |       input = input.values if input.is_a?(Hash) # FIXME | ||||||
|  | 
 | ||||||
|  |       condition = parse_condition(expression) | ||||||
|  |       @context.stack do | ||||||
|  |         input.select do |object| | ||||||
|  |           @context[variable] = object | ||||||
|  |           condition.evaluate(@context) | ||||||
|  |         end | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|     # Sort an array of objects |     # Sort an array of objects | ||||||
|     # |     # | ||||||
|     # input - the object array |     # input - the object array | ||||||
|  | @ -363,5 +384,21 @@ module Jekyll | ||||||
|         end |         end | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
|  | 
 | ||||||
|  |     # Parse a string to a Liquid Condition | ||||||
|  |     def parse_condition(exp) | ||||||
|  |       parser = Liquid::Parser.new(exp) | ||||||
|  |       left_expr = parser.expression | ||||||
|  |       operator = parser.consume?(:comparison) | ||||||
|  |       condition = | ||||||
|  |         if operator | ||||||
|  |           Liquid::Condition.new(left_expr, operator, parser.expression) | ||||||
|  |         else | ||||||
|  |           Liquid::Condition.new(left_expr) | ||||||
|  |         end | ||||||
|  |       parser.consume(:end_of_string) | ||||||
|  | 
 | ||||||
|  |       condition | ||||||
|  |     end | ||||||
|   end |   end | ||||||
| end | end | ||||||
|  |  | ||||||
|  | @ -88,6 +88,22 @@ common tasks easier. | ||||||
|         </p> |         </p> | ||||||
|       </td> |       </td> | ||||||
|     </tr> |     </tr> | ||||||
|  |     <tr> | ||||||
|  |       <td> | ||||||
|  |         <p class="name"><strong>Where Expression</strong></p> | ||||||
|  |         <p>Select all the objects in an array where the expression is true.</p> | ||||||
|  |       </td> | ||||||
|  |       <td class="align-center"> | ||||||
|  |         <p> | ||||||
|  |          <code class="filter">{% raw %}{{ site.members | where_exp:"item", | ||||||
|  | "item.graduation_year == 2014" }}{% endraw %}</code> | ||||||
|  |          <code class="filter">{% raw %}{{ site.members | where_exp:"item", | ||||||
|  | "item.graduation_year < 2014" }}{% endraw %}</code> | ||||||
|  |          <code class="filter">{% raw %}{{ site.members | where_exp:"item", | ||||||
|  | "item.projects contains 'foo'" }}{% endraw %}</code> | ||||||
|  |         </p> | ||||||
|  |       </td> | ||||||
|  |     </tr> | ||||||
|     <tr> |     <tr> | ||||||
|       <td> |       <td> | ||||||
|         <p class="name"><strong>Group By</strong></p> |         <p class="name"><strong>Group By</strong></p> | ||||||
|  |  | ||||||
|  | @ -354,6 +354,64 @@ class TestFilters < JekyllUnitTest | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|  |     context "where_exp filter" do | ||||||
|  |       should "return any input that is not an array" do | ||||||
|  |         assert_equal "some string", @filter.where_exp("some string", "la", "le") | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       should "filter objects in a hash appropriately" do | ||||||
|  |         hash = {"a"=>{"color"=>"red"}, "b"=>{"color"=>"blue"}} | ||||||
|  |         assert_equal 1, @filter.where_exp(hash, "item", "item.color == 'red'").length | ||||||
|  |         assert_equal [{"color"=>"red"}], @filter.where_exp(hash, "item", "item.color == 'red'") | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       should "filter objects appropriately" do | ||||||
|  |         assert_equal 2, @filter.where_exp(@array_of_objects, "item", "item.color == 'red'").length | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       should "stringify during comparison for compatibility with liquid parsing" do | ||||||
|  |         hash = { | ||||||
|  |           "The Words" => {"rating" => 1.2, "featured" => false}, | ||||||
|  |           "Limitless" => {"rating" => 9.2, "featured" => true}, | ||||||
|  |           "Hustle"    => {"rating" => 4.7, "featured" => true}, | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         results = @filter.where_exp(hash, "item", "item.featured == true") | ||||||
|  |         assert_equal 2, results.length | ||||||
|  |         assert_equal 9.2, results[0]["rating"] | ||||||
|  |         assert_equal 4.7, results[1]["rating"] | ||||||
|  | 
 | ||||||
|  |         results = @filter.where_exp(hash, "item", "item.rating == 4.7") | ||||||
|  |         assert_equal 1, results.length | ||||||
|  |         assert_equal 4.7, results[0]["rating"] | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       should "filter with other operators" do | ||||||
|  |         assert_equal [3, 4, 5], @filter.where_exp([ 1, 2, 3, 4, 5 ], "n", "n >= 3") | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       objects = [ | ||||||
|  |         { "id" => "a", "groups" => [1, 2] }, | ||||||
|  |         { "id" => "b", "groups" => [2, 3] }, | ||||||
|  |         { "id" => "c" }, | ||||||
|  |         { "id" => "d", "groups" => [1, 3] } | ||||||
|  |       ] | ||||||
|  |       should "filter with the contains operator over arrays" do | ||||||
|  |         results = @filter.where_exp(objects, "obj", "obj.groups contains 1") | ||||||
|  |         assert_equal 2, results.length | ||||||
|  |         assert_equal "a", results[0]["id"] | ||||||
|  |         assert_equal "d", results[1]["id"] | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       should "filter with the contains operator over hash keys" do | ||||||
|  |         results = @filter.where_exp(objects, "obj", "obj contains 'groups'") | ||||||
|  |         assert_equal 3, results.length | ||||||
|  |         assert_equal "a", results[0]["id"] | ||||||
|  |         assert_equal "b", results[1]["id"] | ||||||
|  |         assert_equal "d", results[2]["id"] | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|     context "sort filter" do |     context "sort filter" do | ||||||
|       should "raise Exception when input is nil" do |       should "raise Exception when input is nil" do | ||||||
|         err = assert_raises ArgumentError do |         err = assert_raises ArgumentError do | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue