factbase 0.0.39 → 0.0.40

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: 8ff1db55d5a25e5036d3a98423caedc960f8323681e2227c82e0a397727067c0
4
- data.tar.gz: b3000b708bd2eb69005998a722011a296384d5cea66641b975471e611b93e4d0
3
+ metadata.gz: 129d17fba6d19bc5131f853d6b90c2e963187e84626270be4f7766e926d4c2c9
4
+ data.tar.gz: 4196611d541b81bdc1a73bf31f4b1fe15077ad702d8d1ec558f0f61d5e530396
5
5
  SHA512:
6
- metadata.gz: a377a359a877b1f50c83f13c451e095036b4087cf88e39c04a0e5d64e86337bfef458883646009daff4b2b90446abbdbd563a4921b3ed275b88b7408b69ef0de
7
- data.tar.gz: 26a5ca088df353e0c526080213aa4d87b23805dd522617d52d2d738b1881c40ae4c659fa886db4fbe19a30a131ff6b5a7e19aadf5dbafcbd0f6b81954386bd65
6
+ metadata.gz: 1d87ddb11c2252043c963c6e086a617f216bc15a79441cfa121a0e3de3157f31705cfd1bfea7b6b89996488c76805ee27152f4be00627c7d3f3e1d506636c303
7
+ data.tar.gz: 5e913f0d675f19fc03cc83c80301af5fd390de8cdaf730a0ea2612768ae0056286be7f5f42686616ee0b1fc69cf22adc0009e9df5315251971e9c948da5ee739
data/README.md CHANGED
@@ -92,6 +92,7 @@ One term is for meta-programming:
92
92
  There are terms that are history of search aware:
93
93
 
94
94
  * `(prev a)` returns the value of `a` in the previously seen fact
95
+ * `(unique k)` returns true if the value of `k` property hasn't been seen yet
95
96
 
96
97
  There are also terms that match the entire factbase
97
98
  and must be used inside the `(agg ..)` term:
data/lib/factbase/fact.rb CHANGED
@@ -24,17 +24,20 @@ require 'json'
24
24
  require 'time'
25
25
  require_relative '../factbase'
26
26
 
27
- # Fact.
27
+ # A single fact in a factbase.
28
28
  #
29
- # This is an internal class, it is not supposed to be instantiated directly.
30
- #
31
- # It is possible to use for testing directly, for example to make a
29
+ # This is an internal class, it is not supposed to be instantiated directly,
30
+ # by the +Factbase+ class.
31
+ # However, it is possible to use it for testing directly, for example to make a
32
32
  # fact with a single key/value pair inside:
33
33
  #
34
34
  # require 'factbase/fact'
35
35
  # f = Factbase::Fact.new(Mutex.new, { 'foo' => [42, 256, 'Hello, world!'] })
36
36
  # assert_equal(42, f.foo)
37
37
  #
38
+ # A fact is basically a key/value hash map, where values are non-empty
39
+ # sets of values.
40
+ #
38
41
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
39
42
  # Copyright:: Copyright (c) 2024 Yegor Bugayenko
40
43
  # License:: MIT
@@ -20,6 +20,7 @@
20
20
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  # SOFTWARE.
22
22
 
23
+ require 'time'
23
24
  require 'loog'
24
25
 
25
26
  # A decorator of a Factbase, that logs all operations.
@@ -114,39 +115,51 @@ class Factbase::Looged
114
115
  def each(&)
115
116
  q = Factbase::Syntax.new(@expr).to_term.to_s
116
117
  if block_given?
117
- r = @query.each(&)
118
+ r = nil
119
+ tail = Factbase::Looged.elapsed do
120
+ r = @query.each(&)
121
+ end
118
122
  raise ".each of #{@query.class} returned #{r.class}" unless r.is_a?(Integer)
119
123
  if r.zero?
120
- @loog.debug("Nothing found by '#{q}'")
124
+ @loog.debug("Nothing found by '#{q}' #{tail}")
121
125
  else
122
- @loog.debug("Found #{r} fact(s) by '#{q}'")
126
+ @loog.debug("Found #{r} fact(s) by '#{q}' #{tail}")
123
127
  end
124
128
  r
125
129
  else
126
130
  array = []
127
- # rubocop:disable Style/MapIntoArray
128
- @query.each do |f|
129
- array << f
131
+ tail = Factbase::Looged.elapsed do
132
+ @query.each do |f|
133
+ array << f
134
+ end
130
135
  end
131
- # rubocop:enable Style/MapIntoArray
132
136
  if array.empty?
133
- @loog.debug("Nothing found by '#{q}'")
137
+ @loog.debug("Nothing found by '#{q}' #{tail}")
134
138
  else
135
- @loog.debug("Found #{array.size} fact(s) by '#{q}'")
139
+ @loog.debug("Found #{array.size} fact(s) by '#{q}' #{tail}")
136
140
  end
137
141
  array
138
142
  end
139
143
  end
140
144
 
141
145
  def delete!
142
- r = @query.delete!
146
+ r = nil
147
+ tail = Factbase::Looged.elapsed do
148
+ r = @query.delete!
149
+ end
143
150
  raise ".delete! of #{@query.class} returned #{r.class}" unless r.is_a?(Integer)
144
151
  if r.zero?
145
- @loog.debug("Nothing deleted by '#{@expr}'")
152
+ @loog.debug("Nothing deleted by '#{@expr}' #{tail}")
146
153
  else
147
- @loog.debug("Deleted #{r} fact(s) by '#{@expr}'")
154
+ @loog.debug("Deleted #{r} fact(s) by '#{@expr}' #{tail}")
148
155
  end
149
156
  r
150
157
  end
151
158
  end
159
+
160
+ def self.elapsed
161
+ start = Time.now
162
+ yield
163
+ "in #{format('%.2f', (Time.now - start) * 1000)}ms"
164
+ end
152
165
  end
data/lib/factbase/term.rb CHANGED
@@ -36,6 +36,15 @@ require_relative 'fact'
36
36
  # t = Factbase::Term.new(:lt, [:foo, 50])
37
37
  # assert(t.evaluate(f))
38
38
  #
39
+ # The design of this class may look ugly, since it has a large number of
40
+ # methods, each of which corresponds to a different type of a +Term+. A much
41
+ # better design would definitely involve many classes, one per each type
42
+ # of a term. It's not done this way because of an experimental nature of
43
+ # the project. Most probably we should keep current design intact, since it
44
+ # works well and is rather simple to extend (by adding new term types).
45
+ # Moreover, it looks like the number of possible term types is rather limited
46
+ # and currently we implement most of them.
47
+ #
39
48
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
40
49
  # Copyright:: Copyright (c) 2024 Yegor Bugayenko
41
50
  # License:: MIT
@@ -184,6 +193,19 @@ class Factbase::Term
184
193
  before
185
194
  end
186
195
 
196
+ def unique(fact, _maps)
197
+ @uniques = [] if @uniques.nil?
198
+ assert_args(1)
199
+ vv = by_symbol(0, fact)
200
+ return false if vv.nil?
201
+ vv = [vv] unless vv.is_a?(Array)
202
+ vv.each do |v|
203
+ return false if @uniques.include?(v)
204
+ @uniques << v
205
+ end
206
+ true
207
+ end
208
+
187
209
  def many(fact, maps)
188
210
  assert_args(1)
189
211
  v = the_values(0, fact, maps)
data/lib/factbase.rb CHANGED
@@ -79,7 +79,7 @@ require 'yaml'
79
79
  # License:: MIT
80
80
  class Factbase
81
81
  # Current version of the gem (changed by .rultor.yml on every release)
82
- VERSION = '0.0.39'
82
+ VERSION = '0.0.40'
83
83
 
84
84
  # An exception that may be thrown in a transaction, to roll it back.
85
85
  class Rollback < StandardError; end
@@ -94,7 +94,7 @@ class TestLooged < Minitest::Test
94
94
  'Found 1 fact(s) by \'(exists bar)\'',
95
95
  'Deleted 3 fact(s) by \'(not (exists bar))\''
96
96
  ].each do |s|
97
- assert(log.to_s.include?("#{s}\n"), "#{log}\n")
97
+ assert(log.to_s.include?(s), "#{log}\n")
98
98
  end
99
99
  end
100
100
  end
@@ -57,6 +57,9 @@ class TestQuery < Minitest::Test
57
57
  '(not (exists hello))' => 3,
58
58
  '(eq "Integer" (type num))' => 2,
59
59
  '(when (eq num 0) (exists time))' => 2,
60
+ '(unique num)' => 2,
61
+ '(unique name)' => 2,
62
+ '(unique pi)' => 1,
60
63
  '(many num)' => 1,
61
64
  '(one num)' => 2,
62
65
  '(gt num (minus 1 (either (at 0 (prev num)) 0)))' => 3,
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.0.39
4
+ version: 0.0.40
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko