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
@@ -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