factbase 0.19.11 → 0.19.12

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.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +5 -4
  3. data/Gemfile.lock +16 -12
  4. data/README.md +49 -18
  5. data/Rakefile +2 -7
  6. data/factbase.gemspec +11 -11
  7. data/lib/factbase/accum.rb +1 -1
  8. data/lib/factbase/cached/cached_fact.rb +1 -2
  9. data/lib/factbase/cached/cached_factbase.rb +3 -3
  10. data/lib/factbase/cached/cached_query.rb +4 -6
  11. data/lib/factbase/cached/cached_term.rb +1 -2
  12. data/lib/factbase/churn.rb +4 -8
  13. data/lib/factbase/fact.rb +12 -9
  14. data/lib/factbase/flatten.rb +2 -2
  15. data/lib/factbase/impatient.rb +14 -13
  16. data/lib/factbase/indexed/indexed_and.rb +14 -20
  17. data/lib/factbase/indexed/indexed_eq.rb +5 -1
  18. data/lib/factbase/indexed/indexed_fact.rb +1 -4
  19. data/lib/factbase/indexed/indexed_factbase.rb +4 -4
  20. data/lib/factbase/indexed/indexed_gt.rb +3 -1
  21. data/lib/factbase/indexed/indexed_gte.rb +51 -0
  22. data/lib/factbase/indexed/indexed_lt.rb +3 -1
  23. data/lib/factbase/indexed/indexed_lte.rb +51 -0
  24. data/lib/factbase/indexed/indexed_not.rb +1 -1
  25. data/lib/factbase/indexed/indexed_or.rb +2 -2
  26. data/lib/factbase/indexed/indexed_query.rb +6 -7
  27. data/lib/factbase/indexed/indexed_term.rb +10 -6
  28. data/lib/factbase/indexed/indexed_unique.rb +4 -2
  29. data/lib/factbase/inv.rb +3 -3
  30. data/lib/factbase/lazy_taped.rb +10 -13
  31. data/lib/factbase/lazy_taped_hash.rb +2 -1
  32. data/lib/factbase/light.rb +1 -1
  33. data/lib/factbase/logged.rb +37 -34
  34. data/lib/factbase/pre.rb +3 -3
  35. data/lib/factbase/query.rb +4 -5
  36. data/lib/factbase/rules.rb +8 -8
  37. data/lib/factbase/sync/sync_factbase.rb +2 -2
  38. data/lib/factbase/syntax.rb +18 -19
  39. data/lib/factbase/tallied.rb +6 -7
  40. data/lib/factbase/taped.rb +5 -11
  41. data/lib/factbase/tee.rb +2 -2
  42. data/lib/factbase/term.rb +53 -60
  43. data/lib/factbase/terms/agg.rb +3 -4
  44. data/lib/factbase/terms/arithmetic.rb +7 -7
  45. data/lib/factbase/terms/as.rb +2 -2
  46. data/lib/factbase/terms/assert.rb +5 -13
  47. data/lib/factbase/terms/base.rb +6 -7
  48. data/lib/factbase/terms/best.rb +1 -1
  49. data/lib/factbase/terms/boolean.rb +1 -1
  50. data/lib/factbase/terms/compare.rb +2 -1
  51. data/lib/factbase/terms/defn.rb +8 -6
  52. data/lib/factbase/terms/empty.rb +1 -1
  53. data/lib/factbase/terms/first.rb +2 -2
  54. data/lib/factbase/terms/head.rb +3 -3
  55. data/lib/factbase/terms/inverted.rb +2 -2
  56. data/lib/factbase/terms/join.rb +8 -7
  57. data/lib/factbase/terms/matches.rb +4 -4
  58. data/lib/factbase/terms/max.rb +1 -1
  59. data/lib/factbase/terms/min.rb +1 -1
  60. data/lib/factbase/terms/nth.rb +3 -3
  61. data/lib/factbase/terms/plus.rb +1 -1
  62. data/lib/factbase/terms/prev.rb +3 -6
  63. data/lib/factbase/terms/sorted.rb +2 -2
  64. data/lib/factbase/terms/sprintf.rb +5 -4
  65. data/lib/factbase/terms/sum.rb +1 -1
  66. data/lib/factbase/terms/to_float.rb +2 -2
  67. data/lib/factbase/terms/to_integer.rb +2 -2
  68. data/lib/factbase/terms/to_string.rb +1 -1
  69. data/lib/factbase/terms/to_time.rb +2 -2
  70. data/lib/factbase/terms/traced.rb +2 -2
  71. data/lib/factbase/terms/undef.rb +2 -2
  72. data/lib/factbase/terms/unique.rb +3 -7
  73. data/lib/factbase/to_json.rb +1 -1
  74. data/lib/factbase/to_xml.rb +5 -9
  75. data/lib/factbase/to_yaml.rb +1 -1
  76. data/lib/factbase/version.rb +1 -2
  77. data/lib/factbase.rb +27 -10
  78. data/lib/fuzz.rb +3 -3
  79. metadata +3 -1
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: Copyright (c) 2024-2026 Yegor Bugayenko
4
+ # SPDX-License-Identifier: MIT
5
+
6
+ # Indexed term 'gte'.
7
+ class Factbase::IndexedGte
8
+ def initialize(term, idx)
9
+ @term = term
10
+ @idx = idx
11
+ end
12
+
13
+ def predict(maps, _fb, params)
14
+ op1, op2 = @term.operands
15
+ return unless op1.is_a?(Symbol) && _scalar?(op2)
16
+ prop = op1.to_s
17
+ target = op2.is_a?(Symbol) ? params[op2.to_s]&.first : op2
18
+ return maps || [] if target.nil?
19
+ key = [maps.object_id, prop, :facts]
20
+ @idx[key] ||= { facts: [], count: 0 }
21
+ entry = @idx[key]
22
+ _feed(maps.to_a, entry, prop)
23
+ matched = _search(entry, target)
24
+ maps.respond_to?(:repack) ? maps.repack(matched) : matched
25
+ end
26
+
27
+ private
28
+
29
+ def _scalar?(item)
30
+ item.is_a?(String) || item.is_a?(Time) || item.is_a?(Integer) || item.is_a?(Float) || item.is_a?(Symbol)
31
+ end
32
+
33
+ def _feed(facts, entry, prop)
34
+ return unless entry[:count] < facts.size
35
+ facts[entry[:count]..].each do |fact|
36
+ fact[prop]&.each do |v|
37
+ entry[:facts] << [v, fact]
38
+ end
39
+ end
40
+ entry[:facts].sort_by! { |pair| pair[0] }
41
+ entry[:count] = facts.size
42
+ end
43
+
44
+ def _search(entry, target)
45
+ idx = entry[:facts].bsearch_index { |v, _| v >= target }
46
+ return [] if idx.nil?
47
+ facts = entry[:facts][idx..].map { |_, f| f }
48
+ facts.uniq!(&:object_id)
49
+ facts
50
+ end
51
+ end
@@ -44,6 +44,8 @@ class Factbase::IndexedLt
44
44
  def _search(entry, target)
45
45
  idx = entry[:facts].bsearch_index { |v, _| v >= target }
46
46
  res = idx.nil? ? entry[:facts] : entry[:facts][0...idx]
47
- res.map { |_, f| f }.uniq(&:object_id)
47
+ facts = res.map { |_, f| f }
48
+ facts.uniq!(&:object_id)
49
+ facts
48
50
  end
49
51
  end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: Copyright (c) 2024-2026 Yegor Bugayenko
4
+ # SPDX-License-Identifier: MIT
5
+
6
+ # Indexed term 'lte'.
7
+ class Factbase::IndexedLte
8
+ def initialize(term, idx)
9
+ @term = term
10
+ @idx = idx
11
+ end
12
+
13
+ def predict(maps, _fb, params)
14
+ op1, op2 = @term.operands
15
+ return unless op1.is_a?(Symbol) && _scalar?(op2)
16
+ prop = op1.to_s
17
+ target = op2.is_a?(Symbol) ? params[op2.to_s]&.first : op2
18
+ return maps || [] if target.nil?
19
+ key = [maps.object_id, prop, :facts]
20
+ @idx[key] ||= { facts: [], count: 0 }
21
+ entry = @idx[key]
22
+ _feed(maps.to_a, entry, prop)
23
+ matched = _search(entry, target)
24
+ maps.respond_to?(:repack) ? maps.repack(matched) : matched
25
+ end
26
+
27
+ private
28
+
29
+ def _scalar?(item)
30
+ item.is_a?(String) || item.is_a?(Time) || item.is_a?(Integer) || item.is_a?(Float) || item.is_a?(Symbol)
31
+ end
32
+
33
+ def _feed(facts, entry, prop)
34
+ return unless entry[:count] < facts.size
35
+ facts[entry[:count]..].each do |fact|
36
+ fact[prop]&.each do |v|
37
+ entry[:facts] << [v, fact]
38
+ end
39
+ end
40
+ entry[:facts].sort_by! { |pair| pair[0] }
41
+ entry[:count] = facts.size
42
+ end
43
+
44
+ def _search(entry, target)
45
+ idx = entry[:facts].bsearch_index { |v, _| v > target }
46
+ res = idx.nil? ? entry[:facts] : entry[:facts][0...idx]
47
+ facts = res.map { |_, f| f }
48
+ facts.uniq!(&:object_id)
49
+ facts
50
+ end
51
+ end
@@ -18,7 +18,7 @@ class Factbase::IndexedNot
18
18
  _feed(maps.to_a, entry) do
19
19
  sub.predict(maps, fb, params)
20
20
  end
21
- return nil if entry[:facts].nil?
21
+ return if entry[:facts].nil?
22
22
  maps.respond_to?(:repack) ? maps.repack(entry[:facts]) : entry[:facts]
23
23
  end
24
24
 
@@ -11,7 +11,7 @@ class Factbase::IndexedOr
11
11
  end
12
12
 
13
13
  def predict(maps, fb, params)
14
- return nil if @idx.nil?
14
+ return if @idx.nil?
15
15
  r = nil
16
16
  @term.operands.each do |o|
17
17
  n = o.predict(maps, fb, params)
@@ -21,7 +21,7 @@ class Factbase::IndexedOr
21
21
  end
22
22
  r = maps & [] if r.nil?
23
23
  r |= n.to_a
24
- return maps if r.size > maps.size / 4 # it's big enough already
24
+ return maps if r.size > maps.size / 4
25
25
  end
26
26
  r
27
27
  end
@@ -37,11 +37,12 @@ class Factbase::IndexedQuery
37
37
  # @return [Integer] Total number of facts yielded
38
38
  def each(fb = @fb, params = {})
39
39
  return to_enum(__method__, fb, params) unless block_given?
40
- a = @origin.each(fb, params).to_a
41
- a.each do |f|
42
- yield Factbase::IndexedFact.new(f, @idx, @fresh)
40
+ n = 0
41
+ @origin.each(fb, params) do |f|
42
+ yield(Factbase::IndexedFact.new(f, @idx, @fresh))
43
+ n += 1
43
44
  end
44
- a.size
45
+ n
45
46
  end
46
47
 
47
48
  # Read a single value.
@@ -56,8 +57,6 @@ class Factbase::IndexedQuery
56
57
  # @param [Factbase] fb The factbase
57
58
  # @return [Integer] Total number of facts deleted
58
59
  def delete!(fb = @fb)
59
- result = @origin.delete!(fb)
60
- @idx.clear
61
- result
60
+ @origin.delete!(fb).tap { @idx.clear }
62
61
  end
63
62
  end
@@ -5,15 +5,17 @@
5
5
 
6
6
  require 'tago'
7
7
  require_relative '../../factbase'
8
+ require_relative '../indexed/indexed_absent'
9
+ require_relative '../indexed/indexed_and'
8
10
  require_relative '../indexed/indexed_eq'
9
- require_relative '../indexed/indexed_lt'
11
+ require_relative '../indexed/indexed_exists'
10
12
  require_relative '../indexed/indexed_gt'
11
- require_relative '../indexed/indexed_one'
13
+ require_relative '../indexed/indexed_gte'
14
+ require_relative '../indexed/indexed_lt'
15
+ require_relative '../indexed/indexed_lte'
12
16
  require_relative '../indexed/indexed_not'
13
- require_relative '../indexed/indexed_exists'
14
- require_relative '../indexed/indexed_and'
17
+ require_relative '../indexed/indexed_one'
15
18
  require_relative '../indexed/indexed_or'
16
- require_relative '../indexed/indexed_absent'
17
19
  require_relative '../indexed/indexed_unique'
18
20
 
19
21
  # Term with an index.
@@ -35,7 +37,7 @@ module Factbase::IndexedTerm
35
37
  return t.predict(maps, fb, params) if t.respond_to?(:predict)
36
38
  end
37
39
  m = :"#{@op}_predict"
38
- return send(m, maps, fb, params) if respond_to?(m)
40
+ return __send__(m, maps, fb, params) if respond_to?(m)
39
41
  _init_indexes unless @indexes
40
42
  @indexes[@op].predict(maps, fb, params) if @indexes.key?(@op)
41
43
  end
@@ -46,7 +48,9 @@ module Factbase::IndexedTerm
46
48
  @indexes = {
47
49
  eq: Factbase::IndexedEq.new(self, @idx),
48
50
  lt: Factbase::IndexedLt.new(self, @idx),
51
+ lte: Factbase::IndexedLte.new(self, @idx),
49
52
  gt: Factbase::IndexedGt.new(self, @idx),
53
+ gte: Factbase::IndexedGte.new(self, @idx),
50
54
  one: Factbase::IndexedOne.new(self, @idx),
51
55
  exists: Factbase::IndexedExists.new(self, @idx),
52
56
  absent: Factbase::IndexedAbsent.new(self, @idx),
@@ -10,7 +10,9 @@ class Factbase::IndexedUnique
10
10
  @idx = idx
11
11
  end
12
12
 
13
- def predict(maps, _fb, _params)
14
- maps
13
+ # rubocop:disable Elegant/NoNilReturn
14
+ def predict(_maps, _fb, _params)
15
+ nil
15
16
  end
17
+ # rubocop:enable Elegant/NoNilReturn
16
18
  end
data/lib/factbase/inv.rb CHANGED
@@ -3,8 +3,8 @@
3
3
  # SPDX-FileCopyrightText: Copyright (c) 2024-2026 Yegor Bugayenko
4
4
  # SPDX-License-Identifier: MIT
5
5
 
6
- require 'others'
7
6
  require 'decoor'
7
+ require 'others'
8
8
  require_relative '../factbase'
9
9
 
10
10
  # A decorator of a Factbase, that checks invariants on every set.
@@ -40,7 +40,7 @@ class Factbase::Inv
40
40
 
41
41
  def txn
42
42
  @fb.txn do |fbt|
43
- yield Factbase::Inv.new(fbt, &@block)
43
+ yield(Factbase::Inv.new(fbt, &@block))
44
44
  end
45
45
  end
46
46
 
@@ -89,7 +89,7 @@ class Factbase::Inv
89
89
  def each(fb = @fb, params = {})
90
90
  return to_enum(__method__, fb, params) unless block_given?
91
91
  @query.each(fb, params) do |f|
92
- yield Fact.new(f, @block)
92
+ yield(Fact.new(f, @block))
93
93
  end
94
94
  end
95
95
  end
@@ -4,8 +4,8 @@
4
4
  # SPDX-License-Identifier: MIT
5
5
 
6
6
  require_relative '../factbase'
7
- require_relative 'taped'
8
7
  require_relative 'lazy_taped_hash'
8
+ require_relative 'taped'
9
9
 
10
10
  # A lazy decorator of an Array with HashMaps that defers copying until modification.
11
11
  class Factbase::LazyTaped
@@ -22,7 +22,7 @@ class Factbase::LazyTaped
22
22
  # Returns the original map this copy was derived from.
23
23
  # Returns nil if the base hasn't been copied yet or if the fact is new.
24
24
  def source_of(copy)
25
- return nil unless @copied
25
+ return unless @copied
26
26
  @copies.key(copy)
27
27
  end
28
28
 
@@ -76,21 +76,21 @@ class Factbase::LazyTaped
76
76
  is_copied = copied?
77
77
  unless is_copied
78
78
  @origin.each do |m|
79
- yield _tape(m)
79
+ yield(_tape(m))
80
80
  yielded_size += 1
81
81
  end
82
82
  end
83
83
  staged = is_copied == copied? ? @staged : @staged[yielded_size..]
84
84
  staged&.each do |f|
85
85
  next if f.nil?
86
- yield _tape(f)
86
+ yield(_tape(f))
87
87
  end
88
88
  end
89
89
 
90
90
  def delete_if
91
91
  ensure_copied!
92
92
  @staged.delete_if do |m|
93
- r = yield m
93
+ r = yield(m)
94
94
  @deleted.append(source_of(m).object_id) if r
95
95
  r
96
96
  end
@@ -102,8 +102,7 @@ class Factbase::LazyTaped
102
102
 
103
103
  def repack(other)
104
104
  ensure_copied!
105
- copied = other.map { |o| @copies[o] || o }
106
- Factbase::Taped.new(copied, inserted: @inserted, deleted: @deleted, added: @added)
105
+ Factbase::Taped.new(other.map { |o| @copies[o] || o }, inserted: @inserted, deleted: @deleted, added: @added)
107
106
  end
108
107
 
109
108
  def &(other)
@@ -135,10 +134,9 @@ class Factbase::LazyTaped
135
134
 
136
135
  def _join(other)
137
136
  ensure_copied!
138
- n = yield to_a, other.to_a
139
- raise 'Cannot join with another Taped' if other.respond_to?(:inserted)
140
- raise 'Can only join with array' unless other.is_a?(Array)
141
- Factbase::Taped.new(n, inserted: @inserted, deleted: @deleted, added: @added)
137
+ raise(ArgumentError, 'Cannot join with another Taped') if other.respond_to?(:inserted)
138
+ raise(ArgumentError, 'Can only join with array') unless other.is_a?(Array)
139
+ Factbase::Taped.new(yield(to_a, other.to_a), inserted: @inserted, deleted: @deleted, added: @added)
142
140
  end
143
141
 
144
142
  def _track(copy, original)
@@ -147,7 +145,6 @@ class Factbase::LazyTaped
147
145
 
148
146
  def _tape(map)
149
147
  return LazyTapedHash.new(map, self, @added) unless copied?
150
- copy = @copies[map] || map
151
- Factbase::Taped::TapedHash.new(copy, @added)
148
+ Factbase::Taped::TapedHash.new(@copies[map] || map, @added)
152
149
  end
153
150
  end
@@ -65,7 +65,8 @@ class Factbase::LazyTaped
65
65
  end
66
66
 
67
67
  def method_missing(method, *, &)
68
- current_map.send(method, *, &)
68
+ ensure_copied_map if method.to_s.end_with?('=', '!')
69
+ current_map.__send__(method, *, &)
69
70
  end
70
71
 
71
72
  def respond_to_missing?(method, include_private = false)
@@ -32,6 +32,6 @@ class Factbase::Light
32
32
  end
33
33
 
34
34
  def txn
35
- raise 'You cannot start a transaction inside another transaction'
35
+ raise(StandardError, 'You cannot start a transaction inside another transaction')
36
36
  end
37
37
  end
@@ -5,8 +5,8 @@
5
5
 
6
6
  require 'decoor'
7
7
  require 'others'
8
- require 'time'
9
8
  require 'tago'
9
+ require 'time'
10
10
  require_relative 'syntax'
11
11
 
12
12
  # A decorator of a Factbase, that logs all operations.
@@ -15,16 +15,18 @@ require_relative 'syntax'
15
15
  # Copyright:: Copyright (c) 2024-2026 Yegor Bugayenko
16
16
  # License:: MIT
17
17
  class Factbase::Logged
18
+ MONO = Process::CLOCK_MONOTONIC
19
+
18
20
  # Ctor.
19
21
  # @param [Factbase] fb The factbase to decorate
20
22
  # @param [Object] log The logging facility
21
23
  # @param [Integer] time_tolerate How many seconds are OK per request
22
24
  # @param [Print] tube The tube to use, if log is NIL
23
25
  def initialize(fb, log = nil, time_tolerate: 1, tube: nil)
24
- raise 'The "fb" is nil' if fb.nil?
26
+ raise(ArgumentError, 'The "fb" is nil') if fb.nil?
25
27
  @origin = fb
26
28
  if log.nil?
27
- raise 'Either "log" or "tube" must be non-NIL' if tube.nil?
29
+ raise(ArgumentError, 'Either "log" or "tube" must be non-NIL') if tube.nil?
28
30
  @tube = tube
29
31
  else
30
32
  @tube = Tube.new(log, time_tolerate:)
@@ -34,10 +36,8 @@ class Factbase::Logged
34
36
  decoor(:origin)
35
37
 
36
38
  def insert
37
- start = Time.now
38
- f = @origin.insert
39
- @tube.say(start, "Inserted new fact ##{@origin.size} in #{start.ago}")
40
- Fact.new(f, tube: @tube)
39
+ @tube.say(Process.clock_gettime(MONO), "Inserted new fact ##{@origin.size} in #{Time.now.ago}")
40
+ Fact.new(@origin.insert, tube: @tube)
41
41
  end
42
42
 
43
43
  def query(term, maps = nil)
@@ -46,21 +46,21 @@ class Factbase::Logged
46
46
  end
47
47
 
48
48
  def txn
49
- start = Time.now
49
+ mono = Process.clock_gettime(MONO)
50
50
  id = nil
51
51
  rollback = false
52
52
  r =
53
53
  @origin.txn do |fbt|
54
54
  id = fbt.object_id
55
- yield Factbase::Logged.new(fbt, tube: @tube)
55
+ yield(Factbase::Logged.new(fbt, tube: @tube))
56
56
  rescue Factbase::Rollback => e
57
57
  rollback = true
58
- raise e
58
+ raise(e)
59
59
  end
60
60
  if rollback
61
- @tube.say(start, "Txn ##{id} rolled back in #{start.ago}")
61
+ @tube.say(mono, "Txn ##{id} rolled back in #{Time.now.ago}")
62
62
  else
63
- @tube.say(start, "Txn ##{id} touched #{r} in #{start.ago}")
63
+ @tube.say(mono, "Txn ##{id} touched #{r} in #{Time.now.ago}")
64
64
  end
65
65
  r
66
66
  end
@@ -72,13 +72,13 @@ class Factbase::Logged
72
72
  @time_tolerate = time_tolerate
73
73
  end
74
74
 
75
- def say(start, msg)
75
+ def say(start_mono, msg)
76
76
  m = :debug
77
- if Time.now - start > @time_tolerate
77
+ if Process.clock_gettime(Factbase::Logged::MONO) - start_mono > @time_tolerate
78
78
  msg = "#{msg} (slow!)"
79
79
  m = :warn
80
80
  end
81
- @log.send(m, msg)
81
+ @log.__send__(m, msg)
82
82
  end
83
83
  end
84
84
 
@@ -108,14 +108,16 @@ class Factbase::Logged
108
108
  end
109
109
 
110
110
  others do |*args|
111
- start = Time.now
111
+ mono = Process.clock_gettime(Factbase::Logged::MONO) if args[0].to_s.end_with?('=')
112
112
  r = @fact.method_missing(*args)
113
113
  k = args[0].to_s
114
114
  v = args[1]
115
- s = v.is_a?(Time) ? v.utc.iso8601 : v.to_s
116
- s = v.to_s.inspect if v.is_a?(String)
117
- s = "#{s[0..(MAX_LENGTH / 2)]}...#{s[(-MAX_LENGTH / 2)..]}" if s.length > MAX_LENGTH
118
- @tube.say(start, "Set '#{k[0..-2]}' to #{s} (#{v.class})") if k.end_with?('=')
115
+ if k.end_with?('=')
116
+ s = v.is_a?(Time) ? v.utc.iso8601 : v.to_s
117
+ s = v.to_s.inspect if v.is_a?(String)
118
+ s = "#{s[0..(MAX_LENGTH / 2)]}...#{s[(-MAX_LENGTH / 2)..]}" if s.length > MAX_LENGTH
119
+ @tube.say(mono, "Set '#{k[0..-2]}' to #{s} (#{v.class})")
120
+ end
119
121
  r
120
122
  end
121
123
  end
@@ -137,26 +139,28 @@ class Factbase::Logged
137
139
 
138
140
  def each(fb = @fb, params = {}, &)
139
141
  return to_enum(__method__, fb, params) unless block_given?
140
- start = Time.now
142
+ mono = Process.clock_gettime(Factbase::Logged::MONO)
141
143
  r = nil
142
144
  qry = @fb.query(@term, @maps)
143
145
  tail =
144
146
  Factbase::Logged.elapsed do
145
147
  r = qry.each(fb, params, &)
146
148
  end
147
- raise ".query(#{@term.to_s.inspect}).each() of #{qry.class} returned #{r.class}" unless r.is_a?(Integer)
149
+ unless r.is_a?(Integer)
150
+ raise(StandardError, ".query(#{@term.to_s.inspect}).each() of #{qry.class} returned #{r.class}")
151
+ end
148
152
  q = Factbase::Syntax.new(@term).to_term.to_s
149
153
  q = "#{q} with {#{params.map { |k, v| "#{k}=#{v}" }.join(', ')}}" if params.is_a?(Hash) && !params.empty?
150
154
  if r.zero?
151
- @tube.say(start, "Zero/#{@fb.size} facts found by #{q} #{tail}")
155
+ @tube.say(mono, "Zero/#{@fb.size} facts found by #{q} #{tail}")
152
156
  else
153
- @tube.say(start, "Found #{r}/#{@fb.size} fact(s) by #{q} #{tail}")
157
+ @tube.say(mono, "Found #{r}/#{@fb.size} fact(s) by #{q} #{tail}")
154
158
  end
155
159
  r
156
160
  end
157
161
 
158
162
  def one(fb = @fb, params = {})
159
- start = Time.now
163
+ mono = Process.clock_gettime(Factbase::Logged::MONO)
160
164
  q = Factbase::Syntax.new(@term).to_term.to_s
161
165
  r = nil
162
166
  tail =
@@ -164,36 +168,35 @@ class Factbase::Logged
164
168
  r = @fb.query(@term, @maps).one(fb, params)
165
169
  end
166
170
  if r.nil?
167
- @tube.say(start, "Nothing found by '#{q}' #{tail}")
171
+ @tube.say(mono, "Nothing found by '#{q}' #{tail}")
168
172
  else
169
- @tube.say(start, "Found #{r} (#{r.class}) by '#{q}' #{tail}")
173
+ @tube.say(mono, "Found #{r} (#{r.class}) by '#{q}' #{tail}")
170
174
  end
171
175
  r
172
176
  end
173
177
 
174
178
  def delete!(fb = @fb)
175
179
  r = nil
176
- start = Time.now
180
+ mono = Process.clock_gettime(Factbase::Logged::MONO)
177
181
  before = @fb.size
178
182
  tail =
179
183
  Factbase::Logged.elapsed do
180
184
  r = @fb.query(@term, @maps).delete!(fb)
181
185
  end
182
- raise ".delete! of #{@term.class} returned #{r.class}" unless r.is_a?(Integer)
186
+ raise(StandardError, ".delete! of #{@term.class} returned #{r.class}") unless r.is_a?(Integer)
183
187
  if before.zero?
184
- @tube.say(start, "There were no facts, nothing deleted by #{@term} #{tail}")
188
+ @tube.say(mono, "There were no facts, nothing deleted by #{@term} #{tail}")
185
189
  elsif r.zero?
186
- @tube.say(start, "No facts out of #{before} deleted by #{@term} #{tail}")
190
+ @tube.say(mono, "No facts out of #{before} deleted by #{@term} #{tail}")
187
191
  else
188
- @tube.say(start, "Deleted #{r} fact(s) out of #{before} by #{@term} #{tail}")
192
+ @tube.say(mono, "Deleted #{r} fact(s) out of #{before} by #{@term} #{tail}")
189
193
  end
190
194
  r
191
195
  end
192
196
  end
193
197
 
194
198
  def self.elapsed
195
- start = Time.now
196
199
  yield
197
- "in #{start.ago}"
200
+ "in #{Time.now.ago}"
198
201
  end
199
202
  end
data/lib/factbase/pre.rb CHANGED
@@ -3,8 +3,8 @@
3
3
  # SPDX-FileCopyrightText: Copyright (c) 2024-2026 Yegor Bugayenko
4
4
  # SPDX-License-Identifier: MIT
5
5
 
6
- require 'loog'
7
6
  require 'decoor'
7
+ require 'loog'
8
8
  require_relative '../factbase'
9
9
 
10
10
  # A decorator of a +Factbase+, that runs a provided block on every +insert+.
@@ -26,7 +26,7 @@ class Factbase::Pre
26
26
  decoor(:fb)
27
27
 
28
28
  def initialize(fb, &block)
29
- raise 'The "fb" is nil' if fb.nil?
29
+ raise(ArgumentError, 'The "fb" is nil') if fb.nil?
30
30
  @fb = fb
31
31
  @block = block
32
32
  end
@@ -39,7 +39,7 @@ class Factbase::Pre
39
39
 
40
40
  def txn
41
41
  @fb.txn do |fbt|
42
- yield Factbase::Pre.new(fbt, &@block)
42
+ yield(Factbase::Pre.new(fbt, &@block))
43
43
  end
44
44
  end
45
45
  end
@@ -52,13 +52,12 @@ class Factbase::Query
52
52
  extras = {}
53
53
  f = Factbase::Fact.new(m)
54
54
  f = Factbase::Tee.new(f, params)
55
- a = Factbase::Accum.new(f, extras, false)
56
- r = @term.evaluate(a, @maps, fb)
55
+ r = @term.evaluate(Factbase::Accum.new(f, extras, false), @maps, fb)
57
56
  unless r.is_a?(TrueClass) || r.is_a?(FalseClass)
58
- raise "Unexpected evaluation result of type #{r.class}, must be Boolean at #{@term.inspect}"
57
+ raise(ArgumentError, "Unexpected evaluation result of type #{r.class}, must be Boolean at #{@term.inspect}")
59
58
  end
60
59
  next unless r
61
- yield Factbase::Accum.new(f, extras, true)
60
+ yield(Factbase::Accum.new(f, extras, true))
62
61
  yielded += 1
63
62
  end
64
63
  yielded
@@ -72,7 +71,7 @@ class Factbase::Query
72
71
  params = params.transform_keys(&:to_s) if params.is_a?(Hash)
73
72
  r = @term.evaluate(Factbase::Tee.new(Factbase::Fact.new({}), params), @maps, fb)
74
73
  unless %w[String Integer Float Time Array NilClass].include?(r.class.to_s)
75
- raise "Incorrect type #{r.class} returned by #{@term.inspect}"
74
+ raise(StandardError, "Incorrect type #{r.class} returned by #{@term.inspect}")
76
75
  end
77
76
  r
78
77
  end
@@ -28,11 +28,11 @@ class Factbase::Rules
28
28
  decoor(:fb)
29
29
 
30
30
  def initialize(fb, rules, check = Check.new(rules), uid: nil)
31
- raise 'The "fb" is nil' if fb.nil?
31
+ raise(ArgumentError, 'The "fb" is nil') if fb.nil?
32
32
  @fb = fb
33
- raise 'The "rules" is nil' if rules.nil?
33
+ raise(ArgumentError, 'The "rules" is nil') if rules.nil?
34
34
  @rules = rules
35
- raise 'The "check" is nil' if check.nil?
35
+ raise(ArgumentError, 'The "check" is nil') if check.nil?
36
36
  @check = check
37
37
  @uid = uid
38
38
  end
@@ -51,7 +51,7 @@ class Factbase::Rules
51
51
  @check = later
52
52
  @fb.txn do |fbt|
53
53
  churn = Factbase::Churn.new
54
- yield Factbase::Tallied.new(Factbase::Rules.new(fbt, @rules, @check, uid: @uid), churn)
54
+ yield(Factbase::Tallied.new(Factbase::Rules.new(fbt, @rules, @check, uid: @uid), churn))
55
55
  @check = before
56
56
  unless churn.zero?
57
57
  fbt.query('(always)').each do |f|
@@ -107,7 +107,7 @@ class Factbase::Rules
107
107
  def each(fb = @fb, params = {})
108
108
  return to_enum(__method__, fb, params) unless block_given?
109
109
  @query.each(fb, params) do |f|
110
- yield Fact.new(f, @check, fb)
110
+ yield(Fact.new(f, @check, fb))
111
111
  end
112
112
  end
113
113
  end
@@ -123,7 +123,7 @@ class Factbase::Rules
123
123
  def it(fact, fb)
124
124
  return if Factbase::Syntax.new(@expr).to_term.evaluate(fact, [], fb)
125
125
  e = "#{@expr[0..32]}..." if @expr.length > 32
126
- raise "The fact doesn't match the #{e.inspect} rule: #{fact}"
126
+ raise(ArgumentError, "The fact doesn't match the #{e.inspect} rule: #{fact}")
127
127
  end
128
128
  end
129
129
 
@@ -140,7 +140,7 @@ class Factbase::Rules
140
140
  return if @uid.nil?
141
141
  a = fact[@uid]
142
142
  return if a.nil?
143
- raise "More than one #{@uid.inspect} in the fact: #{a}" if a.size > 1
143
+ raise(ArgumentError, "More than one #{@uid.inspect} in the fact: #{a}") if a.size > 1
144
144
  @facts << a.first
145
145
  end
146
146
 
@@ -148,7 +148,7 @@ class Factbase::Rules
148
148
  return true if @uid.nil?
149
149
  a = fact[@uid]
150
150
  return true if a.nil?
151
- raise "More than one #{@uid.inspect} in the fact: #{a}" if a.size > 1
151
+ raise(ArgumentError, "More than one #{@uid.inspect} in the fact: #{a}") if a.size > 1
152
152
  @facts.include?(a.first)
153
153
  end
154
154
  end
@@ -43,7 +43,7 @@ class Factbase::SyncFactbase
43
43
  # @param [Array<Hash>] maps Possible maps to use
44
44
  def query(term, maps = nil)
45
45
  term = to_term(term) if term.is_a?(String)
46
- require_relative 'sync_query'
46
+ require_relative('sync_query')
47
47
  Factbase::SyncQuery.new(@origin.query(term, maps), @monitor, self)
48
48
  end
49
49
 
@@ -53,7 +53,7 @@ class Factbase::SyncFactbase
53
53
  def txn
54
54
  try_lock do
55
55
  @origin.txn do |fbt|
56
- yield Factbase::SyncFactbase.new(fbt, @monitor)
56
+ yield(Factbase::SyncFactbase.new(fbt, @monitor))
57
57
  end
58
58
  end
59
59
  end