factbase 0.7.5 → 0.9.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -1
- data/Gemfile +1 -1
- data/Gemfile.lock +3 -3
- data/README.md +24 -27
- data/REUSE.toml +7 -2
- data/Rakefile +8 -1
- data/benchmark/bench_factbase.rb +1 -1
- data/fixtures/stories/agg.yml +17 -0
- data/fixtures/stories/always.yml +16 -0
- data/fixtures/stories/as.yml +16 -0
- data/fixtures/stories/count.yml +18 -0
- data/fixtures/stories/eq.yml +30 -0
- data/fixtures/stories/gt.yml +18 -0
- data/fixtures/stories/join.yml +19 -0
- data/fixtures/stories/max.yml +14 -0
- data/fixtures/stories/min.yml +14 -0
- data/fixtures/stories/nth.yml +14 -0
- data/fixtures/stories/or.yml +18 -0
- data/fixtures/stories/sprintf.yml +12 -0
- data/fixtures/stories/sum.yml +14 -0
- data/lib/factbase/cached/cached_fact.rb +28 -0
- data/lib/factbase/cached/cached_factbase.rb +64 -0
- data/lib/factbase/cached/cached_query.rb +61 -0
- data/lib/factbase/cached/cached_term.rb +25 -0
- data/lib/factbase/fact.rb +13 -13
- data/lib/factbase/indexed/indexed_fact.rb +28 -0
- data/lib/factbase/indexed/indexed_factbase.rb +64 -0
- data/lib/factbase/indexed/indexed_query.rb +56 -0
- data/lib/factbase/indexed/indexed_term.rb +60 -0
- data/lib/factbase/light.rb +7 -6
- data/lib/factbase/logged.rb +70 -35
- data/lib/factbase/query.rb +29 -34
- data/lib/factbase/rules.rb +15 -14
- data/lib/factbase/sync/sync_factbase.rb +57 -0
- data/lib/factbase/sync/sync_query.rb +61 -0
- data/lib/factbase/syntax.rb +16 -26
- data/lib/factbase/tallied.rb +10 -9
- data/lib/factbase/taped.rb +8 -0
- data/lib/factbase/tee.rb +2 -0
- data/lib/factbase/term.rb +45 -17
- data/lib/factbase/terms/aggregates.rb +17 -15
- data/lib/factbase/terms/aliases.rb +4 -4
- data/lib/factbase/terms/casting.rb +8 -8
- data/lib/factbase/terms/debug.rb +2 -2
- data/lib/factbase/terms/defn.rb +3 -3
- data/lib/factbase/terms/logical.rb +53 -14
- data/lib/factbase/terms/math.rb +26 -26
- data/lib/factbase/terms/meta.rb +14 -14
- data/lib/factbase/terms/ordering.rb +4 -4
- data/lib/factbase/terms/strings.rb +8 -8
- data/lib/factbase/terms/system.rb +3 -3
- data/lib/factbase.rb +67 -55
- data/test/factbase/cached/test_cached_factbase.rb +22 -0
- data/test/factbase/cached/test_cached_query.rb +79 -0
- data/test/factbase/indexed/test_indexed_query.rb +175 -0
- data/test/factbase/sync/test_sync_query.rb +30 -0
- data/test/factbase/terms/test_aggregates.rb +5 -5
- data/test/factbase/terms/test_aliases.rb +7 -7
- data/test/factbase/terms/test_casting.rb +8 -8
- data/test/factbase/terms/test_debug.rb +6 -6
- data/test/factbase/terms/test_defn.rb +14 -14
- data/test/factbase/terms/test_logical.rb +17 -19
- data/test/factbase/terms/test_math.rb +63 -61
- data/test/factbase/terms/test_meta.rb +36 -36
- data/test/factbase/terms/test_ordering.rb +9 -9
- data/test/factbase/terms/test_strings.rb +10 -10
- data/test/factbase/terms/test_system.rb +6 -6
- data/test/factbase/test_accum.rb +5 -5
- data/test/factbase/test_fact.rb +12 -12
- data/test/factbase/test_logged.rb +7 -0
- data/test/factbase/test_query.rb +99 -37
- data/test/factbase/test_rules.rb +1 -1
- data/test/factbase/test_syntax.rb +24 -11
- data/test/factbase/test_tee.rb +8 -8
- data/test/factbase/test_term.rb +39 -30
- data/test/test__helper.rb +2 -2
- data/test/test_factbase.rb +6 -0
- metadata +29 -4
- data/lib/factbase/query_once.rb +0 -54
- data/lib/factbase/term_once.rb +0 -67
@@ -0,0 +1,175 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
|
4
|
+
# SPDX-License-Identifier: MIT
|
5
|
+
|
6
|
+
require_relative '../../test__helper'
|
7
|
+
require_relative '../../../lib/factbase'
|
8
|
+
require_relative '../../../lib/factbase/cached/cached_factbase'
|
9
|
+
require_relative '../../../lib/factbase/indexed/indexed_factbase'
|
10
|
+
|
11
|
+
# Query test.
|
12
|
+
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
13
|
+
# Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
|
14
|
+
# License:: MIT
|
15
|
+
class TestIndexedQuery < Factbase::Test
|
16
|
+
def test_queries_many_times
|
17
|
+
fb = Factbase::IndexedFactbase.new(Factbase.new)
|
18
|
+
total = 5
|
19
|
+
total.times { fb.insert }
|
20
|
+
total.times do
|
21
|
+
assert_equal(5, fb.query('(always)').each.to_a.size)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_finds_by_eq
|
26
|
+
fb = Factbase::IndexedFactbase.new(Factbase.new)
|
27
|
+
fb.insert.foo = 42
|
28
|
+
fb.insert
|
29
|
+
3.times do
|
30
|
+
assert_equal(1, fb.query('(eq foo 42)').each.to_a.size)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_finds_by_eq_with_symbol
|
35
|
+
fb = Factbase::IndexedFactbase.new(Factbase.new)
|
36
|
+
f = fb.insert
|
37
|
+
f.num = 256
|
38
|
+
f.num = 42
|
39
|
+
fb.insert.num = 42
|
40
|
+
fb.insert.num = 55
|
41
|
+
fb.insert
|
42
|
+
fb.insert
|
43
|
+
3.times do
|
44
|
+
assert_equal(1, fb.query('(eq 1 (agg (eq num $num) (count)))').each.to_a.size)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_finds_by_eq_with_array
|
49
|
+
fb = Factbase::IndexedFactbase.new(Factbase.new)
|
50
|
+
f = fb.insert
|
51
|
+
f.foo = 33
|
52
|
+
f.foo = 42
|
53
|
+
f.foo = 1
|
54
|
+
3.times do
|
55
|
+
assert_equal(1, fb.query('(eq foo 42)').each.to_a.size)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_finds_by_eq_with_agg
|
60
|
+
fb = Factbase::IndexedFactbase.new(Factbase.new)
|
61
|
+
fb.insert.foo = 42
|
62
|
+
fb.insert.foo = 33
|
63
|
+
fb.insert
|
64
|
+
3.times do
|
65
|
+
assert_equal(1, fb.query('(eq foo (agg (exists foo) (max foo)))').each.to_a.size)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_finds_max_value
|
70
|
+
fb = Factbase::IndexedFactbase.new(Factbase.new)
|
71
|
+
fb.insert.foo = 42
|
72
|
+
fb.insert.foo = 33
|
73
|
+
fb.insert
|
74
|
+
3.times do
|
75
|
+
assert_equal(42, fb.query('(agg (exists foo) (max foo))').one)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_finds_by_eq_with_formula
|
80
|
+
fb = Factbase::IndexedFactbase.new(Factbase.new)
|
81
|
+
fb.insert.foo = 42
|
82
|
+
fb.insert
|
83
|
+
3.times do
|
84
|
+
assert_equal(1, fb.query('(eq foo (plus 40 2))').each.to_a.size)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_finds_by_eq_in_txn
|
89
|
+
fb = Factbase::IndexedFactbase.new(Factbase.new)
|
90
|
+
fb.insert.foo = 42
|
91
|
+
fb.insert
|
92
|
+
3.times do
|
93
|
+
fb.txn { |fbt| assert_equal(1, fbt.query('(eq foo 42)').each.to_a.size) }
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_finds_with_conjunction
|
98
|
+
fb = Factbase::IndexedFactbase.new(Factbase.new)
|
99
|
+
fb.insert.foo = 42
|
100
|
+
fb.insert.bar = 33
|
101
|
+
3.times do
|
102
|
+
assert_equal(2, fb.query('(or (exists foo) (exists bar))').each.to_a.size)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_finds_with_disjunction
|
107
|
+
fb = Factbase::IndexedFactbase.new(Factbase.new)
|
108
|
+
fb.insert.foo = 42
|
109
|
+
fb.insert.bar = 33
|
110
|
+
f = fb.insert
|
111
|
+
f.foo = 42
|
112
|
+
f.bar = 33
|
113
|
+
3.times do
|
114
|
+
assert_equal(1, fb.query('(and (exists foo) (exists bar))').each.to_a.size)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def test_finds_with_inversion
|
119
|
+
fb = Factbase::IndexedFactbase.new(Factbase.new)
|
120
|
+
fb.insert.foo = 42
|
121
|
+
fb.insert.bar = 33
|
122
|
+
3.times do
|
123
|
+
assert_equal(1, fb.query('(not (exists foo))').each.to_a.size)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def test_finds_with_disjunction_in_txn
|
128
|
+
fb = Factbase::IndexedFactbase.new(Factbase.new)
|
129
|
+
fb.insert.foo = 42
|
130
|
+
fb.insert.bar = 33
|
131
|
+
f = fb.insert
|
132
|
+
f.foo = 42
|
133
|
+
f.bar = 33
|
134
|
+
3.times do
|
135
|
+
fb.txn { |fbt| assert_equal(1, fbt.query('(and (exists foo) (exists bar))').each.to_a.size) }
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def test_attaches_alias
|
140
|
+
fb = Factbase::IndexedFactbase.new(Factbase.new)
|
141
|
+
total = 10
|
142
|
+
total.times do |i|
|
143
|
+
f = fb.insert
|
144
|
+
f.foo = rand(0..10)
|
145
|
+
f.bar = rand(0..10)
|
146
|
+
f.xyz = i
|
147
|
+
end
|
148
|
+
3.times do
|
149
|
+
assert_equal(total, fb.query('(as boom (agg (eq foo $bar) (min xyz)))').each.to_a.size)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def test_joins_too
|
154
|
+
fb = Factbase::IndexedFactbase.new(Factbase.new)
|
155
|
+
total = 10_000
|
156
|
+
total.times do |i|
|
157
|
+
f = fb.insert
|
158
|
+
f.who = i
|
159
|
+
end
|
160
|
+
total.times do |i|
|
161
|
+
f = fb.insert
|
162
|
+
f.friend = i
|
163
|
+
end
|
164
|
+
3.times do
|
165
|
+
assert_equal(total, fb.query('(and (exists who) (join "f<=friend" (eq friend $who)))').each.to_a.size)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def test_deletes_too
|
170
|
+
fb = Factbase::IndexedFactbase.new(Factbase.new)
|
171
|
+
fb.insert.foo = 1
|
172
|
+
fb.query('(eq foo 1)').delete!
|
173
|
+
assert_equal(0, fb.query('(always)').each.to_a.size)
|
174
|
+
end
|
175
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
|
4
|
+
# SPDX-License-Identifier: MIT
|
5
|
+
|
6
|
+
require_relative '../../test__helper'
|
7
|
+
require_relative '../../../lib/factbase'
|
8
|
+
require_relative '../../../lib/factbase/sync/sync_factbase'
|
9
|
+
|
10
|
+
# Query test.
|
11
|
+
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
12
|
+
# Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
|
13
|
+
# License:: MIT
|
14
|
+
class TestSyncQuery < Factbase::Test
|
15
|
+
def test_queries_many_times
|
16
|
+
fb = Factbase::SyncFactbase.new(Factbase.new)
|
17
|
+
total = 5
|
18
|
+
total.times { fb.insert }
|
19
|
+
total.times do
|
20
|
+
assert_equal(5, fb.query('(always)').each.to_a.size)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_deletes_too
|
25
|
+
fb = Factbase::SyncFactbase.new(Factbase.new)
|
26
|
+
fb.insert.foo = 1
|
27
|
+
fb.query('(eq foo 1)').delete!
|
28
|
+
assert_equal(0, fb.query('(always)').each.to_a.size)
|
29
|
+
end
|
30
|
+
end
|
@@ -30,10 +30,10 @@ class TestAggregates < Factbase::Test
|
|
30
30
|
'(eq x (agg (eq y 0) (first x)))' => '(eq x 1)',
|
31
31
|
'(agg (eq foo 42) (always))' => '(eq x 1)'
|
32
32
|
}.each do |q, r|
|
33
|
-
t = Factbase::Syntax.new(
|
34
|
-
f = maps.find { |m| t.evaluate(fact(m), maps) }
|
33
|
+
t = Factbase::Syntax.new(q).to_term
|
34
|
+
f = maps.find { |m| t.evaluate(fact(m), maps, Factbase.new) }
|
35
35
|
refute_nil(f, "nothing found by: #{q}")
|
36
|
-
assert(Factbase::Syntax.new(
|
36
|
+
assert(Factbase::Syntax.new(r).to_term.evaluate(fact(f), [], Factbase.new))
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
@@ -46,8 +46,8 @@ class TestAggregates < Factbase::Test
|
|
46
46
|
'(empty (eq y 42))' => true,
|
47
47
|
'(empty (eq x 1))' => false
|
48
48
|
}.each do |q, r|
|
49
|
-
t = Factbase::Syntax.new(
|
50
|
-
assert_equal(r, t.evaluate(
|
49
|
+
t = Factbase::Syntax.new(q).to_term
|
50
|
+
assert_equal(r, t.evaluate(Factbase::Fact.new({}), maps, Factbase.new), q)
|
51
51
|
end
|
52
52
|
end
|
53
53
|
end
|
@@ -24,11 +24,11 @@ class TestAliases < Factbase::Test
|
|
24
24
|
'(as foo (plus bar 1))' => '(absent foo)',
|
25
25
|
'(as foo (minus t1 t2))' => '(when (exists foo) (eq "Float" (type foo)))'
|
26
26
|
}.each do |q, r|
|
27
|
-
t = Factbase::Syntax.new(
|
27
|
+
t = Factbase::Syntax.new(q).to_term
|
28
28
|
maps.each do |m|
|
29
29
|
f = Factbase::Accum.new(fact(m), {}, false)
|
30
|
-
next unless t.evaluate(f, maps)
|
31
|
-
assert(Factbase::Syntax.new(
|
30
|
+
next unless t.evaluate(f, maps, Factbase.new)
|
31
|
+
assert(Factbase::Syntax.new(r).to_term.evaluate(f, [], Factbase.new), "#{q} -> #{f}")
|
32
32
|
end
|
33
33
|
end
|
34
34
|
end
|
@@ -44,13 +44,13 @@ class TestAliases < Factbase::Test
|
|
44
44
|
'(join "uuu" (eq x 1))' => '(absent uuu)',
|
45
45
|
'(join "uuu <= fff" (eq fff 1))' => '(absent uuu)'
|
46
46
|
}.each do |q, r|
|
47
|
-
t = Factbase::Syntax.new(
|
47
|
+
t = Factbase::Syntax.new(q).to_term
|
48
48
|
maps.each do |m|
|
49
49
|
f = Factbase::Accum.new(fact(m), {}, false)
|
50
50
|
require_relative '../../../lib/factbase/logged'
|
51
|
-
f = Factbase::Logged::Fact.new(f, Loog::NULL)
|
52
|
-
next unless t.evaluate(f, maps)
|
53
|
-
assert(Factbase::Syntax.new(
|
51
|
+
f = Factbase::Logged::Fact.new(f, log: Loog::NULL)
|
52
|
+
next unless t.evaluate(f, maps, Factbase.new)
|
53
|
+
assert(Factbase::Syntax.new(r).to_term.evaluate(f, [], Factbase.new), "#{q} -> #{f} doesn't match #{r}")
|
54
54
|
end
|
55
55
|
end
|
56
56
|
end
|
@@ -12,22 +12,22 @@ require_relative '../../../lib/factbase/term'
|
|
12
12
|
# License:: MIT
|
13
13
|
class TestCasting < Factbase::Test
|
14
14
|
def test_to_str
|
15
|
-
t = Factbase::Term.new(
|
16
|
-
assert_equal('String', t.evaluate(fact, []).class.to_s)
|
15
|
+
t = Factbase::Term.new(:to_string, [Time.now])
|
16
|
+
assert_equal('String', t.evaluate(fact, [], Factbase.new).class.to_s)
|
17
17
|
end
|
18
18
|
|
19
19
|
def test_to_integer
|
20
|
-
t = Factbase::Term.new(
|
21
|
-
assert_equal('Integer', t.evaluate(fact, []).class.to_s)
|
20
|
+
t = Factbase::Term.new(:to_integer, [[42, 'hello']])
|
21
|
+
assert_equal('Integer', t.evaluate(fact, [], Factbase.new).class.to_s)
|
22
22
|
end
|
23
23
|
|
24
24
|
def test_to_float
|
25
|
-
t = Factbase::Term.new(
|
26
|
-
assert_equal('Float', t.evaluate(fact, []).class.to_s)
|
25
|
+
t = Factbase::Term.new(:to_float, [[3.14, 'hello']])
|
26
|
+
assert_equal('Float', t.evaluate(fact, [], Factbase.new).class.to_s)
|
27
27
|
end
|
28
28
|
|
29
29
|
def test_to_time
|
30
|
-
t = Factbase::Term.new(
|
31
|
-
assert_equal('Time', t.evaluate(fact, []).class.to_s)
|
30
|
+
t = Factbase::Term.new(:to_time, [%w[2023-01-01 hello]])
|
31
|
+
assert_equal('Time', t.evaluate(fact, [], Factbase.new).class.to_s)
|
32
32
|
end
|
33
33
|
end
|
@@ -12,14 +12,14 @@ require_relative '../../../lib/factbase/term'
|
|
12
12
|
# License:: MIT
|
13
13
|
class TestDebug < Factbase::Test
|
14
14
|
def test_traced
|
15
|
-
t = Factbase::Term.new(
|
15
|
+
t = Factbase::Term.new(:traced, [Factbase::Term.new(:defn, [:test_debug, 'self.to_s'])])
|
16
16
|
assert_output("(traced (defn test_debug 'self.to_s')) -> true\n") do
|
17
|
-
assert(t.evaluate(fact, []))
|
17
|
+
assert(t.evaluate(fact, [], Factbase.new))
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
21
|
def test_traced_raises
|
22
|
-
e = assert_raises(StandardError) { Factbase::Term.new(
|
22
|
+
e = assert_raises(StandardError) { Factbase::Term.new(:traced, ['foo']).evaluate(fact, [], Factbase.new) }
|
23
23
|
assert_match(/A term expected, but 'foo' provided/, e.message)
|
24
24
|
end
|
25
25
|
|
@@ -27,9 +27,9 @@ class TestDebug < Factbase::Test
|
|
27
27
|
e =
|
28
28
|
assert_raises(StandardError) do
|
29
29
|
Factbase::Term.new(
|
30
|
-
|
31
|
-
[Factbase::Term.new(
|
32
|
-
).evaluate(fact, [])
|
30
|
+
:traced,
|
31
|
+
[Factbase::Term.new(:defn, [:debug, 'self.to_s']), 'something']
|
32
|
+
).evaluate(fact, [], Factbase.new)
|
33
33
|
end
|
34
34
|
assert_match(/Too many \(\d+\) operands for 'traced' \(\d+ expected\)/, e.message)
|
35
35
|
end
|
@@ -12,37 +12,37 @@ require_relative '../../../lib/factbase/term'
|
|
12
12
|
# License:: MIT
|
13
13
|
class TestDefn < Factbase::Test
|
14
14
|
def test_defn_simple
|
15
|
-
t = Factbase::Term.new(
|
16
|
-
assert(t.evaluate(fact('foo' => 4), []))
|
17
|
-
t1 = Factbase::Term.new(
|
18
|
-
assert_equal('(foo \'hello, world!\')', t1.evaluate(fact, []))
|
15
|
+
t = Factbase::Term.new(:defn, [:foo, 'self.to_s'])
|
16
|
+
assert(t.evaluate(fact('foo' => 4), [], Factbase.new))
|
17
|
+
t1 = Factbase::Term.new(:foo, ['hello, world!'])
|
18
|
+
assert_equal('(foo \'hello, world!\')', t1.evaluate(fact, [], Factbase.new))
|
19
19
|
end
|
20
20
|
|
21
21
|
def test_defn_again_by_mistake
|
22
|
-
t = Factbase::Term.new(
|
22
|
+
t = Factbase::Term.new(:defn, [:and, 'self.to_s'])
|
23
23
|
assert_raises(StandardError) do
|
24
|
-
t.evaluate(fact, [])
|
24
|
+
t.evaluate(fact, [], Factbase.new)
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
28
|
def test_defn_bad_name_by_mistake
|
29
|
-
t = Factbase::Term.new(
|
29
|
+
t = Factbase::Term.new(:defn, [:to_s, 'self.to_s'])
|
30
30
|
assert_raises(StandardError) do
|
31
|
-
t.evaluate(fact, [])
|
31
|
+
t.evaluate(fact, [], Factbase.new)
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
35
|
def test_defn_bad_name_spelling_by_mistake
|
36
|
-
t = Factbase::Term.new(
|
36
|
+
t = Factbase::Term.new(:defn, [:'some-key', 'self.to_s'])
|
37
37
|
assert_raises(StandardError) do
|
38
|
-
t.evaluate(fact, [])
|
38
|
+
t.evaluate(fact, [], Factbase.new)
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
42
|
def test_undef_simple
|
43
|
-
t = Factbase::Term.new(
|
44
|
-
assert(t.evaluate(fact, []))
|
45
|
-
t = Factbase::Term.new(
|
46
|
-
assert(t.evaluate(fact, []))
|
43
|
+
t = Factbase::Term.new(:defn, [:hello, 'self.to_s'])
|
44
|
+
assert(t.evaluate(fact, [], Factbase.new))
|
45
|
+
t = Factbase::Term.new(:undef, [:hello])
|
46
|
+
assert(t.evaluate(fact, [], Factbase.new))
|
47
47
|
end
|
48
48
|
end
|
@@ -12,46 +12,44 @@ require_relative '../../../lib/factbase/term'
|
|
12
12
|
# License:: MIT
|
13
13
|
class TestLogical < Factbase::Test
|
14
14
|
def test_not_matching
|
15
|
-
t = Factbase::Term.new(
|
16
|
-
refute(t.evaluate(fact('foo' => [100]), []))
|
15
|
+
t = Factbase::Term.new(:not, [Factbase::Term.new(:always, [])])
|
16
|
+
refute(t.evaluate(fact('foo' => [100]), [], Factbase.new))
|
17
17
|
end
|
18
18
|
|
19
19
|
def test_not_eq_matching
|
20
|
-
t = Factbase::Term.new(
|
21
|
-
assert(t.evaluate(fact('foo' => [42, 12, -90]), []))
|
22
|
-
refute(t.evaluate(fact('foo' => 100), []))
|
20
|
+
t = Factbase::Term.new(:not, [Factbase::Term.new(:eq, [:foo, 100])])
|
21
|
+
assert(t.evaluate(fact('foo' => [42, 12, -90]), [], Factbase.new))
|
22
|
+
refute(t.evaluate(fact('foo' => 100), [], Factbase.new))
|
23
23
|
end
|
24
24
|
|
25
25
|
def test_either
|
26
|
-
t = Factbase::Term.new(
|
27
|
-
assert_equal([42], t.evaluate(fact('foo' => 4), []))
|
26
|
+
t = Factbase::Term.new(:either, [Factbase::Term.new(:at, [5, :foo]), 42])
|
27
|
+
assert_equal([42], t.evaluate(fact('foo' => 4), [], Factbase.new))
|
28
28
|
end
|
29
29
|
|
30
30
|
def test_or_matching
|
31
31
|
t = Factbase::Term.new(
|
32
|
-
Factbase.new,
|
33
32
|
:or,
|
34
33
|
[
|
35
|
-
Factbase::Term.new(
|
36
|
-
Factbase::Term.new(
|
34
|
+
Factbase::Term.new(:eq, [:foo, 4]),
|
35
|
+
Factbase::Term.new(:eq, [:bar, 5])
|
37
36
|
]
|
38
37
|
)
|
39
|
-
assert(t.evaluate(fact('foo' => [4]), []))
|
40
|
-
assert(t.evaluate(fact('bar' => [5]), []))
|
41
|
-
refute(t.evaluate(fact('bar' => [42]), []))
|
38
|
+
assert(t.evaluate(fact('foo' => [4]), [], Factbase.new))
|
39
|
+
assert(t.evaluate(fact('bar' => [5]), [], Factbase.new))
|
40
|
+
refute(t.evaluate(fact('bar' => [42]), [], Factbase.new))
|
42
41
|
end
|
43
42
|
|
44
43
|
def test_when_matching
|
45
44
|
t = Factbase::Term.new(
|
46
|
-
Factbase.new,
|
47
45
|
:when,
|
48
46
|
[
|
49
|
-
Factbase::Term.new(
|
50
|
-
Factbase::Term.new(
|
47
|
+
Factbase::Term.new(:eq, [:foo, 4]),
|
48
|
+
Factbase::Term.new(:eq, [:bar, 5])
|
51
49
|
]
|
52
50
|
)
|
53
|
-
assert(t.evaluate(fact('foo' => 4, 'bar' => 5), []))
|
54
|
-
refute(t.evaluate(fact('foo' => 4), []))
|
55
|
-
assert(t.evaluate(fact('foo' => 5, 'bar' => 5), []))
|
51
|
+
assert(t.evaluate(fact('foo' => 4, 'bar' => 5), [], Factbase.new))
|
52
|
+
refute(t.evaluate(fact('foo' => 4), [], Factbase.new))
|
53
|
+
assert(t.evaluate(fact('foo' => 5, 'bar' => 5), [], Factbase.new))
|
56
54
|
end
|
57
55
|
end
|
@@ -12,109 +12,111 @@ require_relative '../../../lib/factbase/term'
|
|
12
12
|
# License:: MIT
|
13
13
|
class TestMath < Factbase::Test
|
14
14
|
def test_simple
|
15
|
-
t = Factbase::Term.new(
|
16
|
-
assert(t.evaluate(fact('foo' => [42]), []))
|
17
|
-
refute(t.evaluate(fact('foo' => 'Hello!'), []))
|
18
|
-
refute(t.evaluate(fact('bar' => ['Hello!']), []))
|
15
|
+
t = Factbase::Term.new(:eq, [:foo, 42])
|
16
|
+
assert(t.evaluate(fact('foo' => [42]), [], Factbase.new))
|
17
|
+
refute(t.evaluate(fact('foo' => 'Hello!'), [], Factbase.new))
|
18
|
+
refute(t.evaluate(fact('bar' => ['Hello!']), [], Factbase.new))
|
19
19
|
end
|
20
20
|
|
21
21
|
def test_zero
|
22
|
-
t = Factbase::Term.new(
|
23
|
-
assert(t.evaluate(fact('foo' => [0]), []))
|
24
|
-
assert(t.evaluate(fact('foo' => [10, 5, 6, -8, 'hey', 0, 9, 'fdsf']), []))
|
25
|
-
refute(t.evaluate(fact('foo' => [100]), []))
|
26
|
-
refute(t.evaluate(fact('foo' => []), []))
|
27
|
-
refute(t.evaluate(fact('bar' => []), []))
|
22
|
+
t = Factbase::Term.new(:zero, [:foo])
|
23
|
+
assert(t.evaluate(fact('foo' => [0]), [], Factbase.new))
|
24
|
+
assert(t.evaluate(fact('foo' => [10, 5, 6, -8, 'hey', 0, 9, 'fdsf']), [], Factbase.new))
|
25
|
+
refute(t.evaluate(fact('foo' => [100]), [], Factbase.new))
|
26
|
+
refute(t.evaluate(fact('foo' => []), [], Factbase.new))
|
27
|
+
refute(t.evaluate(fact('bar' => []), [], Factbase.new))
|
28
28
|
end
|
29
29
|
|
30
30
|
def test_eq
|
31
|
-
t = Factbase::Term.new(
|
32
|
-
assert(t.evaluate(fact('foo' => 42), []))
|
33
|
-
assert(t.evaluate(fact('foo' => [10, 5, 6, -8, 'hey', 42, 9, 'fdsf']), []))
|
34
|
-
refute(t.evaluate(fact('foo' => [100]), []))
|
35
|
-
refute(t.evaluate(fact('foo' => []), []))
|
36
|
-
refute(t.evaluate(fact('bar' => []), []))
|
31
|
+
t = Factbase::Term.new(:eq, [:foo, 42])
|
32
|
+
assert(t.evaluate(fact('foo' => 42), [], Factbase.new))
|
33
|
+
assert(t.evaluate(fact('foo' => [10, 5, 6, -8, 'hey', 42, 9, 'fdsf']), [], Factbase.new))
|
34
|
+
refute(t.evaluate(fact('foo' => [100]), [], Factbase.new))
|
35
|
+
refute(t.evaluate(fact('foo' => []), [], Factbase.new))
|
36
|
+
refute(t.evaluate(fact('bar' => []), [], Factbase.new))
|
37
37
|
end
|
38
38
|
|
39
39
|
def test_eq_time
|
40
40
|
now = Time.now
|
41
|
-
t = Factbase::Term.new(
|
42
|
-
assert(t.evaluate(fact('foo' => now), []))
|
43
|
-
assert(t.evaluate(fact('foo' => [now, Time.now]), []))
|
41
|
+
t = Factbase::Term.new(:eq, [:foo, Time.parse(now.iso8601)])
|
42
|
+
assert(t.evaluate(fact('foo' => now), [], Factbase.new))
|
43
|
+
assert(t.evaluate(fact('foo' => [now, Time.now]), [], Factbase.new))
|
44
44
|
end
|
45
45
|
|
46
46
|
def test_lt
|
47
|
-
t = Factbase::Term.new(
|
48
|
-
assert(t.evaluate(fact('foo' => [10]), []))
|
49
|
-
refute(t.evaluate(fact('foo' => [100]), []))
|
50
|
-
refute(t.evaluate(fact('foo' => 100), []))
|
51
|
-
refute(t.evaluate(fact('bar' => 100), []))
|
47
|
+
t = Factbase::Term.new(:lt, [:foo, 42])
|
48
|
+
assert(t.evaluate(fact('foo' => [10]), [], Factbase.new))
|
49
|
+
refute(t.evaluate(fact('foo' => [100]), [], Factbase.new))
|
50
|
+
refute(t.evaluate(fact('foo' => 100), [], Factbase.new))
|
51
|
+
refute(t.evaluate(fact('bar' => 100), [], Factbase.new))
|
52
52
|
end
|
53
53
|
|
54
54
|
def test_gte
|
55
|
-
t = Factbase::Term.new(
|
56
|
-
assert(t.evaluate(fact('foo' => 100), []))
|
57
|
-
assert(t.evaluate(fact('foo' => 42), []))
|
58
|
-
refute(t.evaluate(fact('foo' => 41), []))
|
55
|
+
t = Factbase::Term.new(:gte, [:foo, 42])
|
56
|
+
assert(t.evaluate(fact('foo' => 100), [], Factbase.new))
|
57
|
+
assert(t.evaluate(fact('foo' => 42), [], Factbase.new))
|
58
|
+
refute(t.evaluate(fact('foo' => 41), [], Factbase.new))
|
59
59
|
end
|
60
60
|
|
61
61
|
def test_lte
|
62
|
-
t = Factbase::Term.new(
|
63
|
-
assert(t.evaluate(fact('foo' => 41), []))
|
64
|
-
assert(t.evaluate(fact('foo' => 42), []))
|
65
|
-
refute(t.evaluate(fact('foo' => 100), []))
|
62
|
+
t = Factbase::Term.new(:lte, [:foo, 42])
|
63
|
+
assert(t.evaluate(fact('foo' => 41), [], Factbase.new))
|
64
|
+
assert(t.evaluate(fact('foo' => 42), [], Factbase.new))
|
65
|
+
refute(t.evaluate(fact('foo' => 100), [], Factbase.new))
|
66
66
|
end
|
67
67
|
|
68
68
|
def test_gt
|
69
|
-
t = Factbase::Term.new(
|
70
|
-
assert(t.evaluate(fact('foo' => [100]), []))
|
71
|
-
assert(t.evaluate(fact('foo' => 100), []))
|
72
|
-
refute(t.evaluate(fact('foo' => [10]), []))
|
73
|
-
refute(t.evaluate(fact('foo' => 10), []))
|
74
|
-
refute(t.evaluate(fact('bar' => 10), []))
|
69
|
+
t = Factbase::Term.new(:gt, [:foo, 42])
|
70
|
+
assert(t.evaluate(fact('foo' => [100]), [], Factbase.new))
|
71
|
+
assert(t.evaluate(fact('foo' => 100), [], Factbase.new))
|
72
|
+
refute(t.evaluate(fact('foo' => [10]), [], Factbase.new))
|
73
|
+
refute(t.evaluate(fact('foo' => 10), [], Factbase.new))
|
74
|
+
refute(t.evaluate(fact('bar' => 10), [], Factbase.new))
|
75
75
|
end
|
76
76
|
|
77
77
|
def test_lt_time
|
78
|
-
t = Factbase::Term.new(
|
79
|
-
assert(t.evaluate(fact('foo' => [Time.now - 100]), []))
|
80
|
-
refute(t.evaluate(fact('foo' => [Time.now + 100]), []))
|
81
|
-
refute(t.evaluate(fact('bar' => [100]), []))
|
78
|
+
t = Factbase::Term.new(:lt, [:foo, Time.now])
|
79
|
+
assert(t.evaluate(fact('foo' => [Time.now - 100]), [], Factbase.new))
|
80
|
+
refute(t.evaluate(fact('foo' => [Time.now + 100]), [], Factbase.new))
|
81
|
+
refute(t.evaluate(fact('bar' => [100]), [], Factbase.new))
|
82
82
|
end
|
83
83
|
|
84
84
|
def test_gt_time
|
85
|
-
t = Factbase::Term.new(
|
86
|
-
assert(t.evaluate(fact('foo' => [Time.now + 100]), []))
|
87
|
-
refute(t.evaluate(fact('foo' => [Time.now - 100]), []))
|
88
|
-
refute(t.evaluate(fact('bar' => [100]), []))
|
85
|
+
t = Factbase::Term.new(:gt, [:foo, Time.now])
|
86
|
+
assert(t.evaluate(fact('foo' => [Time.now + 100]), [], Factbase.new))
|
87
|
+
refute(t.evaluate(fact('foo' => [Time.now - 100]), [], Factbase.new))
|
88
|
+
refute(t.evaluate(fact('bar' => [100]), [], Factbase.new))
|
89
89
|
end
|
90
90
|
|
91
91
|
def test_plus
|
92
|
-
t = Factbase::Term.new(
|
93
|
-
assert_equal(46, t.evaluate(fact('foo' => 4), []))
|
94
|
-
assert_nil(t.evaluate(fact, []))
|
92
|
+
t = Factbase::Term.new(:plus, [:foo, 42])
|
93
|
+
assert_equal(46, t.evaluate(fact('foo' => 4), [], Factbase.new))
|
94
|
+
assert_nil(t.evaluate(fact, [], Factbase.new))
|
95
95
|
end
|
96
96
|
|
97
97
|
def test_plus_time
|
98
|
-
t = Factbase::Term.new(
|
99
|
-
assert_equal(Time.parse('2024-01-13'), t.evaluate(fact('foo' => Time.parse('2024-01-01')), []))
|
100
|
-
assert_nil(t.evaluate(fact, []))
|
98
|
+
t = Factbase::Term.new(:plus, [:foo, '12 days'])
|
99
|
+
assert_equal(Time.parse('2024-01-13'), t.evaluate(fact('foo' => Time.parse('2024-01-01')), [], Factbase.new))
|
100
|
+
assert_nil(t.evaluate(fact, [], Factbase.new))
|
101
101
|
end
|
102
102
|
|
103
103
|
def test_minus
|
104
|
-
t = Factbase::Term.new(
|
105
|
-
assert_equal(58, t.evaluate(fact('foo' => 100), []))
|
106
|
-
assert_nil(t.evaluate(fact, []))
|
104
|
+
t = Factbase::Term.new(:minus, [:foo, 42])
|
105
|
+
assert_equal(58, t.evaluate(fact('foo' => 100), [], Factbase.new))
|
106
|
+
assert_nil(t.evaluate(fact, [], Factbase.new))
|
107
107
|
end
|
108
108
|
|
109
109
|
def test_minus_time
|
110
|
-
t = Factbase::Term.new(
|
111
|
-
assert_equal(Time.parse('2024-01-01T06:04'),
|
112
|
-
|
110
|
+
t = Factbase::Term.new(:minus, [:foo, '4 hours'])
|
111
|
+
assert_equal(Time.parse('2024-01-01T06:04'),
|
112
|
+
t.evaluate(fact('foo' => Time.parse('2024-01-01T10:04')), [], Factbase.new))
|
113
|
+
assert_nil(t.evaluate(fact, [], Factbase.new))
|
113
114
|
end
|
114
115
|
|
115
116
|
def test_minus_time_singular
|
116
|
-
t = Factbase::Term.new(
|
117
|
-
assert_equal(Time.parse('2024-01-01T09:04'),
|
118
|
-
|
117
|
+
t = Factbase::Term.new(:minus, [:foo, '1 hour'])
|
118
|
+
assert_equal(Time.parse('2024-01-01T09:04'),
|
119
|
+
t.evaluate(fact('foo' => Time.parse('2024-01-01T10:04')), [], Factbase.new))
|
120
|
+
assert_nil(t.evaluate(fact, [], Factbase.new))
|
119
121
|
end
|
120
122
|
end
|