factbase 0.9.0 → 0.9.1
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/Gemfile.lock +11 -11
- data/README.md +24 -24
- data/lib/factbase/inv.rb +18 -6
- data/lib/factbase/logged.rb +40 -38
- data/lib/factbase/rules.rb +2 -2
- data/lib/factbase/tallied.rb +2 -2
- data/lib/factbase.rb +1 -1
- data/test/factbase/test_query.rb +11 -0
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d4918d14dba9ff6a434de85e418f0f8249b4e3ea2e3bf7ae1c7cd1ed0836b20c
|
4
|
+
data.tar.gz: 2810d6061d951226e42b51f8d0f6afb27698ad16bdda448088a7dff7dd930937
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 975e9b4c4deda01bd2177c44adbf9049b784ffa76e17b3718f9a6103582eda766c5ae9df7eb92235acaa25823ed8a5bfa42cfd5245a11182132d977c2851ff65
|
7
|
+
data.tar.gz: 914915154434ed8265aad9c1cda22e8e6dbd14ba3f2248478f45579418aa4dcb90752550eab337c80936faba9e384dd09b44539a6ff74278b71d4298b2efc858
|
data/Gemfile.lock
CHANGED
@@ -14,9 +14,9 @@ PATH
|
|
14
14
|
GEM
|
15
15
|
remote: https://rubygems.org/
|
16
16
|
specs:
|
17
|
-
actionpack (8.0.
|
18
|
-
actionview (= 8.0.
|
19
|
-
activesupport (= 8.0.
|
17
|
+
actionpack (8.0.2)
|
18
|
+
actionview (= 8.0.2)
|
19
|
+
activesupport (= 8.0.2)
|
20
20
|
nokogiri (>= 1.8.5)
|
21
21
|
rack (>= 2.2.4)
|
22
22
|
rack-session (>= 1.0.1)
|
@@ -24,13 +24,13 @@ GEM
|
|
24
24
|
rails-dom-testing (~> 2.2)
|
25
25
|
rails-html-sanitizer (~> 1.6)
|
26
26
|
useragent (~> 0.16)
|
27
|
-
actionview (8.0.
|
28
|
-
activesupport (= 8.0.
|
27
|
+
actionview (8.0.2)
|
28
|
+
activesupport (= 8.0.2)
|
29
29
|
builder (~> 3.1)
|
30
30
|
erubi (~> 1.11)
|
31
31
|
rails-dom-testing (~> 2.2)
|
32
32
|
rails-html-sanitizer (~> 1.6)
|
33
|
-
activesupport (8.0.
|
33
|
+
activesupport (8.0.2)
|
34
34
|
base64
|
35
35
|
benchmark (>= 0.3)
|
36
36
|
bigdecimal
|
@@ -66,7 +66,7 @@ GEM
|
|
66
66
|
pp (>= 0.6.0)
|
67
67
|
rdoc (>= 4.0.0)
|
68
68
|
reline (>= 0.4.2)
|
69
|
-
json (2.10.
|
69
|
+
json (2.10.2)
|
70
70
|
language_server-protocol (3.17.0.4)
|
71
71
|
lint_roller (1.1.0)
|
72
72
|
logger (1.6.6)
|
@@ -100,7 +100,7 @@ GEM
|
|
100
100
|
date
|
101
101
|
stringio
|
102
102
|
racc (1.8.1)
|
103
|
-
rack (3.1.
|
103
|
+
rack (3.1.12)
|
104
104
|
rack-session (2.1.0)
|
105
105
|
base64 (>= 0.1.0)
|
106
106
|
rack (>= 3.0.0)
|
@@ -115,9 +115,9 @@ GEM
|
|
115
115
|
rails-html-sanitizer (1.6.2)
|
116
116
|
loofah (~> 2.21)
|
117
117
|
nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
|
118
|
-
railties (8.0.
|
119
|
-
actionpack (= 8.0.
|
120
|
-
activesupport (= 8.0.
|
118
|
+
railties (8.0.2)
|
119
|
+
actionpack (= 8.0.2)
|
120
|
+
activesupport (= 8.0.2)
|
121
121
|
irb (~> 1.13)
|
122
122
|
rackup (>= 1.0.0)
|
123
123
|
rake (>= 12.2)
|
data/README.md
CHANGED
@@ -209,33 +209,33 @@ This is the result of the benchmark:
|
|
209
209
|
<!-- benchmark_begin -->
|
210
210
|
```text
|
211
211
|
user system total real
|
212
|
-
insert 20000 facts 0.
|
213
|
-
export 20000 facts 0.
|
214
|
-
import
|
215
|
-
insert 10 facts 0.
|
216
|
-
query 10 times w/txn
|
217
|
-
query 10 times w/o txn 0.
|
218
|
-
modify 10 attrs w/txn
|
219
|
-
delete 10 facts w/txn 0.
|
220
|
-
(and (eq what 'issue-was-closed') (exists... -> 200 2.
|
221
|
-
(and (eq what 'issue-was-closed') (exists... -> 200/txn 2.
|
222
|
-
(and (eq what 'issue-was-closed') (exists... -> zero 4.
|
223
|
-
(and (eq what 'issue-was-closed') (exists... -> zero/txn 4.
|
224
|
-
(gt time '2024-03-23T03:21:43Z') 0.
|
225
|
-
(gt cost 50) 0.
|
226
|
-
(eq title 'Object Thinking 5000') 0.
|
227
|
-
(and (eq foo 42.998) (or (gt bar 200) (absent zzz))) 0.
|
228
|
-
(eq id (agg (always) (max id))) 0.
|
229
|
-
(join "c<=cost,b<=bar" (eq id (agg (always) (max id)))) 1.
|
230
|
-
delete! 0.
|
231
|
-
Taped.append() x50000 0.
|
232
|
-
Taped.each() x125 1.
|
233
|
-
Taped.delete_if() x375 0.
|
212
|
+
insert 20000 facts 0.625135 0.006294 0.631429 ( 0.631443)
|
213
|
+
export 20000 facts 0.023877 0.000991 0.024868 ( 0.024873)
|
214
|
+
import 411050 bytes (20000 facts) 0.034864 0.004984 0.039848 ( 0.039853)
|
215
|
+
insert 10 facts 0.045526 0.002959 0.048485 ( 0.048490)
|
216
|
+
query 10 times w/txn 2.221776 0.028103 2.249879 ( 2.249996)
|
217
|
+
query 10 times w/o txn 0.842098 0.002019 0.844117 ( 0.844159)
|
218
|
+
modify 10 attrs w/txn 2.056211 0.014021 2.070232 ( 2.070541)
|
219
|
+
delete 10 facts w/txn 0.770236 0.003988 0.774224 ( 0.774272)
|
220
|
+
(and (eq what 'issue-was-closed') (exists... -> 200 2.805609 0.003005 2.808614 ( 2.808765)
|
221
|
+
(and (eq what 'issue-was-closed') (exists... -> 200/txn 2.812425 0.002000 2.814425 ( 2.814779)
|
222
|
+
(and (eq what 'issue-was-closed') (exists... -> zero 4.288405 0.000001 4.288406 ( 4.288758)
|
223
|
+
(and (eq what 'issue-was-closed') (exists... -> zero/txn 4.320962 0.008005 4.328967 ( 4.329469)
|
224
|
+
(gt time '2024-03-23T03:21:43Z') 0.116707 0.001996 0.118703 ( 0.118716)
|
225
|
+
(gt cost 50) 0.093508 0.000004 0.093512 ( 0.093517)
|
226
|
+
(eq title 'Object Thinking 5000') 0.006989 0.000000 0.006989 ( 0.006990)
|
227
|
+
(and (eq foo 42.998) (or (gt bar 200) (absent zzz))) 0.060383 0.000002 0.060385 ( 0.060385)
|
228
|
+
(eq id (agg (always) (max id))) 0.357125 0.000994 0.358119 ( 0.358130)
|
229
|
+
(join "c<=cost,b<=bar" (eq id (agg (always) (max id)))) 1.350979 0.000001 1.350980 ( 1.351072)
|
230
|
+
delete! 0.054491 0.000004 0.054495 ( 0.054497)
|
231
|
+
Taped.append() x50000 0.048166 0.001997 0.050163 ( 0.050167)
|
232
|
+
Taped.each() x125 1.347655 0.002002 1.349657 ( 1.349747)
|
233
|
+
Taped.delete_if() x375 0.846874 0.000003 0.846877 ( 0.846926)
|
234
234
|
```
|
235
235
|
|
236
236
|
The results were calculated in [this GHA job][benchmark-gha]
|
237
|
-
on 2025-03-12 at
|
237
|
+
on 2025-03-12 at 13:25,
|
238
238
|
on Linux with 4 CPUs.
|
239
239
|
<!-- benchmark_end -->
|
240
240
|
|
241
|
-
[benchmark-gha]: https://github.com/yegor256/factbase/actions/runs/
|
241
|
+
[benchmark-gha]: https://github.com/yegor256/factbase/actions/runs/13812482648
|
data/lib/factbase/inv.rb
CHANGED
@@ -8,6 +8,17 @@ require 'decoor'
|
|
8
8
|
require_relative '../factbase'
|
9
9
|
|
10
10
|
# A decorator of a Factbase, that checks invariants on every set.
|
11
|
+
#
|
12
|
+
# For example, you can use this decorator if you want to check that every
|
13
|
+
# fact has +when+:
|
14
|
+
#
|
15
|
+
# fb = Factbase::Inv.new(Factbase.new) do |f, fbt|
|
16
|
+
# assert !f['when'].nil?
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# The second argument passed to the block is the factbase, while the first
|
20
|
+
# one is the fact just touched.
|
21
|
+
#
|
11
22
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
12
23
|
# Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
|
13
24
|
# License:: MIT
|
@@ -23,8 +34,8 @@ class Factbase::Inv
|
|
23
34
|
Fact.new(@fb.insert, @block)
|
24
35
|
end
|
25
36
|
|
26
|
-
def query(query)
|
27
|
-
Query.new(@fb.query(query), @block)
|
37
|
+
def query(query, maps = nil)
|
38
|
+
Query.new(@fb.query(query, maps), @block, self)
|
28
39
|
end
|
29
40
|
|
30
41
|
def txn
|
@@ -65,14 +76,15 @@ class Factbase::Inv
|
|
65
76
|
class Query
|
66
77
|
decoor(:query)
|
67
78
|
|
68
|
-
def initialize(query, block)
|
79
|
+
def initialize(query, block, fb)
|
69
80
|
@query = query
|
70
81
|
@block = block
|
82
|
+
@fb = fb
|
71
83
|
end
|
72
84
|
|
73
|
-
def each
|
74
|
-
return to_enum(__method__) unless block_given?
|
75
|
-
@query.each do |f|
|
85
|
+
def each(fb = @fb, params = {})
|
86
|
+
return to_enum(__method__, fb, params) unless block_given?
|
87
|
+
@query.each(fb, params) do |f|
|
76
88
|
yield Fact.new(f, @block)
|
77
89
|
end
|
78
90
|
end
|
data/lib/factbase/logged.rb
CHANGED
@@ -22,7 +22,7 @@ class Factbase::Logged
|
|
22
22
|
# @param [Print] tube The tube to use, if log is NIL
|
23
23
|
def initialize(fb, log = nil, time_tolerate: 1, tube: nil)
|
24
24
|
raise 'The "fb" is nil' if fb.nil?
|
25
|
-
@
|
25
|
+
@origin = fb
|
26
26
|
if log.nil?
|
27
27
|
raise 'Either "log" or "tube" must be non-NIL' if tube.nil?
|
28
28
|
@tube = tube
|
@@ -31,17 +31,18 @@ class Factbase::Logged
|
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
-
decoor(:
|
34
|
+
decoor(:origin)
|
35
35
|
|
36
36
|
def insert
|
37
37
|
start = Time.now
|
38
|
-
f = @
|
39
|
-
@tube.say(start, "Inserted new fact ##{@
|
38
|
+
f = @origin.insert
|
39
|
+
@tube.say(start, "Inserted new fact ##{@origin.size} in #{start.ago}")
|
40
40
|
Fact.new(f, tube: @tube)
|
41
41
|
end
|
42
42
|
|
43
|
-
def query(
|
44
|
-
|
43
|
+
def query(term, maps = nil)
|
44
|
+
term = to_term(term) if term.is_a?(String)
|
45
|
+
Query.new(term, maps, @tube, @origin)
|
45
46
|
end
|
46
47
|
|
47
48
|
def txn
|
@@ -49,7 +50,7 @@ class Factbase::Logged
|
|
49
50
|
id = nil
|
50
51
|
rollback = false
|
51
52
|
r =
|
52
|
-
@
|
53
|
+
@origin.txn do |fbt|
|
53
54
|
id = fbt.object_id
|
54
55
|
yield Factbase::Logged.new(fbt, tube: @tube)
|
55
56
|
rescue Factbase::Rollback => e
|
@@ -122,40 +123,25 @@ class Factbase::Logged
|
|
122
123
|
# Query decorator.
|
123
124
|
#
|
124
125
|
# This is an internal class, it is not supposed to be instantiated directly.
|
125
|
-
#
|
126
126
|
class Query
|
127
|
-
def initialize(
|
128
|
-
@
|
127
|
+
def initialize(term, maps, tube, fb)
|
128
|
+
@term = term
|
129
|
+
@maps = maps
|
129
130
|
@tube = tube
|
130
131
|
@fb = fb
|
131
132
|
end
|
132
133
|
|
133
|
-
def
|
134
|
-
|
135
|
-
q = Factbase::Syntax.new(@expr).to_term.to_s
|
136
|
-
r = nil
|
137
|
-
tail =
|
138
|
-
Factbase::Logged.elapsed do
|
139
|
-
r = fb.query(@expr).one(fb, params)
|
140
|
-
end
|
141
|
-
if r.nil?
|
142
|
-
@tube.say(start, "Nothing found by '#{q}' #{tail}")
|
143
|
-
else
|
144
|
-
@tube.say(start, "Found #{r} (#{r.class}) by '#{q}' #{tail}")
|
145
|
-
end
|
146
|
-
r
|
147
|
-
end
|
148
|
-
|
149
|
-
def each(fb = @fb, params = {}, &)
|
134
|
+
def each(fb = nil, params = {}, &)
|
135
|
+
return to_enum(__method__, fb, params) unless block_given?
|
150
136
|
start = Time.now
|
151
|
-
q = Factbase::Syntax.new(@
|
137
|
+
q = Factbase::Syntax.new(@term).to_term.to_s
|
152
138
|
if block_given?
|
153
139
|
r = nil
|
154
140
|
tail =
|
155
141
|
Factbase::Logged.elapsed do
|
156
|
-
r = fb.query(@
|
142
|
+
r = @fb.query(@term, @maps).each(@fb, params, &)
|
157
143
|
end
|
158
|
-
raise ".each of #{@
|
144
|
+
raise ".each of #{@term.class} returned #{r.class}" unless r.is_a?(Integer)
|
159
145
|
if r.zero?
|
160
146
|
@tube.say(start, "Nothing found by '#{q}' #{tail}")
|
161
147
|
else
|
@@ -166,7 +152,7 @@ class Factbase::Logged
|
|
166
152
|
array = []
|
167
153
|
tail =
|
168
154
|
Factbase::Logged.elapsed do
|
169
|
-
fb.query(@
|
155
|
+
@fb.query(@term, @maps).each(@fb, params) do |f|
|
170
156
|
array << f
|
171
157
|
end
|
172
158
|
end
|
@@ -179,21 +165,37 @@ class Factbase::Logged
|
|
179
165
|
end
|
180
166
|
end
|
181
167
|
|
182
|
-
def
|
168
|
+
def one(_fb = nil, params = {})
|
169
|
+
start = Time.now
|
170
|
+
q = Factbase::Syntax.new(@term).to_term.to_s
|
171
|
+
r = nil
|
172
|
+
tail =
|
173
|
+
Factbase::Logged.elapsed do
|
174
|
+
r = @fb.query(@term, @maps).one(@fb, params)
|
175
|
+
end
|
176
|
+
if r.nil?
|
177
|
+
@tube.say(start, "Nothing found by '#{q}' #{tail}")
|
178
|
+
else
|
179
|
+
@tube.say(start, "Found #{r} (#{r.class}) by '#{q}' #{tail}")
|
180
|
+
end
|
181
|
+
r
|
182
|
+
end
|
183
|
+
|
184
|
+
def delete!(_fb = nil)
|
183
185
|
r = nil
|
184
186
|
start = Time.now
|
185
|
-
before = fb.size
|
187
|
+
before = @fb.size
|
186
188
|
tail =
|
187
189
|
Factbase::Logged.elapsed do
|
188
|
-
r = @fb.query(@
|
190
|
+
r = @fb.query(@term).delete!(@fb)
|
189
191
|
end
|
190
|
-
raise ".delete! of #{@
|
192
|
+
raise ".delete! of #{@term.class} returned #{r.class}" unless r.is_a?(Integer)
|
191
193
|
if before.zero?
|
192
|
-
@tube.say(start, "There were no facts, nothing deleted by '#{@
|
194
|
+
@tube.say(start, "There were no facts, nothing deleted by '#{@term}' #{tail}")
|
193
195
|
elsif r.zero?
|
194
|
-
@tube.say(start, "No facts out of #{before} deleted by '#{@
|
196
|
+
@tube.say(start, "No facts out of #{before} deleted by '#{@term}' #{tail}")
|
195
197
|
else
|
196
|
-
@tube.say(start, "Deleted #{r} fact(s) out of #{before} by '#{@
|
198
|
+
@tube.say(start, "Deleted #{r} fact(s) out of #{before} by '#{@term}' #{tail}")
|
197
199
|
end
|
198
200
|
r
|
199
201
|
end
|
data/lib/factbase/rules.rb
CHANGED
data/lib/factbase/tallied.rb
CHANGED
data/lib/factbase.rb
CHANGED
@@ -82,7 +82,7 @@ require 'yaml'
|
|
82
82
|
# License:: MIT
|
83
83
|
class Factbase
|
84
84
|
# Current version of the gem (changed by .rultor.yml on every release)
|
85
|
-
VERSION = '0.9.
|
85
|
+
VERSION = '0.9.1'
|
86
86
|
|
87
87
|
# An exception that may be thrown in a transaction, to roll it back.
|
88
88
|
class Rollback < StandardError; end
|
data/test/factbase/test_query.rb
CHANGED
@@ -4,9 +4,15 @@
|
|
4
4
|
# SPDX-License-Identifier: MIT
|
5
5
|
|
6
6
|
require_relative '../test__helper'
|
7
|
+
require 'loog'
|
7
8
|
require 'time'
|
8
9
|
require_relative '../../lib/factbase'
|
9
10
|
require_relative '../../lib/factbase/query'
|
11
|
+
require_relative '../../lib/factbase/logged'
|
12
|
+
require_relative '../../lib/factbase/pre'
|
13
|
+
require_relative '../../lib/factbase/inv'
|
14
|
+
require_relative '../../lib/factbase/rules'
|
15
|
+
require_relative '../../lib/factbase/tallied'
|
10
16
|
require_relative '../../lib/factbase/cached/cached_factbase'
|
11
17
|
require_relative '../../lib/factbase/indexed/indexed_factbase'
|
12
18
|
require_relative '../../lib/factbase/sync/sync_factbase'
|
@@ -227,9 +233,14 @@ class TestQuery < Factbase::Test
|
|
227
233
|
def with_factbases(maps = [], &)
|
228
234
|
{
|
229
235
|
'plain' => Factbase.new(maps),
|
236
|
+
'pre+plain' => Factbase::Pre.new(Factbase.new(maps)) { nil },
|
237
|
+
'rules+plain' => Factbase::Rules.new(Factbase.new(maps), '(always)'),
|
238
|
+
'inv+plain' => Factbase::Inv.new(Factbase.new(maps)) { nil },
|
230
239
|
'sync+plain' => Factbase::SyncFactbase.new(Factbase.new(maps)),
|
240
|
+
'tallied+plain' => Factbase::Tallied.new(Factbase.new(maps)),
|
231
241
|
'indexed+plain' => Factbase::IndexedFactbase.new(Factbase.new(maps)),
|
232
242
|
'cached+plain' => Factbase::CachedFactbase.new(Factbase.new(maps)),
|
243
|
+
'logged+plain' => Factbase::Logged.new(Factbase.new(maps), Loog::NULL),
|
233
244
|
'indexed+cached+plain' => Factbase::IndexedFactbase.new(Factbase::CachedFactbase.new(Factbase.new(maps))),
|
234
245
|
'cached+indexed+plain' => Factbase::CachedFactbase.new(Factbase::IndexedFactbase.new(Factbase.new(maps))),
|
235
246
|
'sync+cached+indexed+plain' => Factbase::SyncFactbase.new(
|