factbase 0.14.7 → 0.15.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/.github/workflows/actionlint.yml +1 -1
- data/.github/workflows/benchmark.yml +1 -1
- data/.github/workflows/codecov.yml +2 -2
- data/.github/workflows/copyrights.yml +1 -1
- data/.github/workflows/markdown-lint.yml +1 -1
- data/.github/workflows/pdd.yml +1 -1
- data/.github/workflows/rake.yml +1 -1
- data/.github/workflows/reuse.yml +1 -1
- data/.github/workflows/typos.yml +2 -2
- data/.github/workflows/xcop.yml +1 -1
- data/.github/workflows/yamllint.yml +1 -1
- data/README.md +6 -12
- data/fixtures/stories/unique.yml +14 -0
- data/lib/factbase/terms/ordering.rb +17 -8
- data/lib/factbase/version.rb +1 -1
- data/test/factbase/terms/test_ordering.rb +16 -0
- data/test/factbase/test_rules.rb +15 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0145cce5633262e8ec7e38d5d85e8500ab0bb1e1c7cb43936ef879449e9968d3
|
4
|
+
data.tar.gz: bc80e63b7f0b8729acec10d3d916576b045b3b1ce65e6972b4985c885eb3a09e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ffcfcc48eaeb4184b7d14ed7858e47d792e0192aa4451c95b17712190eacbd017d7bb92c44b4f43a69730338bc683210cf33c4321c2d5c1dd977462924a13b3f
|
7
|
+
data.tar.gz: 2285fe9c3c071795716c294fb1d24d6f863cac5b61cc20a3bb6c05b2d80a558905dd9b0f84a17dc16a16a99a1183eab668e046b6b38d56e04463b098f2487f84
|
@@ -15,7 +15,7 @@ jobs:
|
|
15
15
|
timeout-minutes: 15
|
16
16
|
runs-on: ubuntu-24.04
|
17
17
|
steps:
|
18
|
-
- uses: actions/checkout@
|
18
|
+
- uses: actions/checkout@v5
|
19
19
|
- name: Download actionlint
|
20
20
|
id: get_actionlint
|
21
21
|
run: bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash)
|
@@ -12,14 +12,14 @@ jobs:
|
|
12
12
|
timeout-minutes: 15
|
13
13
|
runs-on: ubuntu-24.04
|
14
14
|
steps:
|
15
|
-
- uses: actions/checkout@
|
15
|
+
- uses: actions/checkout@v5
|
16
16
|
- uses: ruby/setup-ruby@v1
|
17
17
|
with:
|
18
18
|
ruby-version: 3.3
|
19
19
|
bundler-cache: true
|
20
20
|
- run: bundle config set --global path "$(pwd)/vendor/bundle"
|
21
21
|
- run: bundle install --no-color
|
22
|
-
- run: bundle exec rake
|
22
|
+
- run: bundle exec rake test
|
23
23
|
- uses: codecov/codecov-action@v5
|
24
24
|
with:
|
25
25
|
token: ${{ secrets.CODECOV_TOKEN }}
|
data/.github/workflows/pdd.yml
CHANGED
data/.github/workflows/rake.yml
CHANGED
data/.github/workflows/reuse.yml
CHANGED
data/.github/workflows/typos.yml
CHANGED
data/.github/workflows/xcop.yml
CHANGED
data/README.md
CHANGED
@@ -13,19 +13,11 @@
|
|
13
13
|
[](https://app.fossa.com/projects/git%2Bgithub.com%2Fyegor256%2Ffactbase?ref=badge_shield&issueType=license)
|
14
14
|
|
15
15
|
This Ruby gem manages an in-memory database of facts.
|
16
|
-
A fact is simply
|
16
|
+
A fact is simply an associative array of properties and their values.
|
17
17
|
The values are either atomic literals or non-empty sets of literals.
|
18
18
|
It is possible to delete a fact, but impossible to delete a property
|
19
19
|
from a fact.
|
20
20
|
|
21
|
-
**ATTENTION**: The current implementation is naive and,
|
22
|
-
because of that, **very slow**. I will be very happy
|
23
|
-
if you suggest a better implementation without the change of the interface.
|
24
|
-
The `Factbase::query()` method is what mostly needs performance optimization:
|
25
|
-
currently it simply iterates through all facts in the factbase in order
|
26
|
-
to find those that match the provided terms. Obviously,
|
27
|
-
even a simple indexing may significantly increase performance.
|
28
|
-
|
29
21
|
Here is how you use it (it's thread-safe, by the way):
|
30
22
|
|
31
23
|
```ruby
|
@@ -42,7 +34,7 @@ fb.query('(not (exists seen))').each do |f|
|
|
42
34
|
end
|
43
35
|
```
|
44
36
|
|
45
|
-
You can save the factbase to the
|
37
|
+
You can save the factbase to the disk and then load it back:
|
46
38
|
|
47
39
|
```ruby
|
48
40
|
file = '/tmp/simple.fb'
|
@@ -133,7 +125,7 @@ properties in the facts that match `(gt x 5)`
|
|
133
125
|
Also, some simple arithmetic:
|
134
126
|
|
135
127
|
* `(plus v1 v2)` is a sum of `∑v1` and `∑v2`
|
136
|
-
* `(minus v1 v2)` is a
|
128
|
+
* `(minus v1 v2)` is a deduction of `∑v2` from `∑v1`
|
137
129
|
* `(times v1 v2)` is a multiplication of `∏v1` and `∏v2`
|
138
130
|
* `(div v1 v2)` is a division of `∏v1` by `∏v2`
|
139
131
|
|
@@ -155,7 +147,9 @@ returns `true`
|
|
155
147
|
There are terms that are history of search aware:
|
156
148
|
|
157
149
|
* `(prev p)` returns the value of `p` property in the previously seen fact
|
158
|
-
* `(unique
|
150
|
+
* `(unique p1 p2 ...)` returns true if at least one property value
|
151
|
+
hasn't been seen yet; returns false when all specified properties
|
152
|
+
have duplicate values in this particular combination
|
159
153
|
|
160
154
|
The `agg` term enables sub-queries by evaluating the first argument (term)
|
161
155
|
over all available facts, passing the entire subset to the second argument,
|
data/fixtures/stories/unique.yml
CHANGED
@@ -11,6 +11,20 @@ facts:
|
|
11
11
|
price: 4300
|
12
12
|
- name: BMW
|
13
13
|
price: 4400
|
14
|
+
- name: Mercedes
|
15
|
+
color: red
|
16
|
+
year: 2020
|
17
|
+
- name: Mercedes
|
18
|
+
color: blue
|
19
|
+
year: 2020
|
20
|
+
- name: Mercedes
|
21
|
+
color: red
|
22
|
+
year: 2021
|
23
|
+
- name: Mercedes
|
24
|
+
color: red
|
25
|
+
year: 2020
|
14
26
|
queries:
|
15
27
|
- query: (and (exists price) (unique name))
|
16
28
|
size: 2
|
29
|
+
- query: (and (eq name "Mercedes") (unique color year))
|
30
|
+
size: 3
|
@@ -20,15 +20,24 @@ module Factbase::Ordering
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def unique(fact, maps, fb)
|
23
|
-
@uniques =
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
return false if
|
30
|
-
|
23
|
+
@uniques = {} if @uniques.nil?
|
24
|
+
raise "Too few operands for 'unique' (at least 1 expected)" if @operands.empty?
|
25
|
+
results = []
|
26
|
+
@operands.each_with_index do |_, i|
|
27
|
+
@uniques[i] = [] if @uniques[i].nil?
|
28
|
+
vv = _values(i, fact, maps, fb)
|
29
|
+
return false if vv.nil?
|
30
|
+
vv = [vv] unless vv.respond_to?(:to_a)
|
31
|
+
vv.each do |v|
|
32
|
+
if @uniques[i].include?(v)
|
33
|
+
results << false
|
34
|
+
else
|
35
|
+
@uniques[i] << v
|
36
|
+
results << true
|
37
|
+
end
|
38
|
+
end
|
31
39
|
end
|
40
|
+
return false if results.all?(false)
|
32
41
|
true
|
33
42
|
end
|
34
43
|
end
|
data/lib/factbase/version.rb
CHANGED
@@ -25,4 +25,20 @@ class TestOrdering < Factbase::Test
|
|
25
25
|
refute(t.evaluate(fact('foo' => 41), [], Factbase.new))
|
26
26
|
assert(t.evaluate(fact('foo' => 1), [], Factbase.new))
|
27
27
|
end
|
28
|
+
|
29
|
+
def test_unique_with_multiple_arguments
|
30
|
+
t = Factbase::Term.new(:unique, %i[foo bar])
|
31
|
+
assert(t.evaluate(fact('foo' => 1, 'bar' => 'a'), [], Factbase.new))
|
32
|
+
assert(t.evaluate(fact('foo' => 1, 'bar' => 'b'), [], Factbase.new))
|
33
|
+
assert(t.evaluate(fact('foo' => 2, 'bar' => 'a'), [], Factbase.new))
|
34
|
+
refute(t.evaluate(fact('foo' => 1, 'bar' => 'a'), [], Factbase.new))
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_unique_stops_when_all_arguments_are_duplicates
|
38
|
+
t = Factbase::Term.new(:unique, %i[foo bar baz])
|
39
|
+
assert(t.evaluate(fact('foo' => 1, 'bar' => 'x', 'baz' => true), [], Factbase.new))
|
40
|
+
assert(t.evaluate(fact('foo' => 2, 'bar' => 'x', 'baz' => false), [], Factbase.new))
|
41
|
+
assert(t.evaluate(fact('foo' => 1, 'bar' => 'y', 'baz' => true), [], Factbase.new))
|
42
|
+
refute(t.evaluate(fact('foo' => 1, 'bar' => 'x', 'baz' => true), [], Factbase.new))
|
43
|
+
end
|
28
44
|
end
|
data/test/factbase/test_rules.rb
CHANGED
@@ -92,6 +92,21 @@ class TestRules < Factbase::Test
|
|
92
92
|
end
|
93
93
|
end
|
94
94
|
|
95
|
+
def test_fails_if_id_duplicates_are_there_already
|
96
|
+
fb = Factbase::Rules.new(Factbase.new, '(always)', uid: 'id')
|
97
|
+
fb.insert.then do |f|
|
98
|
+
f.id = 1
|
99
|
+
f.id = 2
|
100
|
+
end
|
101
|
+
assert_raises(StandardError) do
|
102
|
+
fb.txn do |fbt|
|
103
|
+
fbt.insert.then do |f|
|
104
|
+
f.foo = 42
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
95
110
|
def test_in_combination_with_pre
|
96
111
|
fb = Factbase::Rules.new(Factbase.new, '(when (exists a) (exists b))')
|
97
112
|
fb =
|
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.15.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yegor Bugayenko
|
@@ -312,7 +312,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
312
312
|
- !ruby/object:Gem::Version
|
313
313
|
version: '0'
|
314
314
|
requirements: []
|
315
|
-
rubygems_version: 3.6.
|
315
|
+
rubygems_version: 3.6.9
|
316
316
|
specification_version: 4
|
317
317
|
summary: Factbase
|
318
318
|
test_files: []
|