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
@@ -12,59 +12,59 @@ require_relative '../../../lib/factbase/term'
|
|
12
12
|
# License:: MIT
|
13
13
|
class TestMeta < Factbase::Test
|
14
14
|
def test_exists
|
15
|
-
t = Factbase::Term.new(
|
16
|
-
assert(t.evaluate(fact('foo' => 41), []))
|
17
|
-
refute(t.evaluate(fact('bar' => 41), []))
|
15
|
+
t = Factbase::Term.new(:exists, [:foo])
|
16
|
+
assert(t.evaluate(fact('foo' => 41), [], Factbase.new))
|
17
|
+
refute(t.evaluate(fact('bar' => 41), [], Factbase.new))
|
18
18
|
end
|
19
19
|
|
20
20
|
def test_absent
|
21
|
-
t = Factbase::Term.new(
|
22
|
-
refute(t.evaluate(fact('foo' => 41), []))
|
23
|
-
assert(t.evaluate(fact('bar' => 41), []))
|
21
|
+
t = Factbase::Term.new(:absent, [:foo])
|
22
|
+
refute(t.evaluate(fact('foo' => 41), [], Factbase.new))
|
23
|
+
assert(t.evaluate(fact('bar' => 41), [], Factbase.new))
|
24
24
|
end
|
25
25
|
|
26
26
|
def test_size
|
27
|
-
t = Factbase::Term.new(
|
28
|
-
assert_equal(1, t.evaluate(fact('foo' => 41), []))
|
29
|
-
assert_equal(0, t.evaluate(fact('foo' => nil), []))
|
30
|
-
assert_equal(4, t.evaluate(fact('foo' => [1, 2, 3, 4]), []))
|
31
|
-
assert_equal(0, t.evaluate(fact('foo' => []), []))
|
32
|
-
assert_equal(1, t.evaluate(fact('foo' => ''), []))
|
27
|
+
t = Factbase::Term.new(:size, [:foo])
|
28
|
+
assert_equal(1, t.evaluate(fact('foo' => 41), [], Factbase.new))
|
29
|
+
assert_equal(0, t.evaluate(fact('foo' => nil), [], Factbase.new))
|
30
|
+
assert_equal(4, t.evaluate(fact('foo' => [1, 2, 3, 4]), [], Factbase.new))
|
31
|
+
assert_equal(0, t.evaluate(fact('foo' => []), [], Factbase.new))
|
32
|
+
assert_equal(1, t.evaluate(fact('foo' => ''), [], Factbase.new))
|
33
33
|
end
|
34
34
|
|
35
35
|
def test_type
|
36
|
-
t = Factbase::Term.new(
|
37
|
-
assert_equal('nil', t.evaluate(fact('foo' => nil), []))
|
38
|
-
assert_equal('Integer', t.evaluate(fact('foo' => [1]), []))
|
39
|
-
assert_equal('Array', t.evaluate(fact('foo' => [1, 2]), []))
|
40
|
-
assert_equal('String', t.evaluate(fact('foo' => 'bar'), []))
|
41
|
-
assert_equal('Float', t.evaluate(fact('foo' => 2.1), []))
|
42
|
-
assert_equal('Time', t.evaluate(fact('foo' => Time.now), []))
|
36
|
+
t = Factbase::Term.new(:type, [:foo])
|
37
|
+
assert_equal('nil', t.evaluate(fact('foo' => nil), [], Factbase.new))
|
38
|
+
assert_equal('Integer', t.evaluate(fact('foo' => [1]), [], Factbase.new))
|
39
|
+
assert_equal('Array', t.evaluate(fact('foo' => [1, 2]), [], Factbase.new))
|
40
|
+
assert_equal('String', t.evaluate(fact('foo' => 'bar'), [], Factbase.new))
|
41
|
+
assert_equal('Float', t.evaluate(fact('foo' => 2.1), [], Factbase.new))
|
42
|
+
assert_equal('Time', t.evaluate(fact('foo' => Time.now), [], Factbase.new))
|
43
43
|
end
|
44
44
|
|
45
45
|
def test_nil
|
46
|
-
t = Factbase::Term.new(
|
47
|
-
assert(t.evaluate(fact('foo' => nil), []))
|
48
|
-
refute(t.evaluate(fact('foo' => true), []))
|
49
|
-
refute(t.evaluate(fact('foo' => 'bar'), []))
|
46
|
+
t = Factbase::Term.new(:nil, [:foo])
|
47
|
+
assert(t.evaluate(fact('foo' => nil), [], Factbase.new))
|
48
|
+
refute(t.evaluate(fact('foo' => true), [], Factbase.new))
|
49
|
+
refute(t.evaluate(fact('foo' => 'bar'), [], Factbase.new))
|
50
50
|
end
|
51
51
|
|
52
52
|
def test_many
|
53
|
-
t = Factbase::Term.new(
|
54
|
-
refute(t.evaluate(fact('foo' => nil), []))
|
55
|
-
refute(t.evaluate(fact('foo' => 1), []))
|
56
|
-
refute(t.evaluate(fact('foo' => '1234'), []))
|
57
|
-
assert(t.evaluate(fact('foo' => [1, 3, 5]), []))
|
58
|
-
refute(t.evaluate(fact('foo' => []), []))
|
53
|
+
t = Factbase::Term.new(:many, [:foo])
|
54
|
+
refute(t.evaluate(fact('foo' => nil), [], Factbase.new))
|
55
|
+
refute(t.evaluate(fact('foo' => 1), [], Factbase.new))
|
56
|
+
refute(t.evaluate(fact('foo' => '1234'), [], Factbase.new))
|
57
|
+
assert(t.evaluate(fact('foo' => [1, 3, 5]), [], Factbase.new))
|
58
|
+
refute(t.evaluate(fact('foo' => []), [], Factbase.new))
|
59
59
|
end
|
60
60
|
|
61
61
|
def test_one
|
62
|
-
t = Factbase::Term.new(
|
63
|
-
assert(t.evaluate(fact('foo' => 1), []))
|
64
|
-
assert(t.evaluate(fact('foo' => '1234'), []))
|
65
|
-
assert(t.evaluate(fact('foo' => [1]), []))
|
66
|
-
refute(t.evaluate(fact('foo' => nil), []))
|
67
|
-
refute(t.evaluate(fact('foo' => [1, 3, 5]), []))
|
68
|
-
refute(t.evaluate(fact('foo' => []), []))
|
62
|
+
t = Factbase::Term.new(:one, [:foo])
|
63
|
+
assert(t.evaluate(fact('foo' => 1), [], Factbase.new))
|
64
|
+
assert(t.evaluate(fact('foo' => '1234'), [], Factbase.new))
|
65
|
+
assert(t.evaluate(fact('foo' => [1]), [], Factbase.new))
|
66
|
+
refute(t.evaluate(fact('foo' => nil), [], Factbase.new))
|
67
|
+
refute(t.evaluate(fact('foo' => [1, 3, 5]), [], Factbase.new))
|
68
|
+
refute(t.evaluate(fact('foo' => []), [], Factbase.new))
|
69
69
|
end
|
70
70
|
end
|
@@ -12,17 +12,17 @@ require_relative '../../../lib/factbase/term'
|
|
12
12
|
# License:: MIT
|
13
13
|
class TestOrdering < Factbase::Test
|
14
14
|
def test_prev
|
15
|
-
t = Factbase::Term.new(
|
16
|
-
assert_nil(t.evaluate(fact('foo' => 41), []))
|
17
|
-
assert_equal([41], t.evaluate(fact('foo' => 5), []))
|
18
|
-
assert_equal([5], t.evaluate(fact('foo' => 6), []))
|
15
|
+
t = Factbase::Term.new(:prev, [:foo])
|
16
|
+
assert_nil(t.evaluate(fact('foo' => 41), [], Factbase.new))
|
17
|
+
assert_equal([41], t.evaluate(fact('foo' => 5), [], Factbase.new))
|
18
|
+
assert_equal([5], t.evaluate(fact('foo' => 6), [], Factbase.new))
|
19
19
|
end
|
20
20
|
|
21
21
|
def test_unique
|
22
|
-
t = Factbase::Term.new(
|
23
|
-
refute(t.evaluate(fact, []))
|
24
|
-
assert(t.evaluate(fact('foo' => 41), []))
|
25
|
-
refute(t.evaluate(fact('foo' => 41), []))
|
26
|
-
assert(t.evaluate(fact('foo' => 1), []))
|
22
|
+
t = Factbase::Term.new(:unique, [:foo])
|
23
|
+
refute(t.evaluate(fact, [], Factbase.new))
|
24
|
+
assert(t.evaluate(fact('foo' => 41), [], Factbase.new))
|
25
|
+
refute(t.evaluate(fact('foo' => 41), [], Factbase.new))
|
26
|
+
assert(t.evaluate(fact('foo' => 1), [], Factbase.new))
|
27
27
|
end
|
28
28
|
end
|
@@ -12,25 +12,25 @@ require_relative '../../../lib/factbase/term'
|
|
12
12
|
# License:: MIT
|
13
13
|
class TestStrings < Factbase::Test
|
14
14
|
def test_regexp_matching
|
15
|
-
t = Factbase::Term.new(
|
16
|
-
assert(t.evaluate(fact('foo' => 'hello'), []))
|
17
|
-
assert(t.evaluate(fact('foo' => 'hello 42'), []))
|
18
|
-
refute(t.evaluate(fact('foo' => 42), []))
|
15
|
+
t = Factbase::Term.new(:matches, [:foo, '[a-z]+'])
|
16
|
+
assert(t.evaluate(fact('foo' => 'hello'), [], Factbase.new))
|
17
|
+
assert(t.evaluate(fact('foo' => 'hello 42'), [], Factbase.new))
|
18
|
+
refute(t.evaluate(fact('foo' => 42), [], Factbase.new))
|
19
19
|
end
|
20
20
|
|
21
21
|
def test_concat
|
22
|
-
t = Factbase::Term.new(
|
23
|
-
s = t.evaluate(fact, [])
|
22
|
+
t = Factbase::Term.new(:concat, [42, 'hi', 3.14, :hey, Time.now])
|
23
|
+
s = t.evaluate(fact, [], Factbase.new)
|
24
24
|
assert(s.start_with?('42hi3.14'))
|
25
25
|
end
|
26
26
|
|
27
27
|
def test_concat_empty
|
28
|
-
t = Factbase::Term.new(
|
29
|
-
assert_equal('', t.evaluate(fact, []))
|
28
|
+
t = Factbase::Term.new(:concat, [])
|
29
|
+
assert_equal('', t.evaluate(fact, [], Factbase.new))
|
30
30
|
end
|
31
31
|
|
32
32
|
def test_sprintf
|
33
|
-
t = Factbase::Term.new(
|
34
|
-
assert_equal('hi, Jeff!', t.evaluate(fact, []))
|
33
|
+
t = Factbase::Term.new(:sprintf, ['hi, %s!', 'Jeff'])
|
34
|
+
assert_equal('hi, Jeff!', t.evaluate(fact, [], Factbase.new))
|
35
35
|
end
|
36
36
|
end
|
@@ -13,19 +13,19 @@ require_relative '../../../lib/factbase/term'
|
|
13
13
|
class TestSystem < Factbase::Test
|
14
14
|
def test_env
|
15
15
|
ENV.store('FOO', 'bar')
|
16
|
-
t = Factbase::Term.new(
|
17
|
-
assert_equal('bar', t.evaluate(fact, []))
|
16
|
+
t = Factbase::Term.new(:env, ['FOO', ''])
|
17
|
+
assert_equal('bar', t.evaluate(fact, [], Factbase.new))
|
18
18
|
end
|
19
19
|
|
20
20
|
def test_default
|
21
21
|
ENV.delete('FOO')
|
22
|
-
t = Factbase::Term.new(
|
23
|
-
assert_equal('мой друг', t.evaluate(fact, []))
|
22
|
+
t = Factbase::Term.new(:env, ['FOO', 'мой друг'])
|
23
|
+
assert_equal('мой друг', t.evaluate(fact, [], Factbase.new))
|
24
24
|
end
|
25
25
|
|
26
26
|
def test_when_default_is_absent
|
27
27
|
ENV.delete('FOO')
|
28
|
-
t = Factbase::Term.new(
|
29
|
-
assert_raises(StandardError) { t.evaluate(fact, []) }
|
28
|
+
t = Factbase::Term.new(:env, ['FOO'])
|
29
|
+
assert_raises(StandardError) { t.evaluate(fact, [], Factbase.new) }
|
30
30
|
end
|
31
31
|
end
|
data/test/factbase/test_accum.rb
CHANGED
@@ -15,7 +15,7 @@ require_relative '../../lib/factbase/fact'
|
|
15
15
|
class TestAccum < Factbase::Test
|
16
16
|
def test_holds_props
|
17
17
|
map = {}
|
18
|
-
f = Factbase::Fact.new(
|
18
|
+
f = Factbase::Fact.new(map)
|
19
19
|
props = {}
|
20
20
|
a = Factbase::Accum.new(f, props, false)
|
21
21
|
a.foo = 42
|
@@ -26,7 +26,7 @@ class TestAccum < Factbase::Test
|
|
26
26
|
|
27
27
|
def test_passes_props
|
28
28
|
map = {}
|
29
|
-
f = Factbase::Fact.new(
|
29
|
+
f = Factbase::Fact.new(map)
|
30
30
|
props = {}
|
31
31
|
a = Factbase::Accum.new(f, props, true)
|
32
32
|
a.foo = 42
|
@@ -37,7 +37,7 @@ class TestAccum < Factbase::Test
|
|
37
37
|
|
38
38
|
def test_appends_props
|
39
39
|
map = {}
|
40
|
-
f = Factbase::Fact.new(
|
40
|
+
f = Factbase::Fact.new(map)
|
41
41
|
f.foo = 42
|
42
42
|
props = {}
|
43
43
|
a = Factbase::Accum.new(f, props, false)
|
@@ -46,14 +46,14 @@ class TestAccum < Factbase::Test
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def test_empties
|
49
|
-
f = Factbase::Fact.new(
|
49
|
+
f = Factbase::Fact.new({})
|
50
50
|
a = Factbase::Accum.new(f, {}, false)
|
51
51
|
assert_nil(a['foo'])
|
52
52
|
end
|
53
53
|
|
54
54
|
def test_prints_to_string
|
55
55
|
map = {}
|
56
|
-
f = Factbase::Fact.new(
|
56
|
+
f = Factbase::Fact.new(map)
|
57
57
|
props = {}
|
58
58
|
a = Factbase::Accum.new(f, props, true)
|
59
59
|
a.foo = 42
|
data/test/factbase/test_fact.rb
CHANGED
@@ -14,7 +14,7 @@ require_relative '../../lib/factbase/fact'
|
|
14
14
|
class TestFact < Factbase::Test
|
15
15
|
def test_injects_data_correctly
|
16
16
|
map = {}
|
17
|
-
f = Factbase::Fact.new(
|
17
|
+
f = Factbase::Fact.new(map)
|
18
18
|
f.foo = 1
|
19
19
|
f.bar = 2
|
20
20
|
f.bar = 3
|
@@ -25,7 +25,7 @@ class TestFact < Factbase::Test
|
|
25
25
|
|
26
26
|
def test_simple_resetting
|
27
27
|
map = {}
|
28
|
-
f = Factbase::Fact.new(
|
28
|
+
f = Factbase::Fact.new(map)
|
29
29
|
f.foo = 42
|
30
30
|
assert_equal(42, f.foo, f.to_s)
|
31
31
|
f.foo = 256
|
@@ -35,7 +35,7 @@ class TestFact < Factbase::Test
|
|
35
35
|
|
36
36
|
def test_keeps_values_unique
|
37
37
|
map = {}
|
38
|
-
f = Factbase::Fact.new(
|
38
|
+
f = Factbase::Fact.new(map)
|
39
39
|
f.foo = 42
|
40
40
|
f.foo = 'Hello'
|
41
41
|
assert_equal(2, map['foo'].size)
|
@@ -44,28 +44,28 @@ class TestFact < Factbase::Test
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def test_fails_when_empty
|
47
|
-
f = Factbase::Fact.new(
|
47
|
+
f = Factbase::Fact.new({})
|
48
48
|
assert_raises(StandardError) do
|
49
49
|
f.something
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
53
|
def test_fails_when_setting_nil
|
54
|
-
f = Factbase::Fact.new(
|
54
|
+
f = Factbase::Fact.new({})
|
55
55
|
assert_raises(StandardError) do
|
56
56
|
f.foo = nil
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
60
60
|
def test_fails_when_setting_empty
|
61
|
-
f = Factbase::Fact.new(
|
61
|
+
f = Factbase::Fact.new({})
|
62
62
|
assert_raises(StandardError) do
|
63
63
|
f.foo = ''
|
64
64
|
end
|
65
65
|
end
|
66
66
|
|
67
67
|
def test_fails_when_not_found
|
68
|
-
f = Factbase::Fact.new(
|
68
|
+
f = Factbase::Fact.new({})
|
69
69
|
f.first = 42
|
70
70
|
assert_raises(StandardError) do
|
71
71
|
f.second
|
@@ -73,21 +73,21 @@ class TestFact < Factbase::Test
|
|
73
73
|
end
|
74
74
|
|
75
75
|
def test_set_by_name
|
76
|
-
f = Factbase::Fact.new(
|
76
|
+
f = Factbase::Fact.new({})
|
77
77
|
f.send(:_foo_bar=, 42)
|
78
78
|
assert_equal(42, f._foo_bar, f.to_s)
|
79
79
|
end
|
80
80
|
|
81
81
|
def test_set_twice_same_value
|
82
82
|
map = {}
|
83
|
-
f = Factbase::Fact.new(
|
83
|
+
f = Factbase::Fact.new(map)
|
84
84
|
f.foo = 42
|
85
85
|
f.foo = 42
|
86
86
|
assert_equal([42], map['foo'])
|
87
87
|
end
|
88
88
|
|
89
89
|
def test_time_in_utc
|
90
|
-
f = Factbase::Fact.new(
|
90
|
+
f = Factbase::Fact.new({})
|
91
91
|
t = Time.now
|
92
92
|
f.foo = t
|
93
93
|
assert_equal(t.utc, f.foo)
|
@@ -95,13 +95,13 @@ class TestFact < Factbase::Test
|
|
95
95
|
end
|
96
96
|
|
97
97
|
def test_some_names_are_prohibited
|
98
|
-
f = Factbase::Fact.new(
|
98
|
+
f = Factbase::Fact.new({})
|
99
99
|
assert_raises(StandardError) { f.to_s = 42 }
|
100
100
|
assert_raises(StandardError) { f.class = 42 }
|
101
101
|
end
|
102
102
|
|
103
103
|
def test_get_all_properties
|
104
|
-
f = Factbase::Fact.new(
|
104
|
+
f = Factbase::Fact.new({})
|
105
105
|
f.foo = 42
|
106
106
|
assert_includes(f.all_properties, 'foo')
|
107
107
|
end
|
@@ -48,6 +48,13 @@ class TestLogged < Factbase::Test
|
|
48
48
|
assert_includes(log.to_s, 'touched', log)
|
49
49
|
end
|
50
50
|
|
51
|
+
def test_with_slow_txn
|
52
|
+
log = Loog::Buffer.new
|
53
|
+
fb = Factbase::Logged.new(Factbase.new, log, time_tolerate: 0.1)
|
54
|
+
fb.txn { sleep 0.4 }
|
55
|
+
assert_includes(log.to_s, '(slow!)', log)
|
56
|
+
end
|
57
|
+
|
51
58
|
def test_with_txn_rollback
|
52
59
|
log = Loog::Buffer.new
|
53
60
|
fb = Factbase::Logged.new(Factbase.new, log)
|
data/test/factbase/test_query.rb
CHANGED
@@ -7,16 +7,56 @@ require_relative '../test__helper'
|
|
7
7
|
require 'time'
|
8
8
|
require_relative '../../lib/factbase'
|
9
9
|
require_relative '../../lib/factbase/query'
|
10
|
+
require_relative '../../lib/factbase/cached/cached_factbase'
|
11
|
+
require_relative '../../lib/factbase/indexed/indexed_factbase'
|
12
|
+
require_relative '../../lib/factbase/sync/sync_factbase'
|
10
13
|
|
11
14
|
# Query test.
|
12
15
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
13
16
|
# Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
|
14
17
|
# License:: MIT
|
15
18
|
class TestQuery < Factbase::Test
|
19
|
+
def test_stories
|
20
|
+
with_factbases do |badge, fb|
|
21
|
+
Dir[File.join(__dir__, '../../fixtures/stories/**/*.yml')].each do |fixture|
|
22
|
+
base = File.basename(fixture)
|
23
|
+
story = YAML.load_file(fixture)
|
24
|
+
2.times do
|
25
|
+
fb.query('(always)').delete!
|
26
|
+
story['facts'].each do |y|
|
27
|
+
f = fb.insert
|
28
|
+
y.each do |k, vv|
|
29
|
+
vv = [vv] unless vv.is_a?(Array)
|
30
|
+
vv.each do |v|
|
31
|
+
f.send(:"#{k}=", v)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
story['queries'].each do |q|
|
36
|
+
qry = q['query']
|
37
|
+
if q['size']
|
38
|
+
size = q['size']
|
39
|
+
assert_equal(size, fb.query(qry).each.to_a.size, "#{base}: #{qry} at #{badge}")
|
40
|
+
fb.txn do |fbt|
|
41
|
+
assert_equal(size, fbt.query(qry).each.to_a.size, "#{base}: #{qry} at #{badge} (in txn)")
|
42
|
+
end
|
43
|
+
else
|
44
|
+
ret = q['one']
|
45
|
+
assert_equal(ret, fb.query(qry).one, "#{base}: #{qry} at #{badge}")
|
46
|
+
fb.txn do |fbt|
|
47
|
+
assert_equal(ret, fbt.query(qry).one, "#{base}: #{qry} at #{badge} (in txn)")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
16
56
|
def test_simple_parsing
|
17
57
|
maps = []
|
18
58
|
maps << { 'foo' => [42] }
|
19
|
-
q = Factbase::Query.new(
|
59
|
+
q = Factbase::Query.new(maps, '(eq foo 42)', Factbase.new)
|
20
60
|
assert_equal(
|
21
61
|
1,
|
22
62
|
q.each do |f|
|
@@ -26,12 +66,7 @@ class TestQuery < Factbase::Test
|
|
26
66
|
end
|
27
67
|
|
28
68
|
def test_complex_parsing
|
29
|
-
|
30
|
-
{ 'num' => [42], 'name' => ['Jeff'] },
|
31
|
-
{ 'pi' => [3.14], 'num' => [42, 66, 0], 'name' => ['peter'] },
|
32
|
-
{ 'time' => [Time.now - 100], 'num' => [0], 'hi' => [4], 'nome' => ['Walter'] }
|
33
|
-
]
|
34
|
-
{
|
69
|
+
queries = {
|
35
70
|
'(eq num 444)' => 0,
|
36
71
|
'(eq hi 4)' => 1,
|
37
72
|
'(eq time 0)' => 0,
|
@@ -71,11 +106,18 @@ class TestQuery < Factbase::Test
|
|
71
106
|
'(undef something)' => 3,
|
72
107
|
"(or (eq num +66) (lt time #{(Time.now - 200).utc.iso8601}))" => 1,
|
73
108
|
'(eq 3 (agg (eq num $num) (count)))' => 1
|
74
|
-
}
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
109
|
+
}
|
110
|
+
maps = [
|
111
|
+
{ 'num' => [42], 'name' => ['Jeff'] },
|
112
|
+
{ 'num' => [42, 66, 0], 'pi' => [3.14], 'name' => ['peter'] },
|
113
|
+
{ 'num' => [0], 'time' => [Time.now - 100], 'hi' => [4], 'nome' => ['Walter'] }
|
114
|
+
]
|
115
|
+
with_factbases(maps) do |badge, fb|
|
116
|
+
queries.each do |q, r|
|
117
|
+
assert_equal(r, fb.query(q).each.to_a.size, "#{q} in #{badge}")
|
118
|
+
fb.txn do |fbt|
|
119
|
+
assert_equal(r, fbt.query(q).each.to_a.size, "#{q} in #{badge} (txn)")
|
120
|
+
end
|
79
121
|
end
|
80
122
|
end
|
81
123
|
end
|
@@ -84,7 +126,7 @@ class TestQuery < Factbase::Test
|
|
84
126
|
maps = []
|
85
127
|
now = Time.now.utc
|
86
128
|
maps << { 'foo' => [now] }
|
87
|
-
q = Factbase::Query.new(
|
129
|
+
q = Factbase::Query.new(maps, "(eq foo #{now.iso8601})", Factbase.new)
|
88
130
|
assert_equal(1, q.each.to_a.size)
|
89
131
|
end
|
90
132
|
|
@@ -94,7 +136,7 @@ class TestQuery < Factbase::Test
|
|
94
136
|
{ 'bar' => [4, 5] },
|
95
137
|
{ 'bar' => [5] }
|
96
138
|
]
|
97
|
-
q = Factbase::Query.new(
|
139
|
+
q = Factbase::Query.new(maps, '(eq bar 5)', Factbase.new)
|
98
140
|
assert_equal(2, q.delete!)
|
99
141
|
assert_equal(1, maps.size)
|
100
142
|
end
|
@@ -104,18 +146,20 @@ class TestQuery < Factbase::Test
|
|
104
146
|
{ 'foo' => [42] },
|
105
147
|
{ 'bar' => [4, 5] }
|
106
148
|
]
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
149
|
+
with_factbases(maps) do |badge, fb|
|
150
|
+
{
|
151
|
+
'(agg (exists foo) (first foo))' => [42],
|
152
|
+
'(agg (exists z) (first z))' => nil,
|
153
|
+
'(agg (always) (count))' => 2,
|
154
|
+
'(agg (eq bar $v) (count))' => 1,
|
155
|
+
'(agg (eq z 40) (count))' => 0
|
156
|
+
}.each do |q, expected|
|
157
|
+
result = fb.query(q).one(fb, v: 4)
|
158
|
+
if expected.nil?
|
159
|
+
assert_nil(result, "#{q} -> nil in #{badge}")
|
160
|
+
else
|
161
|
+
assert_equal(expected, result, "#{q} -> #{expected} in #{badge}")
|
162
|
+
end
|
119
163
|
end
|
120
164
|
end
|
121
165
|
end
|
@@ -126,28 +170,30 @@ class TestQuery < Factbase::Test
|
|
126
170
|
{ 'bar' => [4, 5] },
|
127
171
|
{ 'bar' => [5] }
|
128
172
|
]
|
129
|
-
|
130
|
-
|
131
|
-
|
173
|
+
with_factbases(maps) do |badge, fb|
|
174
|
+
q = fb.query('(never)')
|
175
|
+
assert_equal(0, q.delete!, "#{q} in #{badge}")
|
176
|
+
assert_equal(3, maps.size, "#{q} in #{badge}")
|
177
|
+
end
|
132
178
|
end
|
133
179
|
|
134
180
|
def test_to_array
|
135
181
|
maps = []
|
136
182
|
maps << { 'foo' => [42] }
|
137
|
-
assert_equal(1, Factbase::Query.new(
|
183
|
+
assert_equal(1, Factbase::Query.new(maps, '(eq foo 42)', Factbase.new).each.to_a.size)
|
138
184
|
end
|
139
185
|
|
140
186
|
def test_returns_int
|
141
187
|
maps = []
|
142
188
|
maps << { 'foo' => [1] }
|
143
|
-
q = Factbase::Query.new(
|
189
|
+
q = Factbase::Query.new(maps, '(eq foo 1)', Factbase.new)
|
144
190
|
assert_equal(1, q.each(&:to_s))
|
145
191
|
end
|
146
192
|
|
147
193
|
def test_with_aliases
|
148
194
|
maps = []
|
149
195
|
maps << { 'foo' => [42] }
|
150
|
-
assert_equal(45, Factbase::Query.new(
|
196
|
+
assert_equal(45, Factbase::Query.new(maps, '(as bar (plus foo 3))', Factbase.new).each.to_a[0].bar)
|
151
197
|
assert_equal(1, maps[0].size)
|
152
198
|
end
|
153
199
|
|
@@ -157,22 +203,38 @@ class TestQuery < Factbase::Test
|
|
157
203
|
{ 'foo' => [17] }
|
158
204
|
]
|
159
205
|
found = 0
|
160
|
-
Factbase::Query.new(
|
206
|
+
Factbase::Query.new(maps, '(eq foo $bar)', Factbase.new).each(Factbase.new, bar: [42]) do
|
161
207
|
found += 1
|
162
208
|
end
|
163
209
|
assert_equal(1, found)
|
164
|
-
assert_equal(1, Factbase::Query.new(
|
165
|
-
assert_equal(0, Factbase::Query.new(
|
210
|
+
assert_equal(1, Factbase::Query.new(maps, '(eq foo $bar)', Factbase.new).each(Factbase.new, bar: 42).to_a.size)
|
211
|
+
assert_equal(0, Factbase::Query.new(maps, '(eq foo $bar)', Factbase.new).each(Factbase.new, bar: 555).to_a.size)
|
166
212
|
end
|
167
213
|
|
168
214
|
def test_with_nil_alias
|
169
215
|
maps = [{ 'foo' => [42] }]
|
170
|
-
assert_nil(Factbase::Query.new(
|
216
|
+
assert_nil(Factbase::Query.new(maps, '(as bar (plus xxx 3))', Factbase.new).each.to_a[0]['bar'])
|
171
217
|
end
|
172
218
|
|
173
219
|
def test_get_all_properties
|
174
220
|
maps = [{ 'foo' => [42] }]
|
175
|
-
f = Factbase::Query.new(
|
221
|
+
f = Factbase::Query.new(maps, '(always)', Factbase.new).each.to_a[0]
|
176
222
|
assert_includes(f.all_properties, 'foo')
|
177
223
|
end
|
224
|
+
|
225
|
+
private
|
226
|
+
|
227
|
+
def with_factbases(maps = [], &)
|
228
|
+
{
|
229
|
+
'plain' => Factbase.new(maps),
|
230
|
+
'sync+plain' => Factbase::SyncFactbase.new(Factbase.new(maps)),
|
231
|
+
'indexed+plain' => Factbase::IndexedFactbase.new(Factbase.new(maps)),
|
232
|
+
'cached+plain' => Factbase::CachedFactbase.new(Factbase.new(maps)),
|
233
|
+
'indexed+cached+plain' => Factbase::IndexedFactbase.new(Factbase::CachedFactbase.new(Factbase.new(maps))),
|
234
|
+
'cached+indexed+plain' => Factbase::CachedFactbase.new(Factbase::IndexedFactbase.new(Factbase.new(maps))),
|
235
|
+
'sync+cached+indexed+plain' => Factbase::SyncFactbase.new(
|
236
|
+
Factbase::CachedFactbase.new(Factbase::IndexedFactbase.new(Factbase.new(maps)))
|
237
|
+
)
|
238
|
+
}.each(&)
|
239
|
+
end
|
178
240
|
end
|
data/test/factbase/test_rules.rb
CHANGED
@@ -51,7 +51,7 @@ class TestRules < Factbase::Test
|
|
51
51
|
fb = Factbase::Rules.new(Factbase.new, '(always)')
|
52
52
|
f = fb.insert
|
53
53
|
f.foo = 42
|
54
|
-
assert_equal(1, fb.query('(agg (eq foo $v) (count))').one(v: 42))
|
54
|
+
assert_equal(1, fb.query('(agg (eq foo $v) (count))').one(Factbase.new, v: 42))
|
55
55
|
end
|
56
56
|
|
57
57
|
def test_check_only_when_txn_is_closed
|