predicated 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.
Files changed (51) hide show
  1. data/README.markdown +183 -1
  2. data/lib/predicated.rb +1 -3
  3. data/lib/predicated/constrain.rb +2 -0
  4. data/lib/predicated/evaluate.rb +25 -6
  5. data/lib/predicated/from/callable_object.rb +7 -8
  6. data/lib/predicated/from/json.rb +59 -0
  7. data/lib/predicated/from/{ruby_string.rb → ruby_code_string.rb} +13 -5
  8. data/lib/predicated/from/{url_fragment.rb → url_part.rb} +17 -10
  9. data/lib/predicated/from/xml.rb +61 -0
  10. data/lib/predicated/predicate.rb +62 -43
  11. data/lib/predicated/print.rb +28 -16
  12. data/lib/predicated/selectable.rb +102 -0
  13. data/lib/predicated/simple_templated_predicate.rb +79 -0
  14. data/lib/predicated/string_utils.rb +20 -0
  15. data/lib/predicated/to/arel.rb +28 -22
  16. data/lib/predicated/to/json.rb +48 -0
  17. data/lib/predicated/to/sentence.rb +17 -14
  18. data/lib/predicated/to/solr.rb +15 -0
  19. data/lib/predicated/to/xml.rb +67 -0
  20. data/lib/predicated/version.rb +2 -2
  21. data/test/canonical_transform_cases.rb +67 -0
  22. data/test/constrain_test.rb +20 -11
  23. data/test/enumerable_test.rb +6 -34
  24. data/test/equality_test.rb +15 -4
  25. data/test/evaluate_test.rb +31 -26
  26. data/test/from/callable_object_canonical_test.rb +43 -0
  27. data/test/from/callable_object_test.rb +16 -40
  28. data/test/from/json_test.rb +83 -0
  29. data/test/from/ruby_code_string_canonical_test.rb +37 -0
  30. data/test/from/ruby_code_string_test.rb +103 -0
  31. data/test/from/{url_fragment_parser_test.rb → url_part_parser_test.rb} +20 -13
  32. data/test/from/url_part_test.rb +48 -0
  33. data/test/from/xml_test.rb +57 -0
  34. data/test/json_conversion_test.rb +33 -0
  35. data/test/print_test.rb +26 -25
  36. data/test/selectable_test.rb +123 -0
  37. data/test/simple_templated_predicate_test.rb +39 -0
  38. data/test/suite.rb +2 -4
  39. data/test/test_helper.rb +26 -4
  40. data/test/test_helper_with_wrong.rb +3 -2
  41. data/test/to/arel_test.rb +71 -31
  42. data/test/to/json_test.rb +74 -0
  43. data/test/to/sentence_test.rb +41 -34
  44. data/test/to/solr_test.rb +39 -0
  45. data/test/to/xml_test.rb +72 -0
  46. data/test/xml_conversion_test.rb +34 -0
  47. metadata +44 -16
  48. data/lib/predicated/selector.rb +0 -55
  49. data/test/from/ruby_string_test.rb +0 -135
  50. data/test/from/url_fragment_test.rb +0 -37
  51. data/test/selector_test.rb +0 -82
@@ -6,30 +6,36 @@ module Predicated
6
6
 
7
7
  require_gem_version("arel", "0.4.0")
8
8
 
9
- {And => Arel::Predicates::And,
10
- Or => Arel::Predicates::Or}.each do |predicated_class, arel_class|
11
-
12
- predicated_class.class_eval %{
13
- def to_arel
14
- #{arel_class.name}.new(left.to_arel, right.to_arel)
15
- end
16
- }
17
-
18
-
9
+ class And
10
+ def to_arel(arel_table)
11
+ Arel::Predicates::And.new(left.to_arel(arel_table), right.to_arel(arel_table))
12
+ end
19
13
  end
20
14
 
21
- {Equal => Arel::Predicates::Equality,
22
- GreaterThan => Arel::Predicates::GreaterThan,
23
- LessThan => Arel::Predicates::LessThan,
24
- GreaterThanOrEqualTo => Arel::Predicates::GreaterThanOrEqualTo,
25
- LessThanOrEqualTo => Arel::Predicates::LessThanOrEqualTo}.each do |predicated_class, arel_class|
26
-
27
- predicated_class.class_eval %{
28
- def to_arel
29
- #{arel_class.name}.new(left, right)
30
- end
31
- }
32
-
15
+ class Or
16
+ def to_arel(arel_table)
17
+ Arel::Predicates::Or.new(left.to_arel(arel_table), right.to_arel(arel_table))
18
+ end
19
+ end
20
+
21
+ class Not
22
+ def to_arel(arel_table)
23
+ Arel::Predicates::Not.new(inner.to_arel(arel_table))
24
+ end
33
25
  end
34
26
 
27
+
28
+ class Operation
29
+ def to_arel(arel_table)
30
+ arel_class.new(arel_table.attributes[left], right)
31
+ end
32
+ end
33
+
34
+ class Equal; def arel_class; Arel::Predicates::Equality end end
35
+ class LessThan; def arel_class; Arel::Predicates::LessThan end end
36
+ class GreaterThan; def arel_class; Arel::Predicates::GreaterThan end end
37
+ class LessThanOrEqualTo; def arel_class; Arel::Predicates::LessThanOrEqualTo end end
38
+ class GreaterThanOrEqualTo; def arel_class; Arel::Predicates::GreaterThanOrEqualTo end end
39
+
40
+
35
41
  end
@@ -0,0 +1,48 @@
1
+ require "predicated/predicate"
2
+
3
+ module Predicated
4
+
5
+ require_gem_version("json", "1.1.9")
6
+
7
+ module JsonStructToJsonStr
8
+ def to_json_str
9
+ JSON.pretty_generate(to_json_struct)
10
+ end
11
+ end
12
+
13
+ class And
14
+ include JsonStructToJsonStr
15
+ def to_json_struct
16
+ {"and" => [left.to_json_struct, right.to_json_struct]}
17
+ end
18
+ end
19
+
20
+ class Or
21
+ include JsonStructToJsonStr
22
+ def to_json_struct
23
+ {"or" => [left.to_json_struct, right.to_json_struct]}
24
+ end
25
+ end
26
+
27
+ class Not
28
+ include JsonStructToJsonStr
29
+ def to_json_struct
30
+ {"not" => inner.to_json_struct}
31
+ end
32
+ end
33
+
34
+ class Operation
35
+ include JsonStructToJsonStr
36
+ def to_json_struct
37
+ [left, json_sign, right]
38
+ end
39
+ end
40
+
41
+ class Equal; private; def json_sign; "==" end end
42
+ class LessThan; private; def json_sign; "<" end end
43
+ class GreaterThan; private; def json_sign; ">" end end
44
+ class LessThanOrEqualTo; private; def json_sign; "<=" end end
45
+ class GreaterThanOrEqualTo; private; def json_sign; ">=" end end
46
+
47
+
48
+ end
@@ -3,18 +3,28 @@ require "predicated/evaluate"
3
3
 
4
4
  module Predicated
5
5
 
6
- module ContainerSentence
6
+ module Conjunction
7
7
  def to_sentence
8
- left.to_sentence + "#{joining_str}" + right.to_sentence
8
+ left.to_sentence + joining_str + right.to_sentence
9
9
  end
10
10
 
11
11
  def to_negative_sentence
12
12
  "This is not true: " + to_sentence
13
13
  end
14
14
  end
15
+
16
+ class And; include Conjunction; def joining_str; " and " end; end
17
+ class Or; include Conjunction; def joining_str; " or " end;end
15
18
 
16
- class And; include ContainerSentence; def joining_str; " and " end; end
17
- class Or; include ContainerSentence; def joining_str; " or " end;end
19
+ class Not
20
+ def to_sentence
21
+ inner.to_negative_sentence
22
+ end
23
+
24
+ def to_negative_sentence
25
+ inner.to_sentence
26
+ end
27
+ end
18
28
 
19
29
  class Operation
20
30
 
@@ -76,16 +86,9 @@ module Predicated
76
86
  end
77
87
 
78
88
  def format_value(value)
79
- if value.is_a?(String)
80
- "'" + value.to_s + "'"
81
- elsif value.nil?
82
- "nil"
83
- elsif value.is_a?(Numeric) || value.is_a?(TrueClass) || value.is_a?(FalseClass)
84
- value.to_s
85
- else
86
- "'" + value.inspect + "'"
87
- end
89
+ value.inspect
88
90
  end
91
+
89
92
  end
90
93
 
91
- end
94
+ end
@@ -0,0 +1,15 @@
1
+ require "predicated/predicate"
2
+
3
+ module Predicated
4
+
5
+ class And; def to_solr; "(#{left.to_solr} AND #{right.to_solr})" end end
6
+ class Or; def to_solr; "(#{left.to_solr} OR #{right.to_solr})" end end
7
+ class Not; def to_solr; "NOT(#{inner.to_solr})" end end
8
+
9
+ class Equal; def to_solr; "#{left}:#{right}" end end
10
+ class GreaterThan; def to_solr; "#{left}:[#{(right+1)} TO *]" end end
11
+ class LessThan; def to_solr; "#{left}:[* TO #{(right-1)}]" end end
12
+ class GreaterThanOrEqualTo; def to_solr; "#{left}:[#{right} TO *]" end end
13
+ class LessThanOrEqualTo; def to_solr; "#{left}:[* TO #{right}]" end end
14
+
15
+ end
@@ -0,0 +1,67 @@
1
+ require "predicated/predicate"
2
+
3
+ module Predicated
4
+
5
+ module ContainerToXml
6
+ private
7
+ def to_xml_with_tag_name(indent, tag_name)
8
+ inner = %{\n#{left.to_xml(indent + " ")}\n#{right.to_xml(indent + " ")}}
9
+ "#{indent}<#{tag_name}>#{inner}\n#{indent}</#{tag_name}>"
10
+ end
11
+ end
12
+
13
+ class And
14
+ include ContainerToXml
15
+ def to_xml(indent="")
16
+ to_xml_with_tag_name(indent, "and")
17
+ end
18
+ end
19
+
20
+ class Or
21
+ include ContainerToXml
22
+ def to_xml(indent="")
23
+ to_xml_with_tag_name(indent, "or")
24
+ end
25
+ end
26
+
27
+ class Not
28
+ def to_xml(indent="")
29
+ "#{indent}<not>\n#{inner.to_xml(indent + " ")}\n#{indent}</not>"
30
+ end
31
+ end
32
+
33
+ class Operation
34
+ def to_xml(indent="")
35
+ "#{indent}<#{tag_name}><left>#{escape(left)}</left><right>#{escape(right)}</right></#{tag_name}>"
36
+ end
37
+
38
+ private
39
+
40
+ CONVERSION_TABLE = [
41
+ ['&', '&amp;'],
42
+ ['<', '&lt;'],
43
+ ['>', '&gt;'],
44
+ ["'", '&apos;'],
45
+ ['"', '&quot;']
46
+ ]
47
+
48
+ #it's fast. see http://groups.google.com/group/ruby-talk-google/browse_thread/thread/c0280bab8a037184/9b8ca81c2607189d?hl=en&ie=UTF-8
49
+ def escape(value)
50
+ if value.class == String
51
+ value.gsub(/['"&<>]/) do |match|
52
+ CONVERSION_TABLE.assoc(match).last
53
+ end
54
+ else
55
+ value
56
+ end
57
+ end
58
+ end
59
+
60
+ class Equal; private; def tag_name; "equal" end end
61
+ class LessThan; private; def tag_name; "lessThan" end end
62
+ class GreaterThan; private; def tag_name; "greaterThan" end end
63
+ class LessThanOrEqualTo; private; def tag_name; "lessThanOrEqualTo" end end
64
+ class GreaterThanOrEqualTo; private; def tag_name; "greaterThanOrEqualTo" end end
65
+
66
+
67
+ end
@@ -1,3 +1,3 @@
1
1
  module Predicated
2
- VERSION = "0.1.0" unless defined?(Predicated::VERSION)
3
- end
2
+ VERSION = "0.2.0" unless defined?(Predicated::VERSION)
3
+ end
@@ -0,0 +1,67 @@
1
+ module CanonicalTransformCases
2
+
3
+ module ClassMethods
4
+ def create_canonical_tests(expectations, proper_typing=true)
5
+ val = {
6
+ :one => 1,
7
+ :two => 2,
8
+ :three => 3,
9
+ :true_value => true,
10
+ :false_value => false
11
+ }
12
+
13
+ val.each{|k,v|val[k]=v.to_s} unless proper_typing
14
+
15
+ tests = {
16
+ "simple operations" => {
17
+ "eq" => Predicate{ Eq("a",val[:three]) },
18
+ "gt" => Predicate{ Gt("a",val[:three]) },
19
+ "lt" => Predicate{ Lt("a",val[:three]) },
20
+ "gte" => Predicate{ Gte("a",val[:three]) },
21
+ "lte" => Predicate{ Lte("a",val[:three]) }
22
+ },
23
+ "primitive types" => {
24
+ "true" => Predicate{ Eq("a",val[:true_value]) },
25
+ "false" => Predicate{ Eq("a",val[:false_value]) },
26
+ "string" => Predicate{ Eq("a","yyy") },
27
+ },
28
+ "not" => {
29
+ "simple" => Predicate{ Not(Eq("a",val[:true_value])) }
30
+ },
31
+ "simple and / or" => {
32
+ "and" => Predicate{ And(Eq("a", val[:one]),Eq("b", val[:two])) },
33
+ "or" => Predicate{ Or(Eq("a", val[:one]),Eq("b", val[:two])) }
34
+ },
35
+ "complex and / or" => {
36
+ "or and" => Predicate{ Or(And(Eq("a", val[:one]),Eq("b", val[:two])), Eq("c",val[:three])) }
37
+ }
38
+ }
39
+
40
+ tests.each do |test_name, cases|
41
+ test test_name do
42
+
43
+ not_found =
44
+ cases.keys.sort.select do |case_name|
45
+ expectations[test_name].nil? ||
46
+ expectations[test_name][case_name].nil?
47
+ end
48
+
49
+ raise "no expectation defined for test: '#{test_name}' cases: [#{not_found.join(", ")}]" unless not_found.empty?
50
+
51
+ cases.each do |case_name, predicate|
52
+ actual = if block_given?
53
+ yield(predicate)
54
+ else
55
+ predicate
56
+ end
57
+ assert { actual == expectations[test_name][case_name] }
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ def self.included(other)
65
+ other.extend(ClassMethods)
66
+ end
67
+ end
@@ -1,10 +1,10 @@
1
- require "test/test_helper_with_wrong"
1
+ require "./test/test_helper_with_wrong"
2
2
 
3
3
  require "predicated/predicate"
4
4
  require "predicated/constrain"
5
5
  include Predicated
6
6
 
7
- apropos %{constraints are rules about the content and structure of predicates.
7
+ regarding %{constraints are rules about the content and structure of predicates.
8
8
  a predicate might violate a constraint} do
9
9
 
10
10
  before do
@@ -16,16 +16,25 @@ apropos %{constraints are rules about the content and structure of predicates.
16
16
  @not_more_than_two_levels_deep =
17
17
  Constraint.new(:name => "Limited to two levels deep",
18
18
  :check_that => proc{|predicate, ancestors| ancestors.length<=2})
19
+
20
+ @one = Predicate{Eq(1,1)}
21
+ @two = Predicate{Eq(2,2)}
22
+ @three = Predicate{Eq(3,3)}
23
+
24
+ @one_and_three = Predicate{And(Eq(1,1), Eq(3,3))}
25
+ @one_and_two = Predicate{And(Eq(1,1), Eq(2,2))}
26
+
27
+ @deeply_nested = Predicate{Or(Or(And(Eq(1,1), Eq(3,3)), Eq(4,4)), Eq(5,5))}
19
28
  end
20
29
 
21
30
  test "apply to each predicate - simple" do
22
31
  constraints = Constraints.new.add(@value_not_equal_to_two)
23
32
 
24
- assert{ constraints.check(Predicate{Eq(1,1)}).pass? }
25
- deny { constraints.check(Predicate{Eq(1,2)}).pass? }
33
+ assert{ constraints.check(@one).pass? }
34
+ deny { constraints.check(@two).pass? }
26
35
 
27
- assert{ constraints.check(Predicate{And(Eq(1,1), Eq(1,3))}).pass? }
28
- deny { constraints.check(Predicate{And(Eq(1,1), Eq(1,2))}).pass? }
36
+ assert{ constraints.check(@one_and_three).pass? }
37
+ deny { constraints.check(@one_and_two).pass? }
29
38
  end
30
39
 
31
40
  test "apply each to each predicate - many constraints" do
@@ -34,11 +43,11 @@ apropos %{constraints are rules about the content and structure of predicates.
34
43
  add(@value_not_equal_to_two).
35
44
  add(@not_more_than_two_levels_deep)
36
45
 
37
- assert{ constraints.check(Predicate{And(Eq(1,1), Eq(3,3))}).pass? }
38
- deny { constraints.check(Predicate{And(Eq(1,1), Eq(2,2))}).pass? }
46
+ assert{ constraints.check(@one_and_three).pass? }
47
+ deny { constraints.check(@one_and_two).pass? }
39
48
 
40
- assert{ constraints.check(Predicate{And(Eq(1,1), Eq(3,3))}).pass? }
41
- deny { constraints.check(Predicate{Or(Or(And(Eq(1,1), Eq(3,3)), Eq(4,4)), Eq(5,5))}).pass? }
49
+ assert{ constraints.check(@one_and_three).pass? }
50
+ deny { constraints.check(@deeply_nested).pass? }
42
51
  end
43
52
 
44
53
  test "equality" do
@@ -60,7 +69,7 @@ apropos %{constraints are rules about the content and structure of predicates.
60
69
  along with the offending predicates} do
61
70
  constraints = Constraints.new.add(@value_not_equal_to_two)
62
71
 
63
- result = constraints.check(Predicate{Eq(1,1)})
72
+ result = constraints.check(@one)
64
73
  assert{ result.pass? }
65
74
  assert{ result.violations == {} }
66
75
 
@@ -1,9 +1,9 @@
1
- require "test/test_helper_with_wrong"
1
+ require "./test/test_helper_with_wrong"
2
2
 
3
3
  require "predicated/predicate"
4
4
  include Predicated
5
5
 
6
- apropos "you can flip through the predicate tree, like any enumerable. a list of ancestors of each node are provided" do
6
+ regarding "you can flip through the predicate tree, like any enumerable. a list of ancestors of each node are provided" do
7
7
 
8
8
  test "simple" do
9
9
  assert { Predicate { Eq(1, 2) }.to_a == [[Predicate { Eq(1, 2) }, []]] }
@@ -23,38 +23,10 @@ apropos "you can flip through the predicate tree, like any enumerable. a list o
23
23
  }
24
24
  end
25
25
 
26
- end
27
-
28
- apropos "there are convenient selectors defined for getting things out of a predicate" do
29
- class Array
30
- def predicates
31
- collect{|p, a|p}
32
- end
26
+ test "not" do
27
+ the_top = Predicate { Not(Eq(1, 2)) }
28
+ assert { the_top.to_a == [[the_top, []], [Predicate { Eq(1, 2) }, [the_top]]] }
33
29
  end
34
-
35
- it "gets predicate parts by type" do
36
- root = Predicate { And(Eq(1, 2), Or(Eq(3, 4), Eq(5, 6))) }
37
- the_or = Predicate { Or(Eq(3, 4), Eq(5, 6)) }
38
-
39
- assert{ root.select(:all).predicates == [root, Equal.new(1, 2), the_or, Equal.new(3, 4), Equal.new(5, 6)] }
40
30
 
41
- assert{ root.select(And).predicates == [root] }
42
- assert{ root.select(Or).predicates == [the_or] }
43
- assert{ root.select(Equal).predicates == [Equal.new(1, 2), Equal.new(3, 4), Equal.new(5, 6)] }
44
- assert{ root.select(GreaterThan).predicates == [] }
45
-
46
- gt_lt = Predicate { And(Gt(1, 2), Lt(3, 4)) }
47
- assert{ gt_lt.select(GreaterThan).predicates == [GreaterThan.new(1, 2)] }
48
- assert{ gt_lt.select(LessThan).predicates == [LessThan.new(3, 4)] }
31
+ end
49
32
 
50
- gte_lte = Predicate { And(Gte(1, 2), Lte(3, 4)) }
51
- assert{ gte_lte.select(GreaterThanOrEqualTo).predicates == [GreaterThanOrEqualTo.new(1, 2)] }
52
- assert{ gte_lte.select(LessThanOrEqualTo).predicates == [LessThanOrEqualTo.new(3, 4)] }
53
-
54
- mixed = Predicate { And(Eq(1, 2), Or(Gt(3, 4), Lt(5, 6))) }
55
- mixed_or = Predicate { Or(Gt(3, 4), Lt(5, 6)) }
56
- assert{ mixed.select(Operation).predicates == [Equal.new(1, 2), GreaterThan.new(3, 4), LessThan.new(5, 6)] }
57
- assert{ mixed.select(Operation).select(Equal).predicates == [Equal.new(1, 2)] }
58
- assert{ mixed.select(Binary).predicates == [mixed, Equal.new(1, 2), mixed_or, GreaterThan.new(3, 4), LessThan.new(5, 6)] }
59
- end
60
- end
@@ -1,21 +1,32 @@
1
- require "test/test_helper_with_wrong"
1
+ require "./test/test_helper_with_wrong"
2
2
 
3
3
  require "predicated/predicate"
4
4
  include Predicated
5
5
 
6
- apropos "prove value equality" do
6
+ regarding "prove value equality" do
7
7
 
8
8
  test "simple" do
9
9
  assert { Predicate { Eq(1, 1) } == Predicate { Eq(1, 1) } }
10
10
  deny { Predicate { Eq(1, 1) } == Predicate { Eq(1, 99) } }
11
11
  end
12
12
 
13
+ test "unary" do
14
+ assert { Predicate { Not(Eq(1, 1)) } == Predicate { Not(Eq(1, 1)) } }
15
+ deny { Predicate { Not(Eq(1, 1)) } == Predicate { Not(Eq(99, 99)) } }
16
+ end
17
+
13
18
  test "complex" do
14
19
  assert { Predicate { And(Eq(1, 1), Or(Eq(2, 2), Eq(3, 3))) } ==
15
20
  Predicate { And(Eq(1, 1), Or(Eq(2, 2), Eq(3, 3))) } }
16
21
 
17
- deny { Predicate { And(Eq(1, 1), Or(Eq(2, 2), Eq(3, 3))) } ==
22
+ deny { Predicate { And(Eq(1, 1), Or(Eq(2, 2), Eq(3, 3))) } ==
18
23
  Predicate { And(Eq(1, 1), Or(Eq(2, 99), Eq(3, 3))) } }
19
24
  end
20
25
 
21
- end
26
+ end
27
+
28
+ regarding "predicate base class. not sure I'm happy with the implementation...too tricky" do
29
+ test "all predicates descend from a predicate base class. it's a marker class" do
30
+ assert{ And.new(Equal.new(1,1),Equal.new(2,2)).is_a?(Predicate) }
31
+ end
32
+ end