predicated 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -1,11 +1,11 @@
1
- require "test/test_helper_with_wrong"
1
+ require "./test/test_helper_with_wrong"
2
2
 
3
3
  require "predicated/evaluate"
4
4
  include Predicated
5
5
 
6
- apropos "evaluate a predicate as boolean logic in ruby. change the context by providing and optional binding." do
6
+ regarding "evaluate a predicate as boolean logic in ruby. change the context by providing and optional binding." do
7
7
 
8
- apropos "proving out basic operations" do
8
+ regarding "proving out basic operations" do
9
9
  test "equals" do
10
10
  assert { Predicate { Eq(1, 1) }.evaluate }
11
11
  deny { Predicate { Eq(1, 2) }.evaluate }
@@ -36,7 +36,7 @@ apropos "evaluate a predicate as boolean logic in ruby. change the context by p
36
36
  end
37
37
  end
38
38
 
39
- apropos "comparing values of different data types" do
39
+ regarding "comparing values of different data types" do
40
40
  test "strings" do
41
41
  assert { Predicate { Eq("1", "1") }.evaluate }
42
42
  deny { Predicate { Eq("1", 1) }.evaluate }
@@ -62,17 +62,6 @@ apropos "evaluate a predicate as boolean logic in ruby. change the context by p
62
62
  deny { Predicate { Eq(1, nil) }.evaluate }
63
63
  end
64
64
 
65
- class Color
66
- attr_reader :name
67
- def initialize(name)
68
- @name = name
69
- end
70
-
71
- def ==(other)
72
- other.is_a?(Color) && @name == other.name
73
- end
74
- end
75
-
76
65
  test "objects" do
77
66
  assert { Predicate { Eq(Color.new("red"), Color.new("red")) }.evaluate }
78
67
  deny { Predicate { Eq(Color.new("red"), Color.new("BLUE")) }.evaluate }
@@ -83,7 +72,7 @@ apropos "evaluate a predicate as boolean logic in ruby. change the context by p
83
72
  end
84
73
 
85
74
 
86
- apropos "and" do
75
+ regarding "and" do
87
76
  test "left and right must be true" do
88
77
  assert { Predicate { And( Eq(1, 1), Eq(2, 2) ) }.evaluate }
89
78
  deny { Predicate { And( Eq(99, 1), Eq(2, 2) ) }.evaluate }
@@ -104,7 +93,7 @@ apropos "evaluate a predicate as boolean logic in ruby. change the context by p
104
93
  end
105
94
  end
106
95
 
107
- apropos "or" do
96
+ regarding "or" do
108
97
  test "one of left or right must be true" do
109
98
  assert { Predicate { Or(true, true) }.evaluate }
110
99
  assert { Predicate { Or(true, false) }.evaluate }
@@ -113,32 +102,48 @@ apropos "evaluate a predicate as boolean logic in ruby. change the context by p
113
102
  end
114
103
  end
115
104
 
116
- apropos "evaluate adds a generic 'call' class. that is, object.message(args)" do
105
+ regarding "not" do
106
+ test "simple negation" do
107
+ assert { Predicate { Not(Eq(3, 2)) }.evaluate }
108
+ deny { Predicate { Not(Eq(2, 2)) }.evaluate }
109
+ end
110
+
111
+ test "complex" do
112
+ assert { Predicate { Not( And(Eq(2, 2),false) ) }.evaluate }
113
+ deny { Predicate { Not( And(Eq(2, 2),true) ) }.evaluate }
114
+ end
115
+ end
116
+
117
+ regarding "evaluate adds a generic 'call' class. that is, object.message(args)" do
117
118
  test "evaluate simple calls" do
118
119
  assert { Predicate { Call("abc", :include?, "bc") }.evaluate }
119
- deny { Predicate { Call("abc", :include?, "ZZ") }.evaluate }
120
+ deny { Predicate { Call("abc", :include?, "ZZ") }.evaluate }
120
121
  end
121
122
 
122
123
  test "nil call. call defaults to no args if none are specified" do
123
124
  assert { Predicate { Call(nil, :nil?, []) }.evaluate }
124
- deny { Predicate { Call("abc", :nil?, []) }.evaluate }
125
+ deny { Predicate { Call("abc", :nil?, []) }.evaluate }
125
126
 
126
127
  assert { Predicate { Call(nil, :nil?) }.evaluate }
127
- deny { Predicate { Call("abc", :nil?) }.evaluate }
128
+ deny { Predicate { Call("abc", :nil?) }.evaluate }
128
129
  end
129
130
 
130
131
  test "inspect" do
131
- assert{ Call.new("abc", :include?, "bc").inspect == "Call('abc'.include?('bc'))" }
132
+ assert { Call.new("abc", :include?, "bc").inspect == "Call('abc'.include?('bc'))" }
133
+ end
134
+
135
+ test "inspect, empty right hand side" do
136
+ assert { Call.new("abc", :nil?).inspect == "Call('abc'.nil?)" }
132
137
  end
133
138
 
134
139
  test "call equality" do
135
140
  assert { Call.new("abc", :include?, "bc") == Call.new("abc", :include?, "bc") }
136
- deny { Call.new("ZZZ", :include?, "bc") == Call.new("abc", :include?, "bc") }
137
- deny { Call.new("abc", :zzz, "bc") == Call.new("abc", :include?, "bc") }
138
- deny { Call.new("abc", :include?, "ZZZ") == Call.new("abc", :include?, "bc") }
141
+ deny { Call.new("ZZZ", :include?, "bc") == Call.new("abc", :include?, "bc") }
142
+ deny { Call.new("abc", :zzz, "bc") == Call.new("abc", :include?, "bc") }
143
+ deny { Call.new("abc", :include?, "ZZZ") == Call.new("abc", :include?, "bc") }
139
144
  end
140
145
 
141
146
 
142
147
  end
143
148
 
144
- end
149
+ end
@@ -0,0 +1,43 @@
1
+ require "./test/test_helper_with_wrong"
2
+ require "./test/canonical_transform_cases"
3
+
4
+ if RUBY_VERSION =~/^1.9/
5
+ puts "skipping callable object-related tests in 1.9"
6
+ else
7
+
8
+ require "predicated/from/callable_object"
9
+ include Predicated
10
+
11
+ regarding "callable object - canoical transform cases" do
12
+ include CanonicalTransformCases
13
+
14
+ @expectations = {
15
+ "simple operations" => {
16
+ "eq" => Predicate.from_callable_object{"a"==3},
17
+ "gt" => Predicate.from_callable_object{"a">3},
18
+ "lt" => Predicate.from_callable_object{"a"<3},
19
+ "gte" => Predicate.from_callable_object{"a">=3},
20
+ "lte" => Predicate.from_callable_object{"a"<=3}
21
+ },
22
+ "primitive types" => {
23
+ "false" => Predicate.from_callable_object{"a"==false},
24
+ "true" => Predicate.from_callable_object{"a"==true},
25
+ "string" => Predicate.from_callable_object{"a"=="yyy"}
26
+ },
27
+ "not" => {
28
+ "simple" => Predicate.from_callable_object{!("a"==true)}
29
+ },
30
+ "simple and / or" => {
31
+ #parens are necessary around AND's in solr in order to force precedence
32
+ "and" => Predicate.from_callable_object{"a"==1 && "b"==2},
33
+ "or" => Predicate.from_callable_object{"a"==1 || "b"==2}
34
+ },
35
+ "complex and / or" => {
36
+ "or and" => Predicate.from_callable_object{"a"==1 && "b"==2 || "c"==3}
37
+ }
38
+ }
39
+
40
+ create_canonical_tests(@expectations)
41
+ end
42
+
43
+ end
@@ -1,25 +1,17 @@
1
- require "test/test_helper"
1
+ require "./test/test_helper"
2
+ require "./test/canonical_transform_cases"
2
3
 
3
- require "predicated/from/callable_object"
4
- include Predicated
5
4
 
6
- apropos "convert a ruby callable object - a proc or lambda - into a predicate" do
5
+ if RUBY_VERSION =~/^1.9/
6
+ puts "skipping callable object-related tests in 1.9"
7
+ else
7
8
 
8
- apropos "basic operations" do
9
+ require "predicated/from/callable_object"
10
+ include Predicated
11
+
12
+ regarding "convert a ruby callable object - a proc or lambda - into a predicate" do
9
13
 
10
- test "simple signs" do
11
- assert_equal Predicate.from_callable_object{1==1}, Predicate{ Eq(1,1) }
12
- assert_equal Predicate.from_callable_object{1 == 1}, Predicate{ Eq(1,1) }
13
- assert_equal Predicate.from_callable_object{0<1}, Predicate{ Lt(0,1) }
14
- assert_equal Predicate.from_callable_object{2>1}, Predicate{ Gt(2,1) }
15
- assert_equal Predicate.from_callable_object{1<=1}, Predicate{ Lte(1,1) }
16
- assert_equal Predicate.from_callable_object{1>=1}, Predicate{ Gte(1,1) }
17
- end
18
-
19
- test "primitive types" do
20
- assert_equal Predicate.from_callable_object{false==true}, Predicate{ Eq(false,true) }
21
- assert_equal Predicate.from_callable_object{"yyy"=="zzz"}, Predicate{ Eq("yyy","zzz") }
22
- end
14
+ regarding "basic operations" do
23
15
 
24
16
  test "complex types" do
25
17
  assert_equal Predicate.from_callable_object{Color.new("red")==Color.new("blue")},
@@ -29,24 +21,11 @@ apropos "convert a ruby callable object - a proc or lambda - into a predicate" d
29
21
  Predicate{ Eq({1=>2},{"a"=>"b"}) }
30
22
  end
31
23
 
32
- test "simple and + or" do
33
- assert_equal Predicate.from_callable_object{1==1 && 2==2}, Predicate{ And(Eq(1,1),Eq(2,2)) }
24
+ test "word and / or" do
34
25
  assert_equal Predicate.from_callable_object{1==1 and 2==2}, Predicate{ And(Eq(1,1),Eq(2,2)) }
35
- assert_equal Predicate.from_callable_object{1==1 || 2==2}, Predicate{ Or(Eq(1,1),Eq(2,2)) }
36
26
  assert_equal Predicate.from_callable_object{1==1 or 2==2}, Predicate{ Or(Eq(1,1),Eq(2,2)) }
37
27
  end
38
-
39
- class Color
40
- attr_reader :name
41
- def initialize(name)
42
- @name = name
43
- end
44
-
45
- def ==(other)
46
- other.is_a?(Color) && @name == other.name
47
- end
48
- end
49
-
28
+
50
29
  test "substitute in from the binding" do
51
30
  a = 1
52
31
  b = "1"
@@ -65,11 +44,6 @@ apropos "convert a ruby callable object - a proc or lambda - into a predicate" d
65
44
  end
66
45
 
67
46
 
68
- test "complex and + or" do
69
- assert_equal Predicate.from_callable_object{1==1 && 2==2 || 3==3},
70
- Predicate{ Or( And(Eq(1,1),Eq(2,2)), Eq(3,3) ) }
71
- end
72
-
73
47
  test "parens change precedence" do
74
48
  assert_equal Predicate.from_callable_object{1==1 || 2==2 && 3==3},
75
49
  Predicate{ Or( Eq(1,1), And(Eq(2,2),Eq(3,3)) ) }
@@ -92,11 +66,13 @@ apropos "convert a ruby callable object - a proc or lambda - into a predicate" d
92
66
 
93
67
  end
94
68
 
95
- apropos "errors" do
69
+ regarding "errors" do
96
70
  test "predicates only" do
97
71
  assert_raises(Predicated::Predicate::DontKnowWhatToDoWithThisSexpError) do
98
72
  Predicate.from_callable_object{a=1}
99
73
  end
100
74
  end
101
75
  end
102
- end
76
+ end
77
+
78
+ end
@@ -0,0 +1,83 @@
1
+ require "./test/test_helper_with_wrong"
2
+
3
+ require "predicated/from/json"
4
+ require "./test/canonical_transform_cases"
5
+ include Predicated
6
+
7
+ regarding "convert a json string to a predicate" do
8
+ include CanonicalTransformCases
9
+
10
+ @expectations = {
11
+ "simple operations" => {
12
+ "eq" => Predicate.from_json_str(%{["a","==",3]}),
13
+ "gt" => Predicate.from_json_str(%{["a",">",3]}),
14
+ "lt" => Predicate.from_json_str(%{["a","<",3]}),
15
+ "gte" => Predicate.from_json_str(%{["a",">=",3]}),
16
+ "lte" => Predicate.from_json_str(%{["a","<=",3]})
17
+ },
18
+ "primitive types" => {
19
+ "false" => Predicate.from_json_str(%{["a","==",false]}),
20
+ "true" => Predicate.from_json_str(%{["a","==",true]}),
21
+ "string" => Predicate.from_json_str(%{["a","==","yyy"]})
22
+ },
23
+ "not" => {
24
+ "simple" => Predicate.from_json_str(%{{"not":["a","==",true]}})
25
+ },
26
+ "simple and / or" => {
27
+ #parens are necessary around AND's in solr in order to force precedence
28
+ "and" => Predicate.from_json_str(%{{"and":[["a","==",1],["b","==",2]]}}),
29
+ "or" => Predicate.from_json_str(%{{"or":[["a","==",1],["b","==",2]]}})
30
+ },
31
+ "complex and / or" => {
32
+ "or and" => Predicate.from_json_str(%{
33
+ {
34
+ "or":[
35
+ {"and":[["a","==",1],["b","==",2]]},
36
+ ["c","==",3]
37
+ ]
38
+ }
39
+ })
40
+ }
41
+ }
42
+
43
+ create_canonical_tests(@expectations)
44
+
45
+ end
46
+
47
+ regarding "convert a json structure to a predicate" do
48
+ include CanonicalTransformCases
49
+
50
+ @expectations = {
51
+ "simple operations" => {
52
+ "eq" => Predicate.from_json_struct(["a", "==", 3]),
53
+ "gt" => Predicate.from_json_struct(["a", ">", 3]),
54
+ "lt" => Predicate.from_json_struct(["a", "<", 3]),
55
+ "gte" => Predicate.from_json_struct(["a", ">=", 3]),
56
+ "lte" => Predicate.from_json_struct(["a", "<=", 3])
57
+ },
58
+ "primitive types" => {
59
+ "false" => Predicate.from_json_struct(["a", "==", false]),
60
+ "true" => Predicate.from_json_struct(["a", "==", true]),
61
+ "string" => Predicate.from_json_struct(["a", "==", "yyy"])
62
+ },
63
+ "not" => {
64
+ "simple" => Predicate.from_json_struct("not" => ["a", "==", true])
65
+ },
66
+ "simple and / or" => {
67
+ #parens are necessary around AND's in solr in order to force precedence
68
+ "and" => Predicate.from_json_struct("and" => [["a", "==", 1],["b", "==", 2]]),
69
+ "or" => Predicate.from_json_struct("or" => [["a", "==", 1],["b", "==", 2]])
70
+ },
71
+ "complex and / or" => {
72
+ "or and" => Predicate.from_json_struct(
73
+ "or" => [
74
+ {"and" => [["a", "==", 1],["b", "==", 2]]},
75
+ ["c", "==", 3]
76
+ ]
77
+ )
78
+ }
79
+ }
80
+
81
+ create_canonical_tests(@expectations)
82
+
83
+ end
@@ -0,0 +1,37 @@
1
+ require "./test/test_helper_with_wrong"
2
+ require "./test/canonical_transform_cases"
3
+
4
+ require "predicated/from/ruby_code_string"
5
+ include Predicated
6
+
7
+ regarding "ruby code string - canoical transform cases" do
8
+ include CanonicalTransformCases
9
+
10
+ @expectations = {
11
+ "simple operations" => {
12
+ "eq" => Predicate.from_ruby_code_string("'a'==3"),
13
+ "gt" => Predicate.from_ruby_code_string("'a'>3"),
14
+ "lt" => Predicate.from_ruby_code_string("'a'<3"),
15
+ "gte" => Predicate.from_ruby_code_string("'a'>=3"),
16
+ "lte" => Predicate.from_ruby_code_string("'a'<=3")
17
+ },
18
+ "primitive types" => {
19
+ "false" => Predicate.from_ruby_code_string("'a'==false"),
20
+ "true" => Predicate.from_ruby_code_string("'a'==true"),
21
+ "string" => Predicate.from_ruby_code_string("'a'=='yyy'"),
22
+ },
23
+ "not" => {
24
+ "simple" => Predicate.from_ruby_code_string("!('a'==true)")
25
+ },
26
+ "simple and / or" => {
27
+ #parens are necessary around AND's in solr in order to force precedence
28
+ "and" => Predicate.from_ruby_code_string("'a'==1 && 'b'==2"),
29
+ "or" => Predicate.from_ruby_code_string("'a'==1 || 'b'==2")
30
+ },
31
+ "complex and / or" => {
32
+ "or and" => Predicate.from_ruby_code_string("'a'==1 && 'b'==2 || 'c'==3")
33
+ }
34
+ }
35
+
36
+ create_canonical_tests(@expectations)
37
+ end
@@ -0,0 +1,103 @@
1
+ require "./test/test_helper"
2
+
3
+ require "predicated/from/ruby_code_string"
4
+ include Predicated
5
+
6
+ regarding "parse a ruby predicate string" do
7
+
8
+ regarding "basic operations" do
9
+
10
+ #'Wrong'-style asserts are specifically avoided here.
11
+ #the circularity between the two projects will make you crazy if you're not careful
12
+
13
+ test "word and" do
14
+ assert_equal Predicate.from_ruby_code_string("1==1 and 2==2"), Predicate{ And(Eq(1,1),Eq(2,2)) }
15
+ end
16
+
17
+ test "substitute in from the binding" do
18
+ a = 1
19
+ b = "1"
20
+ c = "c"
21
+ d = Color.new("purple")
22
+
23
+ assert_equal Predicate.from_ruby_code_string("a==1", binding()), Predicate{ Eq(1,1) }
24
+ assert_equal Predicate.from_ruby_code_string("b==1", binding()), Predicate{ Eq("1",1) }
25
+ assert_equal Predicate.from_ruby_code_string("c==b", binding()), Predicate{ Eq("c","1") }
26
+ assert_equal Predicate.from_ruby_code_string("d==d", binding()), Predicate{ Eq(Color.new("purple"),
27
+ Color.new("purple")) }
28
+ assert_equal Predicate.from_ruby_code_string("d==d", binding()).left, d
29
+
30
+ assert_equal Predicate.from_ruby_code_string("a==b && b==c", binding()),
31
+ Predicate{ And(Eq(1,"1"),Eq("1","c")) }
32
+ end
33
+
34
+
35
+ test "parens change precedence" do
36
+ assert_equal Predicate.from_ruby_code_string("1==1 || 2==2 && 3==3"),
37
+ Predicate{ Or( Eq(1,1), And(Eq(2,2),Eq(3,3)) ) }
38
+
39
+ assert_equal Predicate.from_ruby_code_string("(1==1 || 2==2) && 3==3"),
40
+ Predicate{ And( Or(Eq(1,1),Eq(2,2)), Eq(3,3) ) }
41
+ end
42
+
43
+ regarding "only pay attention to the final line" do
44
+ #might hate myself one day for this. but what else does it make sense to do?
45
+
46
+ test "simple" do
47
+ assert_equal Predicate.from_ruby_code_string("z=2\nb=5\n1==1"), Predicate{ Eq(1,1) }
48
+ end
49
+
50
+ test "can make use of variables defined earlier in the block" do
51
+ #might hate myself one day for this. but what else does it make sense to do?
52
+ assert_equal Predicate.from_ruby_code_string("z=2\nb=5\nz==1"), Predicate{ Eq(2,1) }
53
+ end
54
+ end
55
+
56
+ test "a call that returns a boolean result" do
57
+ assert_equal Predicate.from_ruby_code_string("'abc'.include?('bc')"),
58
+ Predicate{ Call("abc", :include?, "bc") }
59
+
60
+ color = Color.new("purple")
61
+ assert_equal Predicate.from_ruby_code_string("color.name.include?('rp')", binding()),
62
+ Predicate{ Call("purple", :include?, "rp") }
63
+
64
+ assert_equal Predicate.from_ruby_code_string("'abc'.nil?"),
65
+ Predicate{ Call("abc", :nil?, nil) }
66
+ end
67
+
68
+ test "use of instance variables" do
69
+ @a = 1
70
+
71
+ assert_equal Predicate.from_ruby_code_string("@a==1", binding()), Predicate{ Eq(1,1) }
72
+ end
73
+
74
+ test "use of inline assignments" do
75
+ assert_equal Predicate.from_ruby_code_string("(a=2)==1 && a==1"),
76
+ Predicate{ And(Eq(2,1), Eq(2,1)) }
77
+ end
78
+
79
+ test "use of inline expressions" do
80
+ assert_equal Predicate.from_ruby_code_string("(2*1)==1"),
81
+ Predicate{ Eq(2,1) }
82
+
83
+ assert_equal Predicate.from_ruby_code_string("[2,1].first==1"),
84
+ Predicate{ Eq(2,1) }
85
+ end
86
+
87
+
88
+ end
89
+
90
+ regarding "errors" do
91
+ test "can't parse" do
92
+ assert_raises(Racc::ParseError) do
93
+ Predicate.from_ruby_code_string("bad ruby @@@@@****()(((")
94
+ end
95
+ end
96
+
97
+ test "predicates only" do
98
+ assert_raises(Predicated::Predicate::DontKnowWhatToDoWithThisSexpError) do
99
+ Predicate.from_ruby_code_string("a=1")
100
+ end
101
+ end
102
+ end
103
+ end