wrong 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.markdown +114 -25
 - data/lib/predicated/Gemfile +15 -0
 - data/lib/predicated/LICENSE +20 -0
 - data/lib/predicated/README.markdown +191 -0
 - data/lib/predicated/Rakefile +51 -0
 - data/lib/predicated/lib/predicated.rb +4 -0
 - data/lib/predicated/lib/predicated/autogen_call.rb +37 -0
 - data/lib/predicated/lib/predicated/constrain.rb +66 -0
 - data/lib/predicated/lib/predicated/evaluate.rb +94 -0
 - data/lib/predicated/lib/predicated/from/callable_object.rb +108 -0
 - data/lib/predicated/lib/predicated/from/json.rb +59 -0
 - data/lib/predicated/lib/predicated/from/ruby_code_string.rb +73 -0
 - data/lib/predicated/lib/predicated/from/url_part.rb +104 -0
 - data/lib/predicated/lib/predicated/from/xml.rb +61 -0
 - data/lib/predicated/lib/predicated/gem_check.rb +34 -0
 - data/lib/predicated/lib/predicated/predicate.rb +111 -0
 - data/lib/predicated/lib/predicated/print.rb +62 -0
 - data/lib/predicated/lib/predicated/selectable.rb +102 -0
 - data/lib/predicated/lib/predicated/simple_templated_predicate.rb +79 -0
 - data/lib/predicated/lib/predicated/string_utils.rb +20 -0
 - data/lib/predicated/lib/predicated/to/arel.rb +41 -0
 - data/lib/predicated/lib/predicated/to/json.rb +48 -0
 - data/lib/predicated/lib/predicated/to/sentence.rb +94 -0
 - data/lib/predicated/lib/predicated/to/solr.rb +15 -0
 - data/lib/predicated/lib/predicated/to/xml.rb +67 -0
 - data/lib/predicated/lib/predicated/version.rb +3 -0
 - data/lib/predicated/predicated.gemspec +22 -0
 - data/lib/predicated/test/autogen_call_test.rb +40 -0
 - data/lib/predicated/test/canonical_transform_cases.rb +63 -0
 - data/lib/predicated/test/constrain_test.rb +86 -0
 - data/lib/predicated/test/enumerable_test.rb +32 -0
 - data/lib/predicated/test/equality_test.rb +32 -0
 - data/lib/predicated/test/evaluate_test.rb +149 -0
 - data/lib/predicated/test/from/callable_object_canonical_test.rb +43 -0
 - data/lib/predicated/test/from/callable_object_test.rb +78 -0
 - data/lib/predicated/test/from/json_test.rb +83 -0
 - data/lib/predicated/test/from/ruby_code_string_canonical_test.rb +37 -0
 - data/lib/predicated/test/from/ruby_code_string_test.rb +103 -0
 - data/lib/predicated/test/from/url_part_parser_test.rb +123 -0
 - data/lib/predicated/test/from/url_part_test.rb +48 -0
 - data/lib/predicated/test/from/xml_test.rb +57 -0
 - data/lib/predicated/test/json_conversion_test.rb +33 -0
 - data/lib/predicated/test/print_test.rb +66 -0
 - data/lib/predicated/test/selectable_test.rb +123 -0
 - data/lib/predicated/test/simple_templated_predicate_test.rb +39 -0
 - data/lib/predicated/test/suite.rb +2 -0
 - data/lib/predicated/test/test_helper.rb +64 -0
 - data/lib/predicated/test/test_helper_with_wrong.rb +6 -0
 - data/lib/predicated/test/to/arel_test.rb +85 -0
 - data/lib/predicated/test/to/json_test.rb +74 -0
 - data/lib/predicated/test/to/sentence_test.rb +90 -0
 - data/lib/predicated/test/to/solr_test.rb +39 -0
 - data/lib/predicated/test/to/xml_test.rb +72 -0
 - data/lib/predicated/test/xml_conversion_test.rb +34 -0
 - data/lib/predicated/test_integration/arel_integration_test.rb +52 -0
 - data/lib/predicated/test_integration/canonical_integration_cases.rb +66 -0
 - data/lib/predicated/test_integration/schema.xml +83 -0
 - data/lib/predicated/test_integration/solr_integration_test.rb +71 -0
 - data/lib/predicated/test_integration/sqlite_db +0 -0
 - data/lib/predicated/test_integration/suite.rb +2 -0
 - data/lib/predicated/test_integration/usage_test.rb +252 -0
 - data/lib/wrong.rb +3 -1
 - data/lib/wrong/adapters/test_unit.rb +1 -3
 - data/lib/wrong/assert.rb +81 -24
 - data/lib/wrong/chunk.rb +145 -0
 - data/lib/wrong/message/string_diff.rb +2 -4
 - data/lib/wrong/message/test_context.rb +2 -2
 - data/lib/wrong/version.rb +2 -2
 - data/test/adapters/minitest_test.rb +16 -9
 - data/test/adapters/test_unit_test.rb +1 -1
 - data/test/assert_test.rb +90 -0
 - data/test/catch_raise_test.rb +2 -2
 - data/test/chunk_test.rb +236 -0
 - data/test/failures_test.rb +109 -74
 - data/test/message/array_diff_test.rb +35 -19
 - data/test/message/string_diff_test.rb +39 -15
 - data/test/message/test_context_text.rb +2 -2
 - data/test/test_helper.rb +25 -7
 - metadata +86 -33
 - data/test/basic_assert_test.rb +0 -38
 
| 
         @@ -0,0 +1,104 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "predicated/predicate"
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Predicated
         
     | 
| 
      
 4 
     | 
    
         
            +
              
         
     | 
| 
      
 5 
     | 
    
         
            +
              require_gem_version("treetop", "1.4.8")
         
     | 
| 
      
 6 
     | 
    
         
            +
              
         
     | 
| 
      
 7 
     | 
    
         
            +
              class Predicate
         
     | 
| 
      
 8 
     | 
    
         
            +
                def self.from_url_part(url_part)
         
     | 
| 
      
 9 
     | 
    
         
            +
                  TreetopUrlPartParser.new.parse(url_part).to_predicate
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              module TreetopUrlPart
         
     | 
| 
      
 14 
     | 
    
         
            +
                Treetop.load_from_string(%{
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            grammar TreetopUrlPart
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
            include Predicated::TreetopUrlPart
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            rule or
         
     | 
| 
      
 21 
     | 
    
         
            +
              ( and "|" or <OrNode>)  / and
         
     | 
| 
      
 22 
     | 
    
         
            +
            end 
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            rule and
         
     | 
| 
      
 25 
     | 
    
         
            +
              ( leaf "&" and <AndNode> ) / leaf
         
     | 
| 
      
 26 
     | 
    
         
            +
            end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
            rule operation
         
     | 
| 
      
 29 
     | 
    
         
            +
              unquoted_string sign unquoted_string <OperationNode>
         
     | 
| 
      
 30 
     | 
    
         
            +
            end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
            rule parens
         
     | 
| 
      
 33 
     | 
    
         
            +
              not? "(" or ")" <ParensNode>
         
     | 
| 
      
 34 
     | 
    
         
            +
            end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
            rule not
         
     | 
| 
      
 37 
     | 
    
         
            +
              '!'
         
     | 
| 
      
 38 
     | 
    
         
            +
            end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
            rule leaf
         
     | 
| 
      
 43 
     | 
    
         
            +
              operation / parens
         
     | 
| 
      
 44 
     | 
    
         
            +
            end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
            rule unquoted_string
         
     | 
| 
      
 47 
     | 
    
         
            +
              [0-9a-zA-Z]*
         
     | 
| 
      
 48 
     | 
    
         
            +
            end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
            rule sign
         
     | 
| 
      
 51 
     | 
    
         
            +
              ('>=' / '<=' / '<' / '>' / '=' )
         
     | 
| 
      
 52 
     | 
    
         
            +
            end
         
     | 
| 
      
 53 
     | 
    
         
            +
            end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
            })
         
     | 
| 
      
 56 
     | 
    
         
            +
              
         
     | 
| 
      
 57 
     | 
    
         
            +
                class OperationNode < Treetop::Runtime::SyntaxNode
         
     | 
| 
      
 58 
     | 
    
         
            +
                  def left_text; elements[0].text_value end
         
     | 
| 
      
 59 
     | 
    
         
            +
                  def sign_text; elements[1].text_value end
         
     | 
| 
      
 60 
     | 
    
         
            +
                  def right_text; elements[2].text_value end      
         
     | 
| 
      
 61 
     | 
    
         
            +
                  
         
     | 
| 
      
 62 
     | 
    
         
            +
                  SIGN_TO_PREDICATE_CLASS = {
         
     | 
| 
      
 63 
     | 
    
         
            +
                    "=" => Equal, 
         
     | 
| 
      
 64 
     | 
    
         
            +
                    ">" => GreaterThan,
         
     | 
| 
      
 65 
     | 
    
         
            +
                    "<" => LessThan,
         
     | 
| 
      
 66 
     | 
    
         
            +
                    ">=" => GreaterThanOrEqualTo,
         
     | 
| 
      
 67 
     | 
    
         
            +
                    "<=" => LessThanOrEqualTo
         
     | 
| 
      
 68 
     | 
    
         
            +
                  }
         
     | 
| 
      
 69 
     | 
    
         
            +
                  
         
     | 
| 
      
 70 
     | 
    
         
            +
                  def to_predicate
         
     | 
| 
      
 71 
     | 
    
         
            +
                    SIGN_TO_PREDICATE_CLASS[sign_text].new(left_text, right_text)
         
     | 
| 
      
 72 
     | 
    
         
            +
                  end
         
     | 
| 
      
 73 
     | 
    
         
            +
                end      
         
     | 
| 
      
 74 
     | 
    
         
            +
                
         
     | 
| 
      
 75 
     | 
    
         
            +
                class AndNode < Treetop::Runtime::SyntaxNode
         
     | 
| 
      
 76 
     | 
    
         
            +
                  def left; elements[0] end
         
     | 
| 
      
 77 
     | 
    
         
            +
                  def right; elements[2] end      
         
     | 
| 
      
 78 
     | 
    
         
            +
                  
         
     | 
| 
      
 79 
     | 
    
         
            +
                  def to_predicate
         
     | 
| 
      
 80 
     | 
    
         
            +
                    And.new(left.to_predicate, right.to_predicate)
         
     | 
| 
      
 81 
     | 
    
         
            +
                  end
         
     | 
| 
      
 82 
     | 
    
         
            +
                end
         
     | 
| 
      
 83 
     | 
    
         
            +
                      
         
     | 
| 
      
 84 
     | 
    
         
            +
                class OrNode < Treetop::Runtime::SyntaxNode
         
     | 
| 
      
 85 
     | 
    
         
            +
                  def left; elements[0] end
         
     | 
| 
      
 86 
     | 
    
         
            +
                  def right; elements[2] end      
         
     | 
| 
      
 87 
     | 
    
         
            +
                  
         
     | 
| 
      
 88 
     | 
    
         
            +
                  def to_predicate
         
     | 
| 
      
 89 
     | 
    
         
            +
                    Or.new(left.to_predicate, right.to_predicate)
         
     | 
| 
      
 90 
     | 
    
         
            +
                  end
         
     | 
| 
      
 91 
     | 
    
         
            +
                end
         
     | 
| 
      
 92 
     | 
    
         
            +
                
         
     | 
| 
      
 93 
     | 
    
         
            +
                class ParensNode < Treetop::Runtime::SyntaxNode
         
     | 
| 
      
 94 
     | 
    
         
            +
                  def inner; elements[2] end
         
     | 
| 
      
 95 
     | 
    
         
            +
                  def not?; elements[0].text_value=="!" end
         
     | 
| 
      
 96 
     | 
    
         
            +
                  
         
     | 
| 
      
 97 
     | 
    
         
            +
                  def to_predicate
         
     | 
| 
      
 98 
     | 
    
         
            +
                    inner_predicate = inner.to_predicate
         
     | 
| 
      
 99 
     | 
    
         
            +
                    not? ? Not.new(inner_predicate) : inner_predicate
         
     | 
| 
      
 100 
     | 
    
         
            +
                  end
         
     | 
| 
      
 101 
     | 
    
         
            +
                end
         
     | 
| 
      
 102 
     | 
    
         
            +
                
         
     | 
| 
      
 103 
     | 
    
         
            +
              end
         
     | 
| 
      
 104 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,61 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "predicated/predicate"
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Predicated
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
              require_gem_version("nokogiri", "1.4.3")
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              class Predicate
         
     | 
| 
      
 8 
     | 
    
         
            +
                def self.from_xml(xml_str)
         
     | 
| 
      
 9 
     | 
    
         
            +
                  NodeToPredicate.convert(Nokogiri::XML(xml_str).root)
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
                
         
     | 
| 
      
 12 
     | 
    
         
            +
                module NodeToPredicate
         
     | 
| 
      
 13 
     | 
    
         
            +
                  OPERATION_TAG_TO_CLASS = {
         
     | 
| 
      
 14 
     | 
    
         
            +
                    "equal" => Equal, 
         
     | 
| 
      
 15 
     | 
    
         
            +
                    "greaterThan" => GreaterThan, 
         
     | 
| 
      
 16 
     | 
    
         
            +
                    "lessThan" => LessThan, 
         
     | 
| 
      
 17 
     | 
    
         
            +
                    "greaterThanOrEqualTo" => GreaterThanOrEqualTo, 
         
     | 
| 
      
 18 
     | 
    
         
            +
                    "lessThanOrEqualTo" => LessThanOrEqualTo
         
     | 
| 
      
 19 
     | 
    
         
            +
                  }
         
     | 
| 
      
 20 
     | 
    
         
            +
                  
         
     | 
| 
      
 21 
     | 
    
         
            +
                  def self.convert(node)
         
     | 
| 
      
 22 
     | 
    
         
            +
                    
         
     | 
| 
      
 23 
     | 
    
         
            +
                    node = next_non_text_node(node)
         
     | 
| 
      
 24 
     | 
    
         
            +
                    
         
     | 
| 
      
 25 
     | 
    
         
            +
                    if node.name == "and"
         
     | 
| 
      
 26 
     | 
    
         
            +
                      left = next_non_text_node(node.children[0])
         
     | 
| 
      
 27 
     | 
    
         
            +
                      right = next_non_text_node(left.next)
         
     | 
| 
      
 28 
     | 
    
         
            +
                      And.new(convert(left), convert(right))
         
     | 
| 
      
 29 
     | 
    
         
            +
                    elsif node.name == "or"
         
     | 
| 
      
 30 
     | 
    
         
            +
                      left = next_non_text_node(node.children[0])
         
     | 
| 
      
 31 
     | 
    
         
            +
                      right = next_non_text_node(left.next)
         
     | 
| 
      
 32 
     | 
    
         
            +
                      Or.new(convert(left), convert(right))
         
     | 
| 
      
 33 
     | 
    
         
            +
                    elsif node.name == "not"
         
     | 
| 
      
 34 
     | 
    
         
            +
                      inner = next_non_text_node(node.children[0])
         
     | 
| 
      
 35 
     | 
    
         
            +
                      Not.new(convert(inner))
         
     | 
| 
      
 36 
     | 
    
         
            +
                    elsif operation_class=OPERATION_TAG_TO_CLASS[node.name]
         
     | 
| 
      
 37 
     | 
    
         
            +
                      left = next_non_text_node(node.children[0])
         
     | 
| 
      
 38 
     | 
    
         
            +
                      right = next_non_text_node(left.next)
         
     | 
| 
      
 39 
     | 
    
         
            +
                      operation_class.new(left.text, right.text)
         
     | 
| 
      
 40 
     | 
    
         
            +
                    else
         
     | 
| 
      
 41 
     | 
    
         
            +
                      raise DontKnowWhatToDoWithThisXmlTag.new(node.name)
         
     | 
| 
      
 42 
     | 
    
         
            +
                    end
         
     | 
| 
      
 43 
     | 
    
         
            +
                  end
         
     | 
| 
      
 44 
     | 
    
         
            +
                  
         
     | 
| 
      
 45 
     | 
    
         
            +
                  def self.next_non_text_node(node)
         
     | 
| 
      
 46 
     | 
    
         
            +
                    while node.text?
         
     | 
| 
      
 47 
     | 
    
         
            +
                      node = node.next
         
     | 
| 
      
 48 
     | 
    
         
            +
                    end
         
     | 
| 
      
 49 
     | 
    
         
            +
                    node
         
     | 
| 
      
 50 
     | 
    
         
            +
                  end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                end
         
     | 
| 
      
 53 
     | 
    
         
            +
                
         
     | 
| 
      
 54 
     | 
    
         
            +
                class DontKnowWhatToDoWithThisXmlTag < StandardError
         
     | 
| 
      
 55 
     | 
    
         
            +
                  def initialize(xml_tag)
         
     | 
| 
      
 56 
     | 
    
         
            +
                    super("don't know what to do with #{xml_tag}")
         
     | 
| 
      
 57 
     | 
    
         
            +
                  end
         
     | 
| 
      
 58 
     | 
    
         
            +
                end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
              end
         
     | 
| 
      
 61 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,34 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Predicated
         
     | 
| 
      
 2 
     | 
    
         
            +
              def self.require_gem_version(gem_name, minimum_version, require_name=gem_name)
         
     | 
| 
      
 3 
     | 
    
         
            +
                unless Gem.available?(gem_name, Gem::Requirement.create(">= #{minimum_version}"))
         
     | 
| 
      
 4 
     | 
    
         
            +
                  raise %{
         
     | 
| 
      
 5 
     | 
    
         
            +
            Gem: #{gem_name} >=#{minimum_version}
         
     | 
| 
      
 6 
     | 
    
         
            +
            Does not appear to be installed.  Please install it.
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            Predicated is built in a way that allows you to pick and 
         
     | 
| 
      
 9 
     | 
    
         
            +
            choose which features to use.
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            RubyGems has no way to specify optional dependencies,
         
     | 
| 
      
 12 
     | 
    
         
            +
            therefore I've made the decision not to have Predicated
         
     | 
| 
      
 13 
     | 
    
         
            +
            automatically depend into the various gems referenced
         
     | 
| 
      
 14 
     | 
    
         
            +
            in from/to "extensions".
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            The cost here is that the gem install doesn't necessarily 
         
     | 
| 
      
 17 
     | 
    
         
            +
            "just work" for you out of the box.  But in return you get 
         
     | 
| 
      
 18 
     | 
    
         
            +
            greater flexibility.  
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            Notably, rails/arel unfortunately has a hard dependency 
         
     | 
| 
      
 21 
     | 
    
         
            +
            on Rails 3 activesupport, which requires ruby 1.8.7.  
         
     | 
| 
      
 22 
     | 
    
         
            +
            By making from/to dependencies optional, those with 
         
     | 
| 
      
 23 
     | 
    
         
            +
            no interest in arel can use Predicated in a wider 
         
     | 
| 
      
 24 
     | 
    
         
            +
            variety of environments.
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
            For more discussion see:
         
     | 
| 
      
 27 
     | 
    
         
            +
            http://stackoverflow.com/questions/2993335/rubygems-optional-dependencies
         
     | 
| 
      
 28 
     | 
    
         
            +
            }
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
                
         
     | 
| 
      
 31 
     | 
    
         
            +
                require require_name
         
     | 
| 
      
 32 
     | 
    
         
            +
              end
         
     | 
| 
      
 33 
     | 
    
         
            +
              
         
     | 
| 
      
 34 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,111 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "predicated/gem_check"
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Predicated
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
              def Predicate(&block)
         
     | 
| 
      
 6 
     | 
    
         
            +
                result = nil
         
     | 
| 
      
 7 
     | 
    
         
            +
                Module.new do
         
     | 
| 
      
 8 
     | 
    
         
            +
                  extend Shorthand
         
     | 
| 
      
 9 
     | 
    
         
            +
                  result = instance_eval(&block)
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
                result
         
     | 
| 
      
 12 
     | 
    
         
            +
              end
         
     | 
| 
      
 13 
     | 
    
         
            +
              
         
     | 
| 
      
 14 
     | 
    
         
            +
              class Predicate; end #marker class
         
     | 
| 
      
 15 
     | 
    
         
            +
              
         
     | 
| 
      
 16 
     | 
    
         
            +
              class Unary < Predicate
         
     | 
| 
      
 17 
     | 
    
         
            +
                attr_accessor :inner
         
     | 
| 
      
 18 
     | 
    
         
            +
                
         
     | 
| 
      
 19 
     | 
    
         
            +
                def initialize(inner)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  @inner = inner
         
     | 
| 
      
 21 
     | 
    
         
            +
                end    
         
     | 
| 
      
 22 
     | 
    
         
            +
                
         
     | 
| 
      
 23 
     | 
    
         
            +
                module FlipThroughMe
         
     | 
| 
      
 24 
     | 
    
         
            +
                  def each(ancestors=[], &block)
         
     | 
| 
      
 25 
     | 
    
         
            +
                    yield([self, ancestors])
         
     | 
| 
      
 26 
     | 
    
         
            +
                    ancestors_including_me = ancestors.dup + [self]
         
     | 
| 
      
 27 
     | 
    
         
            +
                    inner.each(ancestors_including_me) { |item| block.call(item) } if inner.is_a?(Enumerable)
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
                include FlipThroughMe
         
     | 
| 
      
 31 
     | 
    
         
            +
                include Enumerable
         
     | 
| 
      
 32 
     | 
    
         
            +
                
         
     | 
| 
      
 33 
     | 
    
         
            +
                module ValueEquality
         
     | 
| 
      
 34 
     | 
    
         
            +
                  def ==(other)
         
     | 
| 
      
 35 
     | 
    
         
            +
                    self.class == other.class && 
         
     | 
| 
      
 36 
     | 
    
         
            +
                    self.inner == other.inner
         
     | 
| 
      
 37 
     | 
    
         
            +
                  end
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
                include ValueEquality
         
     | 
| 
      
 40 
     | 
    
         
            +
              end
         
     | 
| 
      
 41 
     | 
    
         
            +
              
         
     | 
| 
      
 42 
     | 
    
         
            +
              class Binary < Predicate
         
     | 
| 
      
 43 
     | 
    
         
            +
                attr_accessor :left, :right
         
     | 
| 
      
 44 
     | 
    
         
            +
                
         
     | 
| 
      
 45 
     | 
    
         
            +
                def initialize(left, right)
         
     | 
| 
      
 46 
     | 
    
         
            +
                  @left = left
         
     | 
| 
      
 47 
     | 
    
         
            +
                  @right = right
         
     | 
| 
      
 48 
     | 
    
         
            +
                end    
         
     | 
| 
      
 49 
     | 
    
         
            +
                
         
     | 
| 
      
 50 
     | 
    
         
            +
                module FlipThroughMe
         
     | 
| 
      
 51 
     | 
    
         
            +
                  def each(ancestors=[], &block)
         
     | 
| 
      
 52 
     | 
    
         
            +
                    yield([self, ancestors])
         
     | 
| 
      
 53 
     | 
    
         
            +
                    ancestors_including_me = ancestors.dup + [self]
         
     | 
| 
      
 54 
     | 
    
         
            +
                    enumerate_side(@left, ancestors_including_me, &block)
         
     | 
| 
      
 55 
     | 
    
         
            +
                    enumerate_side(@right, ancestors_including_me, &block)
         
     | 
| 
      
 56 
     | 
    
         
            +
                  end
         
     | 
| 
      
 57 
     | 
    
         
            +
                  
         
     | 
| 
      
 58 
     | 
    
         
            +
                  private 
         
     | 
| 
      
 59 
     | 
    
         
            +
                  def enumerate_side(thing, ancestors)
         
     | 
| 
      
 60 
     | 
    
         
            +
                    thing.each(ancestors) { |item| yield(item) } if thing.is_a?(Enumerable)
         
     | 
| 
      
 61 
     | 
    
         
            +
                  end
         
     | 
| 
      
 62 
     | 
    
         
            +
                end
         
     | 
| 
      
 63 
     | 
    
         
            +
                include FlipThroughMe
         
     | 
| 
      
 64 
     | 
    
         
            +
                include Enumerable
         
     | 
| 
      
 65 
     | 
    
         
            +
                
         
     | 
| 
      
 66 
     | 
    
         
            +
                module ValueEquality
         
     | 
| 
      
 67 
     | 
    
         
            +
                  def ==(other)
         
     | 
| 
      
 68 
     | 
    
         
            +
                    self.class == other.class && 
         
     | 
| 
      
 69 
     | 
    
         
            +
                    self.left == other.left && 
         
     | 
| 
      
 70 
     | 
    
         
            +
                    self.right == other.right
         
     | 
| 
      
 71 
     | 
    
         
            +
                  end
         
     | 
| 
      
 72 
     | 
    
         
            +
                end
         
     | 
| 
      
 73 
     | 
    
         
            +
                include ValueEquality
         
     | 
| 
      
 74 
     | 
    
         
            +
              end
         
     | 
| 
      
 75 
     | 
    
         
            +
              
         
     | 
| 
      
 76 
     | 
    
         
            +
              
         
     | 
| 
      
 77 
     | 
    
         
            +
              
         
     | 
| 
      
 78 
     | 
    
         
            +
              
         
     | 
| 
      
 79 
     | 
    
         
            +
              class And < Binary; def self.shorthand; :And end end
         
     | 
| 
      
 80 
     | 
    
         
            +
              class Or < Binary; def self.shorthand; :Or end end
         
     | 
| 
      
 81 
     | 
    
         
            +
              class Not < Unary; def self.shorthand; :Not end end
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
              
         
     | 
| 
      
 84 
     | 
    
         
            +
              class Operation < Binary; end
         
     | 
| 
      
 85 
     | 
    
         
            +
              
         
     | 
| 
      
 86 
     | 
    
         
            +
              class Equal < Operation; def self.shorthand; :Eq end end
         
     | 
| 
      
 87 
     | 
    
         
            +
              class LessThan < Operation; def self.shorthand; :Lt end end
         
     | 
| 
      
 88 
     | 
    
         
            +
              class GreaterThan < Operation; def self.shorthand; :Gt end end
         
     | 
| 
      
 89 
     | 
    
         
            +
              class GreaterThanOrEqualTo < Operation; def self.shorthand; :Gte end end
         
     | 
| 
      
 90 
     | 
    
         
            +
              class LessThanOrEqualTo < Operation; def self.shorthand; :Lte end end
         
     | 
| 
      
 91 
     | 
    
         
            +
              
         
     | 
| 
      
 92 
     | 
    
         
            +
              
         
     | 
| 
      
 93 
     | 
    
         
            +
              module Shorthand
         
     | 
| 
      
 94 
     | 
    
         
            +
                def And(left, right) ::Predicated::And.new(left, right) end
         
     | 
| 
      
 95 
     | 
    
         
            +
                def Or(left, right) ::Predicated::Or.new(left, right) end
         
     | 
| 
      
 96 
     | 
    
         
            +
                def Not(inner) ::Predicated::Not.new(inner) end
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                def Eq(left, right) ::Predicated::Equal.new(left, right) end
         
     | 
| 
      
 99 
     | 
    
         
            +
                def Lt(left, right) ::Predicated::LessThan.new(left, right) end
         
     | 
| 
      
 100 
     | 
    
         
            +
                def Gt(left, right) ::Predicated::GreaterThan.new(left, right) end
         
     | 
| 
      
 101 
     | 
    
         
            +
                def Lte(left, right) ::Predicated::LessThanOrEqualTo.new(left, right) end
         
     | 
| 
      
 102 
     | 
    
         
            +
                def Gte(left, right) ::Predicated::GreaterThanOrEqualTo.new(left, right) end    
         
     | 
| 
      
 103 
     | 
    
         
            +
              end
         
     | 
| 
      
 104 
     | 
    
         
            +
              
         
     | 
| 
      
 105 
     | 
    
         
            +
              ALL_PREDICATE_CLASSES = [
         
     | 
| 
      
 106 
     | 
    
         
            +
                And, Or, Not, 
         
     | 
| 
      
 107 
     | 
    
         
            +
                Equal, LessThan, GreaterThan, LessThanOrEqualTo, GreaterThanOrEqualTo
         
     | 
| 
      
 108 
     | 
    
         
            +
              ]
         
     | 
| 
      
 109 
     | 
    
         
            +
            end
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
            require "predicated/print"
         
     | 
| 
         @@ -0,0 +1,62 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Predicated
         
     | 
| 
      
 2 
     | 
    
         
            +
              module PrintSupport
         
     | 
| 
      
 3 
     | 
    
         
            +
                def inspect(indent="")
         
     | 
| 
      
 4 
     | 
    
         
            +
                  indent + to_s
         
     | 
| 
      
 5 
     | 
    
         
            +
                end
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                private
         
     | 
| 
      
 8 
     | 
    
         
            +
                def part_to_s(thing)
         
     | 
| 
      
 9 
     | 
    
         
            +
                  part_to_str(thing) {|thing| thing.to_s}
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
                
         
     | 
| 
      
 12 
     | 
    
         
            +
                def part_inspect(thing, indent="")
         
     | 
| 
      
 13 
     | 
    
         
            +
                  part_to_str(thing, indent) {|thing| thing.inspect(indent)}
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
                
         
     | 
| 
      
 16 
     | 
    
         
            +
                def part_to_str(thing, indent="")
         
     | 
| 
      
 17 
     | 
    
         
            +
                  if thing.is_a?(String)
         
     | 
| 
      
 18 
     | 
    
         
            +
                    "'#{thing}'"
         
     | 
| 
      
 19 
     | 
    
         
            +
                  elsif thing.is_a?(Numeric) || thing.is_a?(TrueClass) || thing.is_a?(FalseClass)
         
     | 
| 
      
 20 
     | 
    
         
            +
                    thing.to_s
         
     | 
| 
      
 21 
     | 
    
         
            +
                  elsif thing.is_a?(Binary)
         
     | 
| 
      
 22 
     | 
    
         
            +
                    yield(thing)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  elsif thing.nil?
         
     | 
| 
      
 24 
     | 
    
         
            +
                    "nil"
         
     | 
| 
      
 25 
     | 
    
         
            +
                  else
         
     | 
| 
      
 26 
     | 
    
         
            +
                    "#{thing.class.name}{'#{thing.to_s}'}"
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
              class Unary < Predicate
         
     | 
| 
      
 32 
     | 
    
         
            +
                include PrintSupport
         
     | 
| 
      
 33 
     | 
    
         
            +
                def to_s
         
     | 
| 
      
 34 
     | 
    
         
            +
                  "#{self.class.shorthand}(#{part_to_s(inner)})"
         
     | 
| 
      
 35 
     | 
    
         
            +
                end    
         
     | 
| 
      
 36 
     | 
    
         
            +
              end
         
     | 
| 
      
 37 
     | 
    
         
            +
              
         
     | 
| 
      
 38 
     | 
    
         
            +
              class Binary < Predicate
         
     | 
| 
      
 39 
     | 
    
         
            +
                include PrintSupport
         
     | 
| 
      
 40 
     | 
    
         
            +
                def to_s
         
     | 
| 
      
 41 
     | 
    
         
            +
                  "#{self.class.shorthand}(#{part_to_s(left)},#{part_to_s(right)})"
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
              end
         
     | 
| 
      
 44 
     | 
    
         
            +
              
         
     | 
| 
      
 45 
     | 
    
         
            +
              module ContainerToString
         
     | 
| 
      
 46 
     | 
    
         
            +
                def inspect(indent="")
         
     | 
| 
      
 47 
     | 
    
         
            +
                  next_indent = indent + " " + " "
         
     | 
| 
      
 48 
     | 
    
         
            +
                  
         
     | 
| 
      
 49 
     | 
    
         
            +
                  str = "#{indent}#{self.class.shorthand}(\n"
         
     | 
| 
      
 50 
     | 
    
         
            +
                  str << "#{part_inspect(left, next_indent)},\n"
         
     | 
| 
      
 51 
     | 
    
         
            +
                  str << "#{part_inspect(right, next_indent)}\n"
         
     | 
| 
      
 52 
     | 
    
         
            +
                  str << "#{indent})"
         
     | 
| 
      
 53 
     | 
    
         
            +
                  str << "\n" if indent == ""
         
     | 
| 
      
 54 
     | 
    
         
            +
                  
         
     | 
| 
      
 55 
     | 
    
         
            +
                  str
         
     | 
| 
      
 56 
     | 
    
         
            +
                end
         
     | 
| 
      
 57 
     | 
    
         
            +
              end
         
     | 
| 
      
 58 
     | 
    
         
            +
              
         
     | 
| 
      
 59 
     | 
    
         
            +
              class And; include ContainerToString end
         
     | 
| 
      
 60 
     | 
    
         
            +
              class Or; include ContainerToString end
         
     | 
| 
      
 61 
     | 
    
         
            +
              
         
     | 
| 
      
 62 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,102 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
             
     | 
| 
      
 2 
     | 
    
         
            +
            class Object
         
     | 
| 
      
 3 
     | 
    
         
            +
              # todo: make this definition conditional
         
     | 
| 
      
 4 
     | 
    
         
            +
              # todo: move this to a monkey patch file
         
     | 
| 
      
 5 
     | 
    
         
            +
              def singleton_class
         
     | 
| 
      
 6 
     | 
    
         
            +
                 class << self
         
     | 
| 
      
 7 
     | 
    
         
            +
                   self
         
     | 
| 
      
 8 
     | 
    
         
            +
                 end
         
     | 
| 
      
 9 
     | 
    
         
            +
              end
         
     | 
| 
      
 10 
     | 
    
         
            +
            end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            module Predicated
         
     | 
| 
      
 13 
     | 
    
         
            +
              module Selectable
         
     | 
| 
      
 14 
     | 
    
         
            +
                # Make an Enumerable instance into a Selectable. 
         
     | 
| 
      
 15 
     | 
    
         
            +
                # This does for instances what "include Selectable" does for classes.
         
     | 
| 
      
 16 
     | 
    
         
            +
                # todo: rename?
         
     | 
| 
      
 17 
     | 
    
         
            +
                def self.bless_enumerable(enumerable, selectors)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  enumerable.singleton_class.instance_eval do
         
     | 
| 
      
 19 
     | 
    
         
            +
                    include Selectable
         
     | 
| 
      
 20 
     | 
    
         
            +
                    selector selectors
         
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                # merge several hashes into one, skipping nils
         
     | 
| 
      
 25 
     | 
    
         
            +
                # todo: unit test
         
     | 
| 
      
 26 
     | 
    
         
            +
                # todo: move onto Hash?
         
     | 
| 
      
 27 
     | 
    
         
            +
                def self.merge_many(*hashes)
         
     | 
| 
      
 28 
     | 
    
         
            +
                  result = {}
         
     | 
| 
      
 29 
     | 
    
         
            +
                  hashes.compact.each do |hash|
         
     | 
| 
      
 30 
     | 
    
         
            +
                    result.merge! hash
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end
         
     | 
| 
      
 32 
     | 
    
         
            +
                  result
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                SELECTORS = :@_predicated_selectors
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                def self.included(base)
         
     | 
| 
      
 38 
     | 
    
         
            +
                  base.extend ClassMethods
         
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                module ClassMethods
         
     | 
| 
      
 42 
     | 
    
         
            +
                  def selector(hash)
         
     | 
| 
      
 43 
     | 
    
         
            +
                    instance_variable_set(SELECTORS, Selectable.merge_many(instance_variable_get(SELECTORS), hash))
         
     | 
| 
      
 44 
     | 
    
         
            +
                  end
         
     | 
| 
      
 45 
     | 
    
         
            +
                end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                def selectors
         
     | 
| 
      
 48 
     | 
    
         
            +
                  class_selectors = self.class.instance_variable_get(SELECTORS)
         
     | 
| 
      
 49 
     | 
    
         
            +
                  singleton_selectors = self.singleton_class.instance_variable_get(SELECTORS)
         
     | 
| 
      
 50 
     | 
    
         
            +
                  instance_selectors = self.instance_variable_get(SELECTORS)
         
     | 
| 
      
 51 
     | 
    
         
            +
                  Selectable.merge_many(class_selectors, singleton_selectors, instance_selectors)
         
     | 
| 
      
 52 
     | 
    
         
            +
                end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                def select(*keys, &block)
         
     | 
| 
      
 55 
     | 
    
         
            +
                  if block_given?
         
     | 
| 
      
 56 
     | 
    
         
            +
                    super
         
     | 
| 
      
 57 
     | 
    
         
            +
                  else
         
     | 
| 
      
 58 
     | 
    
         
            +
                    key = keys.shift
         
     | 
| 
      
 59 
     | 
    
         
            +
                    result =
         
     | 
| 
      
 60 
     | 
    
         
            +
                      if key
         
     | 
| 
      
 61 
     | 
    
         
            +
                        selecting_proc = selectors[key]
         
     | 
| 
      
 62 
     | 
    
         
            +
                        raise "no selector found for '#{key}'.  current selectors: [#{selectors.collect { |k, v| k.to_s }.join(",")}]" unless selecting_proc
         
     | 
| 
      
 63 
     | 
    
         
            +
                        memos_for(:select)[key] ||= begin
         
     | 
| 
      
 64 
     | 
    
         
            +
                          super(&selecting_proc)
         
     | 
| 
      
 65 
     | 
    
         
            +
                        end
         
     | 
| 
      
 66 
     | 
    
         
            +
                      else
         
     | 
| 
      
 67 
     | 
    
         
            +
                        raise "select must be called with either a key or a block"
         
     | 
| 
      
 68 
     | 
    
         
            +
                      end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                    Selectable.bless_enumerable(result, selectors)
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                    if keys.length >= 1
         
     | 
| 
      
 73 
     | 
    
         
            +
                      result.select(*keys, &block)
         
     | 
| 
      
 74 
     | 
    
         
            +
                    else
         
     | 
| 
      
 75 
     | 
    
         
            +
                      result
         
     | 
| 
      
 76 
     | 
    
         
            +
                    end
         
     | 
| 
      
 77 
     | 
    
         
            +
                  end
         
     | 
| 
      
 78 
     | 
    
         
            +
                end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                private
         
     | 
| 
      
 81 
     | 
    
         
            +
                def memos_for(group)
         
     | 
| 
      
 82 
     | 
    
         
            +
                  @_predicated_memos ||= {}
         
     | 
| 
      
 83 
     | 
    
         
            +
                  @_predicated_memos[group] ||= {}
         
     | 
| 
      
 84 
     | 
    
         
            +
                end
         
     | 
| 
      
 85 
     | 
    
         
            +
              end
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
              all_basic_selectors = {:all => proc{|predicate, enumerable|true}}.merge(
         
     | 
| 
      
 88 
     | 
    
         
            +
                ([Unary, Binary, Operation] + ALL_PREDICATE_CLASSES).inject({}) do |h, klass|
         
     | 
| 
      
 89 
     | 
    
         
            +
                  h[klass] = proc{|predicate, enumerable|predicate.is_a?(klass)}
         
     | 
| 
      
 90 
     | 
    
         
            +
                  h
         
     | 
| 
      
 91 
     | 
    
         
            +
                end
         
     | 
| 
      
 92 
     | 
    
         
            +
              )
         
     | 
| 
      
 93 
     | 
    
         
            +
              
         
     | 
| 
      
 94 
     | 
    
         
            +
              ALL_PREDICATE_CLASSES.each do |klass|
         
     | 
| 
      
 95 
     | 
    
         
            +
                klass.class_eval do 
         
     | 
| 
      
 96 
     | 
    
         
            +
                  klass.send(:include, Selectable)
         
     | 
| 
      
 97 
     | 
    
         
            +
                  klass.selector all_basic_selectors
         
     | 
| 
      
 98 
     | 
    
         
            +
                end
         
     | 
| 
      
 99 
     | 
    
         
            +
              end
         
     | 
| 
      
 100 
     | 
    
         
            +
            end
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
             
     |