factbase 0.14.6 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a74a73e5bfec2f7ff39d41428d6c1cbacaaf374d7b2d4b987df0833a0864f0d3
4
- data.tar.gz: 684f79dcdae19e8fe5a2b3deb6b21d786e5f9b75e81624a08a5a28f9af3e8597
3
+ metadata.gz: 0145cce5633262e8ec7e38d5d85e8500ab0bb1e1c7cb43936ef879449e9968d3
4
+ data.tar.gz: bc80e63b7f0b8729acec10d3d916576b045b3b1ce65e6972b4985c885eb3a09e
5
5
  SHA512:
6
- metadata.gz: 29ebcc501c156e5d84d216224918df28df4dfb114e438139b90276a297bdefa373bc848d0d93919035444a40f29c77e83aa22a2e29e5c04e10aa3d5db71068a0
7
- data.tar.gz: 8741e804d14332085756fe5c15edb1efa28d0db8357e445f42a097d96d6f63fe87bb48ca2f9b7239e7ed87ca87d7722001be6bf8564824384033b1bb86bb543f
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@v4
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)
@@ -16,7 +16,7 @@ jobs:
16
16
  timeout-minutes: 15
17
17
  runs-on: ubuntu-24.04
18
18
  steps:
19
- - uses: actions/checkout@v4
19
+ - uses: actions/checkout@v5
20
20
  - uses: ruby/setup-ruby@v1
21
21
  with:
22
22
  ruby-version: 3.3
@@ -12,14 +12,14 @@ jobs:
12
12
  timeout-minutes: 15
13
13
  runs-on: ubuntu-24.04
14
14
  steps:
15
- - uses: actions/checkout@v4
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 }}
@@ -15,5 +15,5 @@ jobs:
15
15
  timeout-minutes: 15
16
16
  runs-on: ubuntu-24.04
17
17
  steps:
18
- - uses: actions/checkout@v4
18
+ - uses: actions/checkout@v5
19
19
  - uses: yegor256/copyrights-action@0.0.12
@@ -19,5 +19,5 @@ jobs:
19
19
  timeout-minutes: 15
20
20
  runs-on: ubuntu-24.04
21
21
  steps:
22
- - uses: actions/checkout@v4
22
+ - uses: actions/checkout@v5
23
23
  - uses: DavidAnson/markdownlint-cli2-action@v20.0.0
@@ -15,5 +15,5 @@ jobs:
15
15
  timeout-minutes: 15
16
16
  runs-on: ubuntu-24.04
17
17
  steps:
18
- - uses: actions/checkout@v4
18
+ - uses: actions/checkout@v5
19
19
  - uses: volodya-lombrozo/pdd-action@master
@@ -18,7 +18,7 @@ jobs:
18
18
  ruby: [3.3]
19
19
  runs-on: ${{ matrix.os }}
20
20
  steps:
21
- - uses: actions/checkout@v4
21
+ - uses: actions/checkout@v5
22
22
  - uses: ruby/setup-ruby@v1
23
23
  with:
24
24
  ruby-version: ${{ matrix.ruby }}
@@ -15,5 +15,5 @@ jobs:
15
15
  timeout-minutes: 15
16
16
  runs-on: ubuntu-24.04
17
17
  steps:
18
- - uses: actions/checkout@v4
18
+ - uses: actions/checkout@v5
19
19
  - uses: fsfe/reuse-action@v5
@@ -15,5 +15,5 @@ jobs:
15
15
  timeout-minutes: 15
16
16
  runs-on: ubuntu-24.04
17
17
  steps:
18
- - uses: actions/checkout@v4
19
- - uses: crate-ci/typos@v1.35.3
18
+ - uses: actions/checkout@v5
19
+ - uses: crate-ci/typos@v1.35.4
@@ -15,5 +15,5 @@ jobs:
15
15
  timeout-minutes: 15
16
16
  runs-on: ubuntu-24.04
17
17
  steps:
18
- - uses: actions/checkout@v4
18
+ - uses: actions/checkout@v5
19
19
  - uses: g4s8/xcop-action@master
@@ -15,5 +15,5 @@ jobs:
15
15
  timeout-minutes: 15
16
16
  runs-on: ubuntu-24.04
17
17
  steps:
18
- - uses: actions/checkout@v4
18
+ - uses: actions/checkout@v5
19
19
  - uses: ibiqlik/action-yamllint@v3
data/README.md CHANGED
@@ -13,19 +13,11 @@
13
13
  [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fyegor256%2Ffactbase.svg?type=shield&issueType=license)](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 a map of properties and values.
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 disc and then load it back:
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 deducation of `∑v2` from `∑v1`
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 p)` returns true if the value of `p` property hasn't been seen yet
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,
@@ -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
data/lib/factbase/inv.rb CHANGED
@@ -82,6 +82,10 @@ class Factbase::Inv
82
82
  @fb = fb
83
83
  end
84
84
 
85
+ def to_s
86
+ @query.to_s
87
+ end
88
+
85
89
  def each(fb = @fb, params = {})
86
90
  return to_enum(__method__, fb, params) unless block_given?
87
91
  @query.each(fb, params) do |f|
@@ -96,6 +96,10 @@ class Factbase::Rules
96
96
  @fb = fb
97
97
  end
98
98
 
99
+ def to_s
100
+ @query.to_s
101
+ end
102
+
99
103
  def each(fb = @fb, params = {})
100
104
  return to_enum(__method__, fb, params) unless block_given?
101
105
  @query.each(fb, params) do |f|
@@ -20,6 +20,11 @@ class Factbase::SyncQuery
20
20
  @fb = fb
21
21
  end
22
22
 
23
+ # Turn it to a string.
24
+ def to_s
25
+ @origin.to_s
26
+ end
27
+
23
28
  # Iterate facts one by one.
24
29
  # @param [Hash] params Optional params accessible in the query via the "$" symbol
25
30
  # @yield [Fact] Facts one-by-one
@@ -75,6 +75,10 @@ class Factbase::Tallied
75
75
  @fb = fb
76
76
  end
77
77
 
78
+ def to_s
79
+ @query.to_s
80
+ end
81
+
78
82
  def one(fb = @fb, params = {})
79
83
  @query.one(fb, params)
80
84
  end
@@ -20,15 +20,24 @@ module Factbase::Ordering
20
20
  end
21
21
 
22
22
  def unique(fact, maps, fb)
23
- @uniques = [] if @uniques.nil?
24
- assert_args(1)
25
- vv = _values(0, fact, maps, fb)
26
- return false if vv.nil?
27
- vv = [vv] unless vv.respond_to?(:to_a)
28
- vv.each do |v|
29
- return false if @uniques.include?(v)
30
- @uniques << v
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
@@ -9,5 +9,5 @@
9
9
  # License:: MIT
10
10
  class Factbase
11
11
  # Current version of the gem (changed by .rultor.yml on every release)
12
- VERSION = '0.14.6' unless const_defined?(:VERSION)
12
+ VERSION = '0.15.0' unless const_defined?(:VERSION)
13
13
  end
@@ -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
@@ -265,6 +265,12 @@ class TestQuery < Factbase::Test
265
265
  end
266
266
  end
267
267
 
268
+ def test_turns_query_to_string
269
+ with_factbases do |badge, fb|
270
+ assert_equal('(always)', fb.query('(always)').to_s, "Fails with #{badge}")
271
+ end
272
+ end
273
+
268
274
  def test_to_array
269
275
  maps = []
270
276
  maps << { 'foo' => [42] }
@@ -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.14.6
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.7
315
+ rubygems_version: 3.6.9
316
316
  specification_version: 4
317
317
  summary: Factbase
318
318
  test_files: []