factbase 0.7.2 → 0.7.4
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/.github/workflows/benchmark.yml +3 -1
- data/.gitignore +5 -5
- data/.rultor.yml +1 -1
- data/Gemfile +1 -1
- data/Gemfile.lock +4 -4
- data/README.md +29 -25
- data/REUSE.toml +4 -0
- data/Rakefile +10 -3
- data/benchmark/bench_factbase.rb +56 -0
- data/benchmark/bench_query.rb +41 -0
- data/benchmark/bench_taped.rb +33 -0
- data/lib/factbase/churn.rb +4 -0
- data/lib/factbase/logged.rb +172 -0
- data/lib/factbase/looged.rb +2 -2
- data/lib/factbase/taped.rb +16 -18
- data/lib/factbase.rb +10 -11
- data/test/factbase/terms/test_aggregates.rb +2 -2
- data/test/factbase/terms/test_aliases.rb +4 -4
- data/test/factbase/terms/test_casting.rb +1 -2
- data/test/factbase/terms/test_debug.rb +2 -2
- data/test/factbase/terms/test_defn.rb +2 -2
- data/test/factbase/terms/test_logical.rb +2 -2
- data/test/factbase/terms/test_math.rb +1 -2
- data/test/factbase/terms/test_meta.rb +2 -2
- data/test/factbase/terms/test_ordering.rb +2 -2
- data/test/factbase/terms/test_strings.rb +2 -2
- data/test/factbase/terms/test_system.rb +1 -2
- data/test/factbase/test_accum.rb +2 -2
- data/test/factbase/test_churn.rb +3 -2
- data/test/factbase/test_fact.rb +2 -2
- data/test/factbase/test_flatten.rb +2 -2
- data/test/factbase/test_inv.rb +2 -2
- data/test/factbase/test_logged.rb +129 -0
- data/test/factbase/test_looged.rb +8 -8
- data/test/factbase/test_pre.rb +2 -2
- data/test/factbase/test_query.rb +2 -2
- data/test/factbase/test_rules.rb +2 -2
- data/test/factbase/test_syntax.rb +2 -2
- data/test/factbase/test_tallied.rb +2 -2
- data/test/factbase/test_taped.rb +12 -2
- data/test/factbase/test_tee.rb +2 -2
- data/test/factbase/test_term.rb +2 -2
- data/test/factbase/test_to_json.rb +2 -2
- data/test/factbase/test_to_xml.rb +2 -2
- data/test/factbase/test_to_yaml.rb +2 -2
- data/test/test__helper.rb +3 -1
- data/test/test_factbase.rb +45 -12
- metadata +8 -7
- data/benchmarks/simple.rb +0 -127
data/test/factbase/test_taped.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
|
4
4
|
# SPDX-License-Identifier: MIT
|
5
5
|
|
6
|
-
|
6
|
+
require_relative '../test__helper'
|
7
7
|
require_relative '../../lib/factbase'
|
8
8
|
require_relative '../../lib/factbase/taped'
|
9
9
|
|
@@ -11,7 +11,7 @@ require_relative '../../lib/factbase/taped'
|
|
11
11
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
12
12
|
# Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
|
13
13
|
# License:: MIT
|
14
|
-
class TestTaped <
|
14
|
+
class TestTaped < Factbase::Test
|
15
15
|
def test_tracks_insertion
|
16
16
|
t = Factbase::Taped.new([])
|
17
17
|
t << {}
|
@@ -33,4 +33,14 @@ class TestTaped < Minitest::Test
|
|
33
33
|
assert_equal(1, t.added.size)
|
34
34
|
assert_equal(h.object_id, t.added.first)
|
35
35
|
end
|
36
|
+
|
37
|
+
def test_tracks_addition_uniquely
|
38
|
+
h = { f: 5 }
|
39
|
+
t = Factbase::Taped.new([h])
|
40
|
+
t.each do |m|
|
41
|
+
m[:bar] = 66
|
42
|
+
m[:foo] = 77
|
43
|
+
end
|
44
|
+
assert_equal(1, t.added.size)
|
45
|
+
end
|
36
46
|
end
|
data/test/factbase/test_tee.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
|
4
4
|
# SPDX-License-Identifier: MIT
|
5
5
|
|
6
|
-
|
6
|
+
require_relative '../test__helper'
|
7
7
|
require_relative '../../lib/factbase'
|
8
8
|
require_relative '../../lib/factbase/tee'
|
9
9
|
require_relative '../../lib/factbase/fact'
|
@@ -12,7 +12,7 @@ require_relative '../../lib/factbase/fact'
|
|
12
12
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
13
13
|
# Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
|
14
14
|
# License:: MIT
|
15
|
-
class TestTee <
|
15
|
+
class TestTee < Factbase::Test
|
16
16
|
def test_two_facts
|
17
17
|
prim = Factbase::Fact.new(Factbase.new, Mutex.new, {})
|
18
18
|
prim.foo = 42
|
data/test/factbase/test_term.rb
CHANGED
@@ -3,14 +3,14 @@
|
|
3
3
|
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
|
4
4
|
# SPDX-License-Identifier: MIT
|
5
5
|
|
6
|
-
|
6
|
+
require_relative '../test__helper'
|
7
7
|
require_relative '../../lib/factbase/term'
|
8
8
|
|
9
9
|
# Term test.
|
10
10
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
11
11
|
# Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
|
12
12
|
# License:: MIT
|
13
|
-
class TestTerm <
|
13
|
+
class TestTerm < Factbase::Test
|
14
14
|
def test_false_matching
|
15
15
|
t = Factbase::Term.new(Factbase.new, :never, [])
|
16
16
|
refute(t.evaluate(fact('foo' => [100]), []))
|
@@ -3,7 +3,7 @@
|
|
3
3
|
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
|
4
4
|
# SPDX-License-Identifier: MIT
|
5
5
|
|
6
|
-
|
6
|
+
require_relative '../test__helper'
|
7
7
|
require 'loog'
|
8
8
|
require_relative '../../lib/factbase'
|
9
9
|
require_relative '../../lib/factbase/to_json'
|
@@ -12,7 +12,7 @@ require_relative '../../lib/factbase/to_json'
|
|
12
12
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
13
13
|
# Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
|
14
14
|
# License:: MIT
|
15
|
-
class TestToJSON <
|
15
|
+
class TestToJSON < Factbase::Test
|
16
16
|
def test_simple_rendering
|
17
17
|
fb = Factbase.new
|
18
18
|
f = fb.insert
|
@@ -3,7 +3,7 @@
|
|
3
3
|
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
|
4
4
|
# SPDX-License-Identifier: MIT
|
5
5
|
|
6
|
-
|
6
|
+
require_relative '../test__helper'
|
7
7
|
require 'loog'
|
8
8
|
require_relative '../../lib/factbase'
|
9
9
|
require_relative '../../lib/factbase/to_xml'
|
@@ -12,7 +12,7 @@ require_relative '../../lib/factbase/to_xml'
|
|
12
12
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
13
13
|
# Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
|
14
14
|
# License:: MIT
|
15
|
-
class TestToXML <
|
15
|
+
class TestToXML < Factbase::Test
|
16
16
|
def test_simple_rendering
|
17
17
|
fb = Factbase.new
|
18
18
|
fb.insert.t = Time.now
|
@@ -3,7 +3,7 @@
|
|
3
3
|
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
|
4
4
|
# SPDX-License-Identifier: MIT
|
5
5
|
|
6
|
-
|
6
|
+
require_relative '../test__helper'
|
7
7
|
require 'loog'
|
8
8
|
require_relative '../../lib/factbase'
|
9
9
|
require_relative '../../lib/factbase/to_yaml'
|
@@ -12,7 +12,7 @@ require_relative '../../lib/factbase/to_yaml'
|
|
12
12
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
13
13
|
# Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
|
14
14
|
# License:: MIT
|
15
|
-
class TestToYAML <
|
15
|
+
class TestToYAML < Factbase::Test
|
16
16
|
def test_simple_rendering
|
17
17
|
fb = Factbase.new
|
18
18
|
f = fb.insert
|
data/test/test__helper.rb
CHANGED
@@ -17,8 +17,10 @@ require 'minitest/autorun'
|
|
17
17
|
require 'minitest/reporters'
|
18
18
|
Minitest::Reporters.use! [Minitest::Reporters::SpecReporter.new]
|
19
19
|
|
20
|
+
require_relative '../lib/factbase'
|
21
|
+
|
20
22
|
# Default methods for all tests.
|
21
|
-
class Minitest::Test
|
23
|
+
class Factbase::Test < Minitest::Test
|
22
24
|
def fact(map = {})
|
23
25
|
require 'factbase/fact'
|
24
26
|
Factbase::Fact.new(Factbase.new, Mutex.new, map)
|
data/test/test_factbase.rb
CHANGED
@@ -3,20 +3,20 @@
|
|
3
3
|
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
|
4
4
|
# SPDX-License-Identifier: MIT
|
5
5
|
|
6
|
-
require 'minitest/autorun'
|
7
6
|
require 'loog'
|
8
7
|
require 'threads'
|
9
8
|
require_relative '../lib/factbase'
|
10
|
-
require_relative '../lib/factbase/rules'
|
11
9
|
require_relative '../lib/factbase/inv'
|
10
|
+
require_relative '../lib/factbase/logged'
|
12
11
|
require_relative '../lib/factbase/pre'
|
13
|
-
require_relative '../lib/factbase/
|
12
|
+
require_relative '../lib/factbase/rules'
|
13
|
+
require_relative 'test__helper'
|
14
14
|
|
15
15
|
# Factbase main module test.
|
16
16
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
17
17
|
# Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
|
18
18
|
# License:: MIT
|
19
|
-
class TestFactbase <
|
19
|
+
class TestFactbase < Factbase::Test
|
20
20
|
def test_injects_data_correctly
|
21
21
|
maps = []
|
22
22
|
fb = Factbase.new(maps)
|
@@ -104,11 +104,11 @@ class TestFactbase < Minitest::Test
|
|
104
104
|
|
105
105
|
def test_txn_returns_boolean
|
106
106
|
fb = Factbase.new
|
107
|
-
assert_equal(1, fb.txn(&:insert))
|
108
|
-
assert_equal(1, fb.txn { |fbt| fbt.insert.bar = 42 })
|
109
|
-
assert_equal(0, fb.txn { |fbt| fbt.query('(always)').each.to_a })
|
110
|
-
assert_equal(2, fb.txn { |fbt| fbt.query('(always)').each { |f| f.hello = 33 } })
|
111
|
-
assert_equal(1, fb.txn { |fbt| fbt.query('(always)').each.to_a[0].zzz = 33 })
|
107
|
+
assert_equal(1, fb.txn(&:insert).to_i)
|
108
|
+
assert_equal(1, fb.txn { |fbt| fbt.insert.bar = 42 }.to_i)
|
109
|
+
assert_equal(0, fb.txn { |fbt| fbt.query('(always)').each.to_a }.to_i)
|
110
|
+
assert_equal(2, fb.txn { |fbt| fbt.query('(always)').each { |f| f.hello = 33 } }.to_i)
|
111
|
+
assert_equal(1, fb.txn { |fbt| fbt.query('(always)').each.to_a[0].zzz = 33 }.to_i)
|
112
112
|
end
|
113
113
|
|
114
114
|
def test_appends_in_txn
|
@@ -177,7 +177,7 @@ class TestFactbase < Minitest::Test
|
|
177
177
|
Factbase::Rules.new(Factbase.new, '(always)'),
|
178
178
|
Factbase::Inv.new(Factbase.new) { |_, _| true },
|
179
179
|
Factbase::Pre.new(Factbase.new) { |_| true },
|
180
|
-
Factbase::
|
180
|
+
Factbase::Logged.new(Factbase.new, Loog::NULL)
|
181
181
|
].each do |d|
|
182
182
|
f = d.insert
|
183
183
|
f.foo = 42
|
@@ -212,15 +212,48 @@ class TestFactbase < Minitest::Test
|
|
212
212
|
assert_equal(1, fb.query('(exists xyz)').each.to_a.size)
|
213
213
|
end
|
214
214
|
|
215
|
+
def test_evals_complex_txn
|
216
|
+
fb = Factbase.new
|
217
|
+
n = fb.insert
|
218
|
+
n.foo = 42
|
219
|
+
n.bar = 555
|
220
|
+
fb.txn do |fbt|
|
221
|
+
fbt.insert.foo = 333
|
222
|
+
fbt.insert.bar = 333
|
223
|
+
fbt.query('(eq bar 555)').delete!
|
224
|
+
fbt.insert.bar = 555
|
225
|
+
fbt.query('(eq bar 777)').delete! # nothing to be deleted
|
226
|
+
n1 = fbt.insert
|
227
|
+
n1.bar = 9
|
228
|
+
n1.bar = 99
|
229
|
+
end
|
230
|
+
assert_equal(4, fb.query('(always)').each.to_a.size)
|
231
|
+
end
|
232
|
+
|
215
233
|
def test_txn_with_rollback
|
216
234
|
fb = Factbase.new
|
235
|
+
n = fb.insert
|
236
|
+
n.bar = 55
|
217
237
|
modified =
|
218
238
|
fb.txn do |fbt|
|
219
239
|
fbt.insert.bar = 33
|
240
|
+
fbt.query('(eq bar 55)').each.to_a.first.boom = 44
|
220
241
|
raise Factbase::Rollback
|
221
242
|
end
|
222
|
-
assert_equal(0, modified)
|
223
|
-
assert_equal(
|
243
|
+
assert_equal(0, modified.to_i)
|
244
|
+
assert_equal(1, fb.query('(always)').each.to_a.size)
|
245
|
+
assert_equal(0, fb.query('(exists boom)').each.to_a.size)
|
246
|
+
end
|
247
|
+
|
248
|
+
def test_modifies_existing_fact_in_txn
|
249
|
+
fb = Factbase.new
|
250
|
+
n = fb.insert
|
251
|
+
n.foo = 1
|
252
|
+
fb.txn do |fbt|
|
253
|
+
f = fbt.query('(always)').each.to_a.first
|
254
|
+
f.bar = 2
|
255
|
+
end
|
256
|
+
assert_equal(1, fb.query('(eq foo 1)').each.to_a.size)
|
224
257
|
end
|
225
258
|
|
226
259
|
def test_simple_concurrent_inserts
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: factbase
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yegor Bugayenko
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date: 2025-02-
|
10
|
+
date: 2025-02-28 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: backtrace
|
@@ -158,7 +157,9 @@ files:
|
|
158
157
|
- README.md
|
159
158
|
- REUSE.toml
|
160
159
|
- Rakefile
|
161
|
-
-
|
160
|
+
- benchmark/bench_factbase.rb
|
161
|
+
- benchmark/bench_query.rb
|
162
|
+
- benchmark/bench_taped.rb
|
162
163
|
- factbase.gemspec
|
163
164
|
- lib/factbase.rb
|
164
165
|
- lib/factbase/accum.rb
|
@@ -167,6 +168,7 @@ files:
|
|
167
168
|
- lib/factbase/flatten.rb
|
168
169
|
- lib/factbase/inv.rb
|
169
170
|
- lib/factbase/light.rb
|
171
|
+
- lib/factbase/logged.rb
|
170
172
|
- lib/factbase/looged.rb
|
171
173
|
- lib/factbase/pre.rb
|
172
174
|
- lib/factbase/query.rb
|
@@ -209,6 +211,7 @@ files:
|
|
209
211
|
- test/factbase/test_fact.rb
|
210
212
|
- test/factbase/test_flatten.rb
|
211
213
|
- test/factbase/test_inv.rb
|
214
|
+
- test/factbase/test_logged.rb
|
212
215
|
- test/factbase/test_looged.rb
|
213
216
|
- test/factbase/test_pre.rb
|
214
217
|
- test/factbase/test_query.rb
|
@@ -228,7 +231,6 @@ licenses:
|
|
228
231
|
- MIT
|
229
232
|
metadata:
|
230
233
|
rubygems_mfa_required: 'true'
|
231
|
-
post_install_message:
|
232
234
|
rdoc_options:
|
233
235
|
- "--charset=UTF-8"
|
234
236
|
require_paths:
|
@@ -244,8 +246,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
244
246
|
- !ruby/object:Gem::Version
|
245
247
|
version: '0'
|
246
248
|
requirements: []
|
247
|
-
rubygems_version: 3.
|
248
|
-
signing_key:
|
249
|
+
rubygems_version: 3.6.2
|
249
250
|
specification_version: 4
|
250
251
|
summary: Factbase
|
251
252
|
test_files: []
|
data/benchmarks/simple.rb
DELETED
@@ -1,127 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
|
5
|
-
# SPDX-License-Identifier: MIT
|
6
|
-
|
7
|
-
require 'benchmark'
|
8
|
-
require 'time'
|
9
|
-
require_relative '../lib/factbase'
|
10
|
-
|
11
|
-
def insert(fb, total)
|
12
|
-
time =
|
13
|
-
Benchmark.measure do
|
14
|
-
total.times do |i|
|
15
|
-
fact = fb.insert
|
16
|
-
fact.id = i
|
17
|
-
fact.title = "Object Thinking #{i}"
|
18
|
-
fact.time = Time.now.iso8601
|
19
|
-
fact.cost = rand(1..100)
|
20
|
-
fact.foo = rand(0.0..100.0).round(3)
|
21
|
-
fact.bar = rand(100..300)
|
22
|
-
fact.seenBy = "User#{i}" if i.even?
|
23
|
-
fact.zzz = "Extra#{i}" if (i % 10).zero?
|
24
|
-
end
|
25
|
-
end
|
26
|
-
{
|
27
|
-
title: '`fb.insert()`',
|
28
|
-
time: time.real,
|
29
|
-
details: "Inserted #{total} facts"
|
30
|
-
}
|
31
|
-
end
|
32
|
-
|
33
|
-
def query(fb, query)
|
34
|
-
total = 0
|
35
|
-
runs = 100
|
36
|
-
time =
|
37
|
-
Benchmark.measure do
|
38
|
-
runs.times do
|
39
|
-
total = fb.query(query).each.to_a.size
|
40
|
-
end
|
41
|
-
end
|
42
|
-
{
|
43
|
-
title: "`#{query}`",
|
44
|
-
time: time.real.round(6),
|
45
|
-
details: "#{total} facts x#{runs}"
|
46
|
-
}
|
47
|
-
end
|
48
|
-
|
49
|
-
def delete(fb, query)
|
50
|
-
total = 0
|
51
|
-
time =
|
52
|
-
Benchmark.measure do
|
53
|
-
total = fb.query(query).delete!
|
54
|
-
end
|
55
|
-
{
|
56
|
-
title: "`#{query}`",
|
57
|
-
time: time.real.round(6),
|
58
|
-
details: "Deleted #{total} fact(s)"
|
59
|
-
}
|
60
|
-
end
|
61
|
-
|
62
|
-
def impex(fb)
|
63
|
-
size = 0
|
64
|
-
time =
|
65
|
-
Benchmark.measure do
|
66
|
-
bin = fb.export
|
67
|
-
size = bin.size
|
68
|
-
fb2 = Factbase.new
|
69
|
-
fb2.import(bin)
|
70
|
-
end
|
71
|
-
{
|
72
|
-
title: '`.export()` + `.import()`',
|
73
|
-
time: time.real,
|
74
|
-
details: "#{size} bytes"
|
75
|
-
}
|
76
|
-
end
|
77
|
-
|
78
|
-
def txn(fb, scenario, &block)
|
79
|
-
modified = 0
|
80
|
-
time =
|
81
|
-
Benchmark.measure do
|
82
|
-
modified = fb.txn(&block)
|
83
|
-
end
|
84
|
-
{
|
85
|
-
title: "txn: `#{scenario}`",
|
86
|
-
time: time.real,
|
87
|
-
details: "modified #{modified} facts"
|
88
|
-
}
|
89
|
-
end
|
90
|
-
|
91
|
-
fb = Factbase.new
|
92
|
-
rows = [
|
93
|
-
insert(fb, 25_000),
|
94
|
-
query(fb, '(gt time \'2024-03-23T03:21:43Z\')'),
|
95
|
-
query(fb, '(gt cost 50)'),
|
96
|
-
query(fb, '(eq title \'Object Thinking 5000\')'),
|
97
|
-
query(fb, '(and (eq foo 42.998) (or (gt bar 200) (absent zzz)))'),
|
98
|
-
query(fb, '(eq id (agg (always) (max id)))'),
|
99
|
-
query(fb, '(join "c<=cost,b<=bar" (eq id (agg (always) (max id))))'),
|
100
|
-
txn(fb, 'query()') do |fbt|
|
101
|
-
100.times do |i|
|
102
|
-
fbt.query("(gt foo #{i})").each.to_a
|
103
|
-
end
|
104
|
-
end,
|
105
|
-
txn(fb, 'insert()') do |fbt|
|
106
|
-
100.times do
|
107
|
-
fbt.insert
|
108
|
-
end
|
109
|
-
end,
|
110
|
-
txn(fb, 'add()') do |fbt|
|
111
|
-
100.times do |i|
|
112
|
-
fbt.query("(gt foo #{i})").each.to_a.first.bar = 55
|
113
|
-
end
|
114
|
-
end,
|
115
|
-
txn(fb, 'delete!()') do |fbt|
|
116
|
-
50.times do |i|
|
117
|
-
fbt.query("(gt foo #{100 - i})").delete!
|
118
|
-
end
|
119
|
-
end,
|
120
|
-
impex(fb),
|
121
|
-
delete(fb, '(gt cost 3)'),
|
122
|
-
delete(fb, '(gt bar 1)')
|
123
|
-
].map { |r| "| #{r[:title]} | #{format('%0.3f', r[:time])} | #{r[:details]} |" }
|
124
|
-
|
125
|
-
puts '| Action | Seconds | Details |'
|
126
|
-
puts '| --- | --: | --- |'
|
127
|
-
rows.each { |row| puts row }
|