predicated 0.1.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 +9 -0
- data/lib/predicated.rb +6 -0
- data/lib/predicated/constrain.rb +64 -0
- data/lib/predicated/evaluate.rb +75 -0
- data/lib/predicated/from/callable_object.rb +79 -0
- data/lib/predicated/from/ruby_string.rb +65 -0
- data/lib/predicated/from/url_fragment.rb +97 -0
- data/lib/predicated/gem_check.rb +34 -0
- data/lib/predicated/predicate.rb +92 -0
- data/lib/predicated/print.rb +50 -0
- data/lib/predicated/selector.rb +55 -0
- data/lib/predicated/to/arel.rb +35 -0
- data/lib/predicated/to/sentence.rb +91 -0
- data/lib/predicated/version.rb +3 -0
- data/test/constrain_test.rb +77 -0
- data/test/enumerable_test.rb +60 -0
- data/test/equality_test.rb +21 -0
- data/test/evaluate_test.rb +144 -0
- data/test/from/callable_object_test.rb +102 -0
- data/test/from/ruby_string_test.rb +135 -0
- data/test/from/url_fragment_parser_test.rb +116 -0
- data/test/from/url_fragment_test.rb +37 -0
- data/test/print_test.rb +65 -0
- data/test/selector_test.rb +82 -0
- data/test/suite.rb +4 -0
- data/test/test_helper.rb +42 -0
- data/test/test_helper_with_wrong.rb +5 -0
- data/test/to/arel_test.rb +45 -0
- data/test/to/sentence_test.rb +83 -0
- metadata +109 -0
@@ -0,0 +1,50 @@
|
|
1
|
+
module Predicated
|
2
|
+
class Binary
|
3
|
+
def inspect
|
4
|
+
"#{self.class.shorthand}(#{part_inspect(left)},#{part_inspect(right)})"
|
5
|
+
end
|
6
|
+
|
7
|
+
def to_s(indent="")
|
8
|
+
indent + inspect
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
def part_inspect(thing)
|
13
|
+
part_to_str(thing) {|thing| thing.inspect}
|
14
|
+
end
|
15
|
+
|
16
|
+
def part_to_s(thing, indent="")
|
17
|
+
part_to_str(thing, indent) {|thing| thing.to_s(indent)}
|
18
|
+
end
|
19
|
+
|
20
|
+
def part_to_str(thing, indent="")
|
21
|
+
if thing.is_a?(String)
|
22
|
+
"'#{thing}'"
|
23
|
+
elsif thing.is_a?(Numeric) || thing.is_a?(TrueClass) || thing.is_a?(FalseClass)
|
24
|
+
thing.to_s
|
25
|
+
elsif thing.is_a?(Binary)
|
26
|
+
yield(thing)
|
27
|
+
else
|
28
|
+
"#{thing.class.name}{'#{thing.to_s}'}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
module ContainerToString
|
34
|
+
def to_s(indent="")
|
35
|
+
next_indent = indent + " " + " "
|
36
|
+
|
37
|
+
str = "#{indent}#{self.class.shorthand}(\n"
|
38
|
+
str << "#{part_to_s(left, next_indent)},\n"
|
39
|
+
str << "#{part_to_s(right, next_indent)}\n"
|
40
|
+
str << "#{indent})"
|
41
|
+
str << "\n" if indent == ""
|
42
|
+
|
43
|
+
str
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class And; include ContainerToString end
|
48
|
+
class Or; include ContainerToString end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Predicated
|
2
|
+
module Selector
|
3
|
+
#this is no doubt totally non-performant with all the extends, and
|
4
|
+
#could probably be just done better / more elegantly
|
5
|
+
#seek help.
|
6
|
+
|
7
|
+
def self.SelectorEnumerable(key_to_selecting_proc)
|
8
|
+
Module.new do
|
9
|
+
@key_to_selecting_proc = key_to_selecting_proc
|
10
|
+
|
11
|
+
def self.extended(base)
|
12
|
+
sym = "@key_to_selecting_proc".to_sym
|
13
|
+
hash = base.instance_variable_get(sym) || {}
|
14
|
+
base.instance_variable_set(sym, hash.merge(@key_to_selecting_proc))
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.included(base)
|
18
|
+
extended(base)
|
19
|
+
end
|
20
|
+
|
21
|
+
#ugh ugh
|
22
|
+
def key_to_selecting_proc
|
23
|
+
@key_to_selecting_proc || self.class.instance_variable_get("@key_to_selecting_proc".to_sym)
|
24
|
+
end
|
25
|
+
|
26
|
+
def select(*keys, &block)
|
27
|
+
key = keys.shift if keys.length>=1
|
28
|
+
result =
|
29
|
+
if key
|
30
|
+
selecting_proc = key_to_selecting_proc[key]
|
31
|
+
raise "no selector found for '#{key}'. current selectors: [#{key_to_selecting_proc.collect{|k,v|k.to_s}.join(",")}]" unless selecting_proc
|
32
|
+
memos_for(:select)[key] ||= super(&selecting_proc)
|
33
|
+
else
|
34
|
+
super(&block)
|
35
|
+
end
|
36
|
+
#ugh
|
37
|
+
result.extend(Predicated::Selector.SelectorEnumerable(key_to_selecting_proc))
|
38
|
+
keys.length>=1 ? result.select(*keys, &block) : result
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
def memos_for(group)
|
43
|
+
@memos ||= {}
|
44
|
+
@memos[group] ||= {}
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
#ugh
|
50
|
+
def SelectorEnumerable(key_to_selecting_proc)
|
51
|
+
Predicated::Selector.SelectorEnumerable(key_to_selecting_proc)
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
raise "this doesn't work in 1.8.6 because the arel gem is 1.8.7-only" if RUBY_VERSION=="1.8.6"
|
2
|
+
|
3
|
+
require "predicated/predicate"
|
4
|
+
|
5
|
+
module Predicated
|
6
|
+
|
7
|
+
require_gem_version("arel", "0.4.0")
|
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
|
+
|
19
|
+
end
|
20
|
+
|
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
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require "predicated/predicate"
|
2
|
+
require "predicated/evaluate"
|
3
|
+
|
4
|
+
module Predicated
|
5
|
+
|
6
|
+
module ContainerSentence
|
7
|
+
def to_sentence
|
8
|
+
left.to_sentence + "#{joining_str}" + right.to_sentence
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_negative_sentence
|
12
|
+
"This is not true: " + to_sentence
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class And; include ContainerSentence; def joining_str; " and " end; end
|
17
|
+
class Or; include ContainerSentence; def joining_str; " or " end;end
|
18
|
+
|
19
|
+
class Operation
|
20
|
+
|
21
|
+
def self.register_verb_phrase(method_sym,
|
22
|
+
positive_verb_phrase,
|
23
|
+
negative_verb_phrase,
|
24
|
+
accepts_object=true)
|
25
|
+
@@method_sym_to_phrase_info[method_sym] = {
|
26
|
+
:positive => positive_verb_phrase,
|
27
|
+
:negative => negative_verb_phrase,
|
28
|
+
:accepts_object => accepts_object
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.reset_verb_phrases
|
33
|
+
@@method_sym_to_phrase_info = {}
|
34
|
+
|
35
|
+
register_verb_phrase(:==, "is equal to", "is not equal to")
|
36
|
+
register_verb_phrase(:>, "is greater than", "is not greater than")
|
37
|
+
register_verb_phrase(:<, "is less than", "is not less than")
|
38
|
+
register_verb_phrase(:>=, "is greater than or equal to", "is not greater than or equal to")
|
39
|
+
register_verb_phrase(:<=, "is less than or equal to", "is not less than or equal to")
|
40
|
+
|
41
|
+
register_verb_phrase(:include?, "includes", "does not include")
|
42
|
+
register_verb_phrase(:is_a?, "is a", "is not a")
|
43
|
+
register_verb_phrase(:nil?, "is nil", "is not nil", accepts_object=false)
|
44
|
+
|
45
|
+
nil
|
46
|
+
end
|
47
|
+
|
48
|
+
reset_verb_phrases
|
49
|
+
|
50
|
+
|
51
|
+
def to_sentence
|
52
|
+
sentence(verb_phrase[:positive])
|
53
|
+
end
|
54
|
+
|
55
|
+
def to_negative_sentence
|
56
|
+
sentence(verb_phrase[:negative])
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def sentence(verb_phrase_str)
|
62
|
+
left_str = format_value(left)
|
63
|
+
right_str = format_value(right)
|
64
|
+
|
65
|
+
str = left_str + " " + verb_phrase_str
|
66
|
+
str << " " + right_str if verb_phrase[:accepts_object]
|
67
|
+
str
|
68
|
+
end
|
69
|
+
|
70
|
+
def verb_phrase
|
71
|
+
@@method_sym_to_phrase_info[method_sym] || {
|
72
|
+
:positive => "is " + rudimentary=method_sym.to_s.gsub("_", " ").gsub("?", ""),
|
73
|
+
:negative => "is not " + rudimentary,
|
74
|
+
:accepts_object => true
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
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
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require "test/test_helper_with_wrong"
|
2
|
+
|
3
|
+
require "predicated/predicate"
|
4
|
+
require "predicated/constrain"
|
5
|
+
include Predicated
|
6
|
+
|
7
|
+
apropos %{constraints are rules about the content and structure of predicates.
|
8
|
+
a predicate might violate a constraint} do
|
9
|
+
|
10
|
+
before do
|
11
|
+
@value_not_equal_to_two =
|
12
|
+
Constraint.new(:name => "Value can't be two",
|
13
|
+
:selectors => [Operation],
|
14
|
+
:check_that => proc{|predicate, ancestors| predicate.right!=2})
|
15
|
+
|
16
|
+
@not_more_than_two_levels_deep =
|
17
|
+
Constraint.new(:name => "Limited to two levels deep",
|
18
|
+
:check_that => proc{|predicate, ancestors| ancestors.length<=2})
|
19
|
+
end
|
20
|
+
|
21
|
+
test "apply to each predicate - simple" do
|
22
|
+
constraints = Constraints.new.add(@value_not_equal_to_two)
|
23
|
+
|
24
|
+
assert{ constraints.check(Predicate{Eq(1,1)}).pass? }
|
25
|
+
deny { constraints.check(Predicate{Eq(1,2)}).pass? }
|
26
|
+
|
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? }
|
29
|
+
end
|
30
|
+
|
31
|
+
test "apply each to each predicate - many constraints" do
|
32
|
+
constraints =
|
33
|
+
Constraints.new.
|
34
|
+
add(@value_not_equal_to_two).
|
35
|
+
add(@not_more_than_two_levels_deep)
|
36
|
+
|
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? }
|
39
|
+
|
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? }
|
42
|
+
end
|
43
|
+
|
44
|
+
test "equality" do
|
45
|
+
one = Constraint.new(:name => "Value can't be two",
|
46
|
+
:selectors => [Operation],
|
47
|
+
:check_that => proc{|predicate, ancestors| predicate.right!=2})
|
48
|
+
two = Constraint.new(:name => "Value can't be two",
|
49
|
+
:selectors => [Operation],
|
50
|
+
:check_that => proc{|predicate, ancestors| predicate.right!=2})
|
51
|
+
three = Constraint.new(:name => "Some other constraint",
|
52
|
+
:check_that => proc{|predicate, ancestors| false})
|
53
|
+
|
54
|
+
assert{ one == two }
|
55
|
+
deny { one == three }
|
56
|
+
end
|
57
|
+
|
58
|
+
test %{result contains information about whether the checks passed,
|
59
|
+
which constraints were violated,
|
60
|
+
along with the offending predicates} do
|
61
|
+
constraints = Constraints.new.add(@value_not_equal_to_two)
|
62
|
+
|
63
|
+
result = constraints.check(Predicate{Eq(1,1)})
|
64
|
+
assert{ result.pass? }
|
65
|
+
assert{ result.violations == {} }
|
66
|
+
|
67
|
+
result = constraints.check(Predicate{And(Eq(1,1), And(Eq(2,2), Eq(3,2)))})
|
68
|
+
deny { result.pass? }
|
69
|
+
assert{
|
70
|
+
result.violations == {
|
71
|
+
@value_not_equal_to_two => [Equal.new(2,2), Equal.new(3,2)]
|
72
|
+
}
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require "test/test_helper_with_wrong"
|
2
|
+
|
3
|
+
require "predicated/predicate"
|
4
|
+
include Predicated
|
5
|
+
|
6
|
+
apropos "you can flip through the predicate tree, like any enumerable. a list of ancestors of each node are provided" do
|
7
|
+
|
8
|
+
test "simple" do
|
9
|
+
assert { Predicate { Eq(1, 2) }.to_a == [[Predicate { Eq(1, 2) }, []]] }
|
10
|
+
end
|
11
|
+
|
12
|
+
test "complex" do
|
13
|
+
the_top = Predicate { And(Eq(1, 2), Or(Eq(3, 4), Eq(5, 6))) }
|
14
|
+
the_or = Predicate { Or(Eq(3, 4), Eq(5, 6)) }
|
15
|
+
assert { the_top.to_a ==
|
16
|
+
[
|
17
|
+
[the_top, []],
|
18
|
+
[Predicate { Eq(1, 2) }, [the_top]],
|
19
|
+
[Predicate { Or(Eq(3, 4), Eq(5, 6)) }, [the_top]],
|
20
|
+
[Predicate { Eq(3, 4) }, [the_top, the_or]],
|
21
|
+
[Predicate { Eq(5, 6) }, [the_top, the_or]]
|
22
|
+
]
|
23
|
+
}
|
24
|
+
end
|
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
|
33
|
+
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
|
+
|
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)] }
|
49
|
+
|
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
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "test/test_helper_with_wrong"
|
2
|
+
|
3
|
+
require "predicated/predicate"
|
4
|
+
include Predicated
|
5
|
+
|
6
|
+
apropos "prove value equality" do
|
7
|
+
|
8
|
+
test "simple" do
|
9
|
+
assert { Predicate { Eq(1, 1) } == Predicate { Eq(1, 1) } }
|
10
|
+
deny { Predicate { Eq(1, 1) } == Predicate { Eq(1, 99) } }
|
11
|
+
end
|
12
|
+
|
13
|
+
test "complex" do
|
14
|
+
assert { Predicate { And(Eq(1, 1), Or(Eq(2, 2), Eq(3, 3))) } ==
|
15
|
+
Predicate { And(Eq(1, 1), Or(Eq(2, 2), Eq(3, 3))) } }
|
16
|
+
|
17
|
+
deny { Predicate { And(Eq(1, 1), Or(Eq(2, 2), Eq(3, 3))) } ==
|
18
|
+
Predicate { And(Eq(1, 1), Or(Eq(2, 99), Eq(3, 3))) } }
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
require "test/test_helper_with_wrong"
|
2
|
+
|
3
|
+
require "predicated/evaluate"
|
4
|
+
include Predicated
|
5
|
+
|
6
|
+
apropos "evaluate a predicate as boolean logic in ruby. change the context by providing and optional binding." do
|
7
|
+
|
8
|
+
apropos "proving out basic operations" do
|
9
|
+
test "equals" do
|
10
|
+
assert { Predicate { Eq(1, 1) }.evaluate }
|
11
|
+
deny { Predicate { Eq(1, 2) }.evaluate }
|
12
|
+
end
|
13
|
+
|
14
|
+
test "less than" do
|
15
|
+
assert { Predicate { Lt(1, 2) }.evaluate }
|
16
|
+
deny { Predicate { Lt(2, 2) }.evaluate }
|
17
|
+
deny { Predicate { Lt(3, 2) }.evaluate }
|
18
|
+
end
|
19
|
+
|
20
|
+
test "greater than" do
|
21
|
+
deny { Predicate { Gt(1, 2) }.evaluate }
|
22
|
+
deny { Predicate { Gt(2, 2) }.evaluate }
|
23
|
+
assert { Predicate { Gt(3, 2) }.evaluate }
|
24
|
+
end
|
25
|
+
|
26
|
+
test "less than or equal to" do
|
27
|
+
assert { Predicate { Lte(1, 2) }.evaluate }
|
28
|
+
assert { Predicate { Lte(2, 2) }.evaluate }
|
29
|
+
deny { Predicate { Lte(3, 2) }.evaluate }
|
30
|
+
end
|
31
|
+
|
32
|
+
test "greater than or equal to" do
|
33
|
+
deny { Predicate { Gte(1, 2) }.evaluate }
|
34
|
+
assert { Predicate { Gte(2, 2) }.evaluate }
|
35
|
+
assert { Predicate { Gte(3, 2) }.evaluate }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
apropos "comparing values of different data types" do
|
40
|
+
test "strings" do
|
41
|
+
assert { Predicate { Eq("1", "1") }.evaluate }
|
42
|
+
deny { Predicate { Eq("1", 1) }.evaluate }
|
43
|
+
deny { Predicate { Eq("1", nil) }.evaluate }
|
44
|
+
end
|
45
|
+
|
46
|
+
test "booleans" do
|
47
|
+
assert { Predicate { Eq(true, true) }.evaluate }
|
48
|
+
deny { Predicate { Eq(false, true) }.evaluate }
|
49
|
+
|
50
|
+
deny { Predicate { Eq(false, nil) }.evaluate }
|
51
|
+
deny { Predicate { Eq(true, nil) }.evaluate }
|
52
|
+
|
53
|
+
deny { Predicate { Eq("false", false) }.evaluate }
|
54
|
+
deny { Predicate { Eq("true", true) }.evaluate }
|
55
|
+
end
|
56
|
+
|
57
|
+
test "numbers" do
|
58
|
+
assert { Predicate { Eq(1, 1) }.evaluate }
|
59
|
+
assert { Predicate { Eq(1, 1.0) }.evaluate }
|
60
|
+
assert { Predicate { Eq(1.0, 1.0) }.evaluate }
|
61
|
+
deny { Predicate { Eq(1, 2) }.evaluate }
|
62
|
+
deny { Predicate { Eq(1, nil) }.evaluate }
|
63
|
+
end
|
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
|
+
test "objects" do
|
77
|
+
assert { Predicate { Eq(Color.new("red"), Color.new("red")) }.evaluate }
|
78
|
+
deny { Predicate { Eq(Color.new("red"), Color.new("BLUE")) }.evaluate }
|
79
|
+
deny { Predicate { Eq(Color.new("red"), 2) }.evaluate }
|
80
|
+
deny { Predicate { Eq(Color.new("red"), "red") }.evaluate }
|
81
|
+
deny { Predicate { Eq(Color.new("red"), nil) }.evaluate }
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
apropos "and" do
|
87
|
+
test "left and right must be true" do
|
88
|
+
assert { Predicate { And( Eq(1, 1), Eq(2, 2) ) }.evaluate }
|
89
|
+
deny { Predicate { And( Eq(99, 1), Eq(2, 2) ) }.evaluate }
|
90
|
+
deny { Predicate { And( Eq(1, 1), Eq(99, 2) ) }.evaluate }
|
91
|
+
end
|
92
|
+
|
93
|
+
test "simple true and false work too" do
|
94
|
+
assert { Predicate { And( true, true ) }.evaluate }
|
95
|
+
assert { Predicate { And( true, Eq(2, 2) ) }.evaluate }
|
96
|
+
deny { Predicate { And( true, false ) }.evaluate }
|
97
|
+
deny { Predicate { And( Eq(2, 2), false ) }.evaluate }
|
98
|
+
end
|
99
|
+
|
100
|
+
test "nested" do
|
101
|
+
assert { Predicate { And( true, And(true, true) ) }.evaluate }
|
102
|
+
deny { Predicate { And( false, And(true, true) ) }.evaluate }
|
103
|
+
deny { Predicate { And( true, And(true, false) ) }.evaluate }
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
apropos "or" do
|
108
|
+
test "one of left or right must be true" do
|
109
|
+
assert { Predicate { Or(true, true) }.evaluate }
|
110
|
+
assert { Predicate { Or(true, false) }.evaluate }
|
111
|
+
assert { Predicate { Or(false, true) }.evaluate }
|
112
|
+
deny { Predicate { Or(false, false) }.evaluate }
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
apropos "evaluate adds a generic 'call' class. that is, object.message(args)" do
|
117
|
+
test "evaluate simple calls" do
|
118
|
+
assert { Predicate { Call("abc", :include?, "bc") }.evaluate }
|
119
|
+
deny { Predicate { Call("abc", :include?, "ZZ") }.evaluate }
|
120
|
+
end
|
121
|
+
|
122
|
+
test "nil call. call defaults to no args if none are specified" do
|
123
|
+
assert { Predicate { Call(nil, :nil?, []) }.evaluate }
|
124
|
+
deny { Predicate { Call("abc", :nil?, []) }.evaluate }
|
125
|
+
|
126
|
+
assert { Predicate { Call(nil, :nil?) }.evaluate }
|
127
|
+
deny { Predicate { Call("abc", :nil?) }.evaluate }
|
128
|
+
end
|
129
|
+
|
130
|
+
test "inspect" do
|
131
|
+
assert{ Call.new("abc", :include?, "bc").inspect == "Call('abc'.include?('bc'))" }
|
132
|
+
end
|
133
|
+
|
134
|
+
test "call equality" do
|
135
|
+
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") }
|
139
|
+
end
|
140
|
+
|
141
|
+
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|