factbase 0.11.1 → 0.12.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/.rultor.yml +2 -2
- data/Gemfile.lock +1 -1
- data/README.md +24 -24
- data/factbase.gemspec +1 -1
- data/lib/factbase/term.rb +2 -2
- data/lib/factbase/terms/debug.rb +25 -0
- data/lib/factbase/version.rb +13 -0
- data/lib/factbase.rb +1 -2
- data/test/factbase/terms/test_debug.rb +75 -0
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f31200ddcd8de0d737dbabea9aefc051794f86bbae0891f8d624545912972add
|
4
|
+
data.tar.gz: 7c1688856cde3e72f8e9f53e846b39c7cef508a0df95cc88d0631bfae36ce07f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 26c83d3a288f8106bfe17856bccb9c1bde6b130384e9a4546456a76c1b6a69fff9c49954789aaeee6413f7cfae638d6f1320f1de4f7ac000b3c552b31449ebbd
|
7
|
+
data.tar.gz: 761d577bc112c4d542667508924406570ff449fbab87af3a18fc0ce7c8a94cccf6d40f400b1b1c3f6bd76d95a51678a680fd17feeee0b89caacbac2b56e8e6f6
|
data/.rultor.yml
CHANGED
@@ -16,8 +16,8 @@ release:
|
|
16
16
|
[[ "${tag}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || exit -1
|
17
17
|
bundle exec rake
|
18
18
|
rm -rf *.gem
|
19
|
-
sed -i "s/0\.0\.0/${tag}/g" lib/factbase.rb
|
20
|
-
git add lib/factbase.rb
|
19
|
+
sed -i "s/0\.0\.0/${tag}/g" lib/factbase/version.rb
|
20
|
+
git add lib/factbase/version.rb
|
21
21
|
git commit -m "version set to ${tag}"
|
22
22
|
gem build factbase.gemspec
|
23
23
|
chmod 0600 ../rubygems.yml
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -210,33 +210,33 @@ This is the result of the benchmark:
|
|
210
210
|
<!-- benchmark_begin -->
|
211
211
|
```text
|
212
212
|
user
|
213
|
-
insert 20000 facts 0.
|
214
|
-
export 20000 facts 0.
|
215
|
-
import
|
216
|
-
insert 10 facts 0.
|
217
|
-
query 10 times w/txn 1.
|
218
|
-
query 10 times w/o txn 0.
|
219
|
-
modify 10 attrs w/txn 1.
|
220
|
-
delete 10 facts w/txn 1.
|
221
|
-
(and (eq what 'issue-was-closed') (exists... -> 200 2.
|
222
|
-
(and (eq what 'issue-was-closed') (exists... -> 200/txn 1.
|
223
|
-
(and (eq what 'issue-was-closed') (exists... -> zero 2.
|
224
|
-
(and (eq what 'issue-was-closed') (exists... -> zero/txn 1.
|
225
|
-
(gt time '2024-03-23T03:21:43Z') 0.
|
226
|
-
(gt cost 50) 0.
|
227
|
-
(eq title 'Object Thinking 5000') 0.
|
228
|
-
(and (eq foo 42.998) (or (gt bar 200) (absent zzz))) 0.
|
229
|
-
(eq id (agg (always) (max id))) 0.
|
230
|
-
(join "c<=cost,b<=bar" (eq id (agg (always) (max id)))) 0.
|
231
|
-
delete! 0.
|
232
|
-
Taped.append() x50000 0.
|
233
|
-
Taped.each() x125 1.
|
234
|
-
Taped.delete_if() x375 0.
|
213
|
+
insert 20000 facts 0.618307
|
214
|
+
export 20000 facts 0.021391
|
215
|
+
import 410726 bytes (20000 facts) 0.030150
|
216
|
+
insert 10 facts 0.045651
|
217
|
+
query 10 times w/txn 1.970434
|
218
|
+
query 10 times w/o txn 0.039459
|
219
|
+
modify 10 attrs w/txn 1.819769
|
220
|
+
delete 10 facts w/txn 1.056563
|
221
|
+
(and (eq what 'issue-was-closed') (exists... -> 200 2.125753
|
222
|
+
(and (eq what 'issue-was-closed') (exists... -> 200/txn 1.135618
|
223
|
+
(and (eq what 'issue-was-closed') (exists... -> zero 2.422668
|
224
|
+
(and (eq what 'issue-was-closed') (exists... -> zero/txn 1.288236
|
225
|
+
(gt time '2024-03-23T03:21:43Z') 0.108849
|
226
|
+
(gt cost 50) 0.090092
|
227
|
+
(eq title 'Object Thinking 5000') 0.002640
|
228
|
+
(and (eq foo 42.998) (or (gt bar 200) (absent zzz))) 0.023785
|
229
|
+
(eq id (agg (always) (max id))) 0.252980
|
230
|
+
(join "c<=cost,b<=bar" (eq id (agg (always) (max id)))) 0.655997
|
231
|
+
delete! 0.075510
|
232
|
+
Taped.append() x50000 0.042923
|
233
|
+
Taped.each() x125 1.343819
|
234
|
+
Taped.delete_if() x375 0.830124
|
235
235
|
```
|
236
236
|
|
237
237
|
The results were calculated in [this GHA job][benchmark-gha]
|
238
|
-
on 2025-06-
|
238
|
+
on 2025-06-21 at 08:24,
|
239
239
|
on Linux with 4 CPUs.
|
240
240
|
<!-- benchmark_end -->
|
241
241
|
|
242
|
-
[benchmark-gha]: https://github.com/yegor256/factbase/actions/runs/
|
242
|
+
[benchmark-gha]: https://github.com/yegor256/factbase/actions/runs/15793882855
|
data/factbase.gemspec
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
# SPDX-License-Identifier: MIT
|
5
5
|
|
6
6
|
require 'English'
|
7
|
-
require_relative 'lib/factbase'
|
7
|
+
require_relative 'lib/factbase/version'
|
8
8
|
|
9
9
|
Gem::Specification.new do |s|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new('>= 0') if s.respond_to? :required_rubygems_version=
|
data/lib/factbase/term.rb
CHANGED
@@ -118,9 +118,9 @@ class Factbase::Term
|
|
118
118
|
def evaluate(fact, maps, fb)
|
119
119
|
send(@op, fact, maps, fb)
|
120
120
|
rescue NoMethodError => e
|
121
|
-
raise "Probably the term '#{@op}' is not defined at #{self}
|
121
|
+
raise "Probably the term '#{@op}' is not defined at #{self}: #{e.message}"
|
122
122
|
rescue StandardError => e
|
123
|
-
raise "#{e.message} at #{self}
|
123
|
+
raise "#{e.message} at #{self}"
|
124
124
|
end
|
125
125
|
|
126
126
|
# Simplify it if possible.
|
data/lib/factbase/terms/debug.rb
CHANGED
@@ -19,4 +19,29 @@ module Factbase::Debug
|
|
19
19
|
puts "#{self} -> #{r}"
|
20
20
|
r
|
21
21
|
end
|
22
|
+
|
23
|
+
def assert(fact, maps, fb)
|
24
|
+
assert_args(2)
|
25
|
+
message = @operands[0]
|
26
|
+
unless message.is_a?(String)
|
27
|
+
raise ArgumentError,
|
28
|
+
"A string expected as first argument of 'assert', but '#{message}' provided"
|
29
|
+
end
|
30
|
+
t = @operands[1]
|
31
|
+
unless t.is_a?(Factbase::Term)
|
32
|
+
raise ArgumentError,
|
33
|
+
"A term expected as second argument of 'assert', but '#{t}' provided"
|
34
|
+
end
|
35
|
+
result = t.evaluate(fact, maps, fb)
|
36
|
+
# Convert result to boolean-like evaluation
|
37
|
+
# Arrays are truthy if they contain at least one truthy element
|
38
|
+
truthy =
|
39
|
+
if result.is_a?(Array)
|
40
|
+
result.any? { |v| v && v != 0 }
|
41
|
+
else
|
42
|
+
result && result != 0
|
43
|
+
end
|
44
|
+
raise message unless truthy
|
45
|
+
true
|
46
|
+
end
|
22
47
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
|
4
|
+
# SPDX-License-Identifier: MIT
|
5
|
+
|
6
|
+
# Just version.
|
7
|
+
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
8
|
+
# Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
|
9
|
+
# License:: MIT
|
10
|
+
class Factbase
|
11
|
+
# Current version of the gem (changed by .rultor.yml on every release)
|
12
|
+
VERSION = '0.12.0' unless const_defined?(:VERSION)
|
13
|
+
end
|
data/lib/factbase.rb
CHANGED
@@ -81,8 +81,7 @@ require 'yaml'
|
|
81
81
|
# Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
|
82
82
|
# License:: MIT
|
83
83
|
class Factbase
|
84
|
-
|
85
|
-
VERSION = '0.11.1' unless const_defined?(:VERSION)
|
84
|
+
require_relative 'factbase/version'
|
86
85
|
|
87
86
|
# An exception that may be thrown in a transaction, to roll it back.
|
88
87
|
class Rollback < StandardError; end
|
@@ -33,4 +33,79 @@ class TestDebug < Factbase::Test
|
|
33
33
|
end
|
34
34
|
assert_match(/Too many \(\d+\) operands for 'traced' \(\d+ expected\)/, e.message)
|
35
35
|
end
|
36
|
+
|
37
|
+
def test_assert_with_true_condition
|
38
|
+
t = Factbase::Term.new(:assert, ['all must be positive', Factbase::Term.new(:gt, [:foo, 0])])
|
39
|
+
assert(t.evaluate(fact('foo' => 5), [], Factbase.new))
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_assert_with_false_condition
|
43
|
+
t = Factbase::Term.new(:assert, ['all must be positive', Factbase::Term.new(:gt, [:foo, 0])])
|
44
|
+
e =
|
45
|
+
assert_raises(RuntimeError) do
|
46
|
+
t.evaluate(fact('foo' => -1), [], Factbase.new)
|
47
|
+
end
|
48
|
+
assert_equal("all must be positive at (assert 'all must be positive' (gt foo 0))", e.message)
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_assert_with_zero_value
|
52
|
+
t = Factbase::Term.new(:assert, ['value must not be zero', Factbase::Term.new(:gt, [:foo, 0])])
|
53
|
+
e =
|
54
|
+
assert_raises(RuntimeError) do
|
55
|
+
t.evaluate(fact('foo' => 0), [], Factbase.new)
|
56
|
+
end
|
57
|
+
assert_equal("value must not be zero at (assert 'value must not be zero' (gt foo 0))", e.message)
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_assert_with_array_true_condition
|
61
|
+
t = Factbase::Term.new(:assert, ['at least one positive', Factbase::Term.new(:gt, [:foo, 0])])
|
62
|
+
assert(t.evaluate(fact('foo' => [1, 2, 3]), [], Factbase.new))
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_assert_with_array_false_condition
|
66
|
+
t = Factbase::Term.new(:assert, ['at least one positive', Factbase::Term.new(:gt, [:foo, 0])])
|
67
|
+
e =
|
68
|
+
assert_raises(RuntimeError) do
|
69
|
+
t.evaluate(fact('foo' => [-1, -2, -3]), [], Factbase.new)
|
70
|
+
end
|
71
|
+
assert_equal("at least one positive at (assert 'at least one positive' (gt foo 0))", e.message)
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_assert_with_mixed_array
|
75
|
+
t = Factbase::Term.new(:assert, ['at least one positive', Factbase::Term.new(:gt, [:foo, 0])])
|
76
|
+
assert(t.evaluate(fact('foo' => [-1, 0, 3]), [], Factbase.new))
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_assert_raises_when_message_not_string
|
80
|
+
e =
|
81
|
+
assert_raises(StandardError) do
|
82
|
+
Factbase::Term.new(:assert, [123, Factbase::Term.new(:gt, [:foo, 0])]).evaluate(fact, [], Factbase.new)
|
83
|
+
end
|
84
|
+
assert_match(/A string expected as first argument of 'assert', but '123' provided/, e.message)
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_assert_raises_when_second_arg_not_term
|
88
|
+
e =
|
89
|
+
assert_raises(StandardError) do
|
90
|
+
Factbase::Term.new(:assert, %w[message not_a_term]).evaluate(fact, [], Factbase.new)
|
91
|
+
end
|
92
|
+
assert_match(/A term expected as second argument of 'assert', but 'not_a_term' provided/, e.message)
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_assert_raises_when_too_few_args
|
96
|
+
e =
|
97
|
+
assert_raises(StandardError) do
|
98
|
+
Factbase::Term.new(:assert, ['message']).evaluate(fact, [], Factbase.new)
|
99
|
+
end
|
100
|
+
assert_match(/Too few \(\d+\) operands for 'assert' \(\d+ expected\)/, e.message)
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_assert_raises_when_too_many_args
|
104
|
+
e =
|
105
|
+
assert_raises(StandardError) do
|
106
|
+
Factbase::Term.new(:assert, ['message', Factbase::Term.new(:gt, [:foo, 0]), 'extra']).evaluate(fact, [],
|
107
|
+
Factbase.new)
|
108
|
+
end
|
109
|
+
assert_match(/Too many \(\d+\) operands for 'assert' \(\d+ expected\)/, e.message)
|
110
|
+
end
|
36
111
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: factbase
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.12.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yegor Bugayenko
|
@@ -249,6 +249,7 @@ files:
|
|
249
249
|
- lib/factbase/to_json.rb
|
250
250
|
- lib/factbase/to_xml.rb
|
251
251
|
- lib/factbase/to_yaml.rb
|
252
|
+
- lib/factbase/version.rb
|
252
253
|
- renovate.json
|
253
254
|
- test/factbase/cached/test_cached_factbase.rb
|
254
255
|
- test/factbase/cached/test_cached_query.rb
|