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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/benchmark.yml +3 -1
  3. data/.gitignore +5 -5
  4. data/.rultor.yml +1 -1
  5. data/Gemfile +1 -1
  6. data/Gemfile.lock +4 -4
  7. data/README.md +29 -25
  8. data/REUSE.toml +4 -0
  9. data/Rakefile +10 -3
  10. data/benchmark/bench_factbase.rb +56 -0
  11. data/benchmark/bench_query.rb +41 -0
  12. data/benchmark/bench_taped.rb +33 -0
  13. data/lib/factbase/churn.rb +4 -0
  14. data/lib/factbase/logged.rb +172 -0
  15. data/lib/factbase/looged.rb +2 -2
  16. data/lib/factbase/taped.rb +16 -18
  17. data/lib/factbase.rb +10 -11
  18. data/test/factbase/terms/test_aggregates.rb +2 -2
  19. data/test/factbase/terms/test_aliases.rb +4 -4
  20. data/test/factbase/terms/test_casting.rb +1 -2
  21. data/test/factbase/terms/test_debug.rb +2 -2
  22. data/test/factbase/terms/test_defn.rb +2 -2
  23. data/test/factbase/terms/test_logical.rb +2 -2
  24. data/test/factbase/terms/test_math.rb +1 -2
  25. data/test/factbase/terms/test_meta.rb +2 -2
  26. data/test/factbase/terms/test_ordering.rb +2 -2
  27. data/test/factbase/terms/test_strings.rb +2 -2
  28. data/test/factbase/terms/test_system.rb +1 -2
  29. data/test/factbase/test_accum.rb +2 -2
  30. data/test/factbase/test_churn.rb +3 -2
  31. data/test/factbase/test_fact.rb +2 -2
  32. data/test/factbase/test_flatten.rb +2 -2
  33. data/test/factbase/test_inv.rb +2 -2
  34. data/test/factbase/test_logged.rb +129 -0
  35. data/test/factbase/test_looged.rb +8 -8
  36. data/test/factbase/test_pre.rb +2 -2
  37. data/test/factbase/test_query.rb +2 -2
  38. data/test/factbase/test_rules.rb +2 -2
  39. data/test/factbase/test_syntax.rb +2 -2
  40. data/test/factbase/test_tallied.rb +2 -2
  41. data/test/factbase/test_taped.rb +12 -2
  42. data/test/factbase/test_tee.rb +2 -2
  43. data/test/factbase/test_term.rb +2 -2
  44. data/test/factbase/test_to_json.rb +2 -2
  45. data/test/factbase/test_to_xml.rb +2 -2
  46. data/test/factbase/test_to_yaml.rb +2 -2
  47. data/test/test__helper.rb +3 -1
  48. data/test/test_factbase.rb +45 -12
  49. metadata +8 -7
  50. data/benchmarks/simple.rb +0 -127
@@ -3,7 +3,7 @@
3
3
  # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
4
4
  # SPDX-License-Identifier: MIT
5
5
 
6
- require 'minitest/autorun'
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 < Minitest::Test
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
@@ -3,7 +3,7 @@
3
3
  # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
4
4
  # SPDX-License-Identifier: MIT
5
5
 
6
- require 'minitest/autorun'
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 < Minitest::Test
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
@@ -3,14 +3,14 @@
3
3
  # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
4
4
  # SPDX-License-Identifier: MIT
5
5
 
6
- require 'minitest/autorun'
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 < Minitest::Test
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
- require 'minitest/autorun'
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 < Minitest::Test
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
- require 'minitest/autorun'
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 < Minitest::Test
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
- require 'minitest/autorun'
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 < Minitest::Test
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)
@@ -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/looged'
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 < Minitest::Test
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::Looged.new(Factbase.new, Loog::NULL)
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(0, fb.query('(always)').each.to_a.size)
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.2
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-24 00:00:00.000000000 Z
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
- - benchmarks/simple.rb
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.4.10
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 }