factbase 0.8.0 → 0.9.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.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/Gemfile +1 -1
  4. data/Gemfile.lock +3 -3
  5. data/README.md +24 -24
  6. data/REUSE.toml +7 -2
  7. data/Rakefile +8 -1
  8. data/benchmark/bench_factbase.rb +1 -1
  9. data/fixtures/stories/agg.yml +17 -0
  10. data/fixtures/stories/always.yml +16 -0
  11. data/fixtures/stories/as.yml +16 -0
  12. data/fixtures/stories/count.yml +18 -0
  13. data/fixtures/stories/eq.yml +30 -0
  14. data/fixtures/stories/gt.yml +18 -0
  15. data/fixtures/stories/join.yml +19 -0
  16. data/fixtures/stories/max.yml +14 -0
  17. data/fixtures/stories/min.yml +14 -0
  18. data/fixtures/stories/nth.yml +14 -0
  19. data/fixtures/stories/or.yml +18 -0
  20. data/fixtures/stories/sprintf.yml +12 -0
  21. data/fixtures/stories/sum.yml +14 -0
  22. data/lib/factbase/cached/cached_fact.rb +28 -0
  23. data/lib/factbase/cached/cached_factbase.rb +64 -0
  24. data/lib/factbase/cached/cached_query.rb +61 -0
  25. data/lib/factbase/cached/cached_term.rb +25 -0
  26. data/lib/factbase/fact.rb +13 -13
  27. data/lib/factbase/indexed/indexed_fact.rb +28 -0
  28. data/lib/factbase/indexed/indexed_factbase.rb +64 -0
  29. data/lib/factbase/indexed/indexed_query.rb +56 -0
  30. data/lib/factbase/indexed/indexed_term.rb +60 -0
  31. data/lib/factbase/light.rb +7 -6
  32. data/lib/factbase/logged.rb +67 -44
  33. data/lib/factbase/query.rb +29 -34
  34. data/lib/factbase/rules.rb +15 -14
  35. data/lib/factbase/sync/sync_factbase.rb +57 -0
  36. data/lib/factbase/sync/sync_query.rb +61 -0
  37. data/lib/factbase/syntax.rb +12 -25
  38. data/lib/factbase/tallied.rb +10 -9
  39. data/lib/factbase/taped.rb +8 -0
  40. data/lib/factbase/tee.rb +2 -0
  41. data/lib/factbase/term.rb +45 -17
  42. data/lib/factbase/terms/aggregates.rb +17 -15
  43. data/lib/factbase/terms/aliases.rb +4 -4
  44. data/lib/factbase/terms/casting.rb +8 -8
  45. data/lib/factbase/terms/debug.rb +2 -2
  46. data/lib/factbase/terms/defn.rb +3 -3
  47. data/lib/factbase/terms/logical.rb +53 -14
  48. data/lib/factbase/terms/math.rb +26 -26
  49. data/lib/factbase/terms/meta.rb +14 -14
  50. data/lib/factbase/terms/ordering.rb +4 -4
  51. data/lib/factbase/terms/strings.rb +8 -8
  52. data/lib/factbase/terms/system.rb +3 -3
  53. data/lib/factbase.rb +67 -55
  54. data/test/factbase/cached/test_cached_factbase.rb +22 -0
  55. data/test/factbase/cached/test_cached_query.rb +79 -0
  56. data/test/factbase/indexed/test_indexed_query.rb +175 -0
  57. data/test/factbase/sync/test_sync_query.rb +30 -0
  58. data/test/factbase/terms/test_aggregates.rb +5 -5
  59. data/test/factbase/terms/test_aliases.rb +7 -7
  60. data/test/factbase/terms/test_casting.rb +8 -8
  61. data/test/factbase/terms/test_debug.rb +6 -6
  62. data/test/factbase/terms/test_defn.rb +14 -14
  63. data/test/factbase/terms/test_logical.rb +17 -19
  64. data/test/factbase/terms/test_math.rb +63 -61
  65. data/test/factbase/terms/test_meta.rb +36 -36
  66. data/test/factbase/terms/test_ordering.rb +9 -9
  67. data/test/factbase/terms/test_strings.rb +10 -10
  68. data/test/factbase/terms/test_system.rb +6 -6
  69. data/test/factbase/test_accum.rb +5 -5
  70. data/test/factbase/test_fact.rb +12 -12
  71. data/test/factbase/test_logged.rb +7 -0
  72. data/test/factbase/test_query.rb +99 -37
  73. data/test/factbase/test_rules.rb +1 -1
  74. data/test/factbase/test_syntax.rb +12 -12
  75. data/test/factbase/test_tee.rb +8 -8
  76. data/test/factbase/test_term.rb +39 -30
  77. data/test/test__helper.rb +2 -2
  78. data/test/test_factbase.rb +6 -0
  79. metadata +29 -4
  80. data/lib/factbase/query_once.rb +0 -54
  81. data/lib/factbase/term_once.rb +0 -67
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
4
+ # SPDX-License-Identifier: MIT
5
+
6
+ require 'decoor'
7
+ require_relative '../../factbase'
8
+
9
+ # A synchronous thread-safe factbase.
10
+ #
11
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
12
+ # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
13
+ # License:: MIT
14
+ class Factbase::SyncFactbase
15
+ decoor(:origin)
16
+
17
+ # Constructor.
18
+ # @param [Factbase] origin Original factbase to decorate
19
+ # @param [Mutex] mutex Mutex to use for synchronization
20
+ def initialize(origin, mutex = Mutex.new)
21
+ @origin = origin
22
+ @mutex = mutex
23
+ end
24
+
25
+ # Insert a new fact and return it.
26
+ # @return [Factbase::Fact] The fact just inserted
27
+ def insert
28
+ @mutex.synchronize do
29
+ @origin.insert
30
+ end
31
+ end
32
+
33
+ # Convert a query to a term.
34
+ # @param [String] query The query to convert
35
+ # @return [Factbase::Term] The term
36
+ def to_term(query)
37
+ @origin.to_term(query)
38
+ end
39
+
40
+ # Create a query capable of iterating.
41
+ # @param [String] term The query to use for selections
42
+ # @param [Array<Hash>] maps Possible maps to use
43
+ def query(term, maps = nil)
44
+ term = to_term(term) if term.is_a?(String)
45
+ require_relative 'sync_query'
46
+ Factbase::SyncQuery.new(@origin.query(term, maps), @mutex, self)
47
+ end
48
+
49
+ # Run an ACID transaction.
50
+ # @return [Factbase::Churn] How many facts have been changed (zero if rolled back)
51
+ # @yield [Factbase] Block to execute in transaction
52
+ def txn
53
+ @origin.txn do |fbt|
54
+ yield Factbase::SyncFactbase.new(fbt, @mutex)
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
4
+ # SPDX-License-Identifier: MIT
5
+
6
+ require_relative '../../factbase'
7
+
8
+ # Synchronized thread-safe query.
9
+ #
10
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
11
+ # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
12
+ # License:: MIT
13
+ class Factbase::SyncQuery
14
+ # Constructor.
15
+ # @param [Factbase::Query] origin Original query
16
+ # @param [Mutex] mutex The mutex
17
+ def initialize(origin, mutex, fb)
18
+ @origin = origin
19
+ @mutex = mutex
20
+ @fb = fb
21
+ end
22
+
23
+ # Iterate facts one by one.
24
+ # @param [Hash] params Optional params accessible in the query via the "$" symbol
25
+ # @yield [Fact] Facts one-by-one
26
+ # @return [Integer] Total number of facts yielded
27
+ def each(fb = @fb, params = {}, &)
28
+ return to_enum(__method__, fb, params) unless block_given?
29
+ try_lock do
30
+ @origin.each(fb, params, &)
31
+ end
32
+ end
33
+
34
+ # Read a single value.
35
+ # @param [Factbase] fb The factbase
36
+ # @param [Hash] params Optional params accessible in the query via the "$" symbol
37
+ # @return [String|Integer|Float|Time|Array|NilClass] The value evaluated
38
+ def one(fb = @fb, params = {})
39
+ try_lock do
40
+ @origin.one(fb, params)
41
+ end
42
+ end
43
+
44
+ # Delete all facts that match the query.
45
+ # @param [Factbase] fb The factbase
46
+ # @return [Integer] Total number of facts deleted
47
+ def delete!(fb = @fb)
48
+ try_lock do
49
+ @origin.delete!(fb)
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def try_lock
56
+ locked = @mutex.try_lock
57
+ r = yield
58
+ @mutex.unlock if locked
59
+ r
60
+ end
61
+ end
@@ -8,22 +8,11 @@ require 'time'
8
8
  require_relative '../factbase'
9
9
  require_relative 'fact'
10
10
  require_relative 'term'
11
- require_relative 'term_once'
12
11
 
13
- # Syntax.
12
+ # Syntax parser.
14
13
  #
15
14
  # This is an internal class, it is not supposed to be instantiated directly.
16
15
  #
17
- # However, you can use it directly, if you need a parser of our syntax. You can
18
- # create your own "Term" class and let this parser make instances of it for
19
- # every term it meets in the query:
20
- #
21
- # require 'factbase/syntax'
22
- # t = Factbase::Syntax.new(Factbase.new, '(hello world)', MyTerm).to_term
23
- #
24
- # The +MyTerm+ class should have a constructor with two arguments:
25
- # the operator and the list of operands (Array).
26
- #
27
16
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
28
17
  # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
29
18
  # License:: MIT
@@ -33,19 +22,13 @@ class Factbase::Syntax
33
22
 
34
23
  # Ctor.
35
24
  #
36
- # The class provided as the +term+ argument must have a three-argument
37
- # constructor, similar to the class +Factbase::Term+. Also, it must be
25
+ # The class provided as the +term+ argument must have a constructor that accepts
26
+ # an operator, operands array, and a keyword argument fb. Also, it must be
38
27
  # a child of +Factbase::Term+.
39
28
  #
40
- # @param [Factbase] fb Factbase
41
29
  # @param [String] query The query, for example "(eq id 42)"
42
- # @param [Class] term The class to instantiate to make every term
43
- def initialize(fb, query, term: Factbase::Term)
44
- @fb = fb
30
+ def initialize(query)
45
31
  @query = query
46
- raise "Term must be a Class, while #{term.class.name} provided" unless term.is_a?(Class)
47
- raise "The 'term' must be a child of Factbase::Term, while #{term.name} provided" unless term <= Factbase::Term
48
- @term = term
49
32
  end
50
33
 
51
34
  # Convert it to a term.
@@ -74,7 +57,7 @@ class Factbase::Syntax
74
57
  raise "Too many terms (#{@ast[1]} != #{@tokens.size})" if @ast[1] != @tokens.size
75
58
  t = @ast[0]
76
59
  raise 'No terms found in the AST' if t.nil?
77
- raise "#{t.class.name} is not an instance of #{@term}, thus not a proper term" unless t.is_a?(@term)
60
+ raise "#{t.class.name} is not an instance of Term" unless t.is_a?(Factbase::Term)
78
61
  t
79
62
  end
80
63
 
@@ -82,9 +65,13 @@ class Factbase::Syntax
82
65
  # token at the position is not a literal (like 42 or "Hello") but a term,
83
66
  # the function recursively calls itself.
84
67
  #
85
- # The function returns an two-elements array, where the first element
68
+ # The function returns a two-element array, where the first element
86
69
  # is the term/literal and the second one is the position where the
87
70
  # scanning should continue.
71
+ #
72
+ # @param [Array] tokens Array of tokens
73
+ # @param [Integer] at Position to start parsing from
74
+ # @return [Array<Factbase::Term,Integer>] The term detected and ending position
88
75
  def to_ast(tokens, at)
89
76
  raise "Closing too soon at ##{at}" if tokens[at] == :close
90
77
  return [tokens[at], at + 1] unless tokens[at] == :open
@@ -103,12 +90,12 @@ class Factbase::Syntax
103
90
  operands << operand
104
91
  break if tokens[at] == :close
105
92
  end
106
- t = @term.new(@fb, op, operands)
107
- t = Factbase::TermOnce.new(t, @fb.cache) if t.instance_of?(Factbase::Term)
93
+ t = Factbase::Term.new(op, operands)
108
94
  [t, at + 1]
109
95
  end
110
96
 
111
97
  # Turns a query into an array of tokens.
98
+ # @return [Array] Array of tokens
112
99
  def to_tokens
113
100
  list = []
114
101
  acc = ''
@@ -32,7 +32,7 @@ class Factbase::Tallied
32
32
  end
33
33
 
34
34
  def query(query)
35
- Query.new(@fb.query(query), @churn)
35
+ Query.new(@fb.query(query), @churn, @fb)
36
36
  end
37
37
 
38
38
  def txn
@@ -69,24 +69,25 @@ class Factbase::Tallied
69
69
  #
70
70
  # This is an internal class, it is not supposed to be instantiated directly.
71
71
  class Query
72
- def initialize(query, churn)
72
+ def initialize(query, churn, fb)
73
73
  @query = query
74
74
  @churn = churn
75
+ @fb = fb
75
76
  end
76
77
 
77
- def one(params = {})
78
- @query.one(params)
78
+ def one(fb = @fb, params = {})
79
+ @query.one(fb, params)
79
80
  end
80
81
 
81
- def each(params = {}, &)
82
- return to_enum(__method__, params) unless block_given?
83
- @query.each(params) do |f|
82
+ def each(fb = @fb, params = {}, &)
83
+ return to_enum(__method__, fb, params) unless block_given?
84
+ @query.each(fb, params) do |f|
84
85
  yield Fact.new(f, @churn)
85
86
  end
86
87
  end
87
88
 
88
- def delete!
89
- c = @query.delete!
89
+ def delete!(fb = @fb)
90
+ c = @query.delete!(fb)
90
91
  @churn.append(0, c, 0)
91
92
  c
92
93
  end
@@ -59,6 +59,14 @@ class Factbase::Taped
59
59
  end
60
60
  end
61
61
 
62
+ def to_a
63
+ @origin.to_a
64
+ end
65
+
66
+ def group_by(&)
67
+ @origin.group_by(&)
68
+ end
69
+
62
70
  # Decorator of Hash.
63
71
  class TapedHash
64
72
  def initialize(origin, added)
data/lib/factbase/tee.rb CHANGED
@@ -16,7 +16,9 @@ class Factbase::Tee
16
16
  # @param [Factbase::Fact] fact Primary fact to use for reading
17
17
  # @param [Factbase::Fact] upper Fact to access with a "$" prefix
18
18
  def initialize(fact, upper)
19
+ raise 'Fact is nil' if fact.nil?
19
20
  @fact = fact
21
+ raise 'Upper is nil' if upper.nil?
20
22
  @upper = upper
21
23
  end
22
24
 
data/lib/factbase/term.rb CHANGED
@@ -17,8 +17,8 @@ require_relative 'tee'
17
17
  #
18
18
  # require 'factbase/fact'
19
19
  # require 'factbase/term'
20
- # f = Factbase::Fact.new(Factbase.new, Mutex.new, { 'foo' => [42, 256, 'Hello, world!'] })
21
- # t = Factbase::Term.new(Factbase.new, :lt, [:foo, 50])
20
+ # f = Factbase::Fact.new({ 'foo' => [42, 256, 'Hello, world!'] })
21
+ # t = Factbase::Term.new(:lt, [:foo, 50])
22
22
  # assert(t.evaluate(f))
23
23
  #
24
24
  # The design of this class may look ugly, since it has a large number of
@@ -30,11 +30,19 @@ require_relative 'tee'
30
30
  # Moreover, it looks like the number of possible term types is rather limited
31
31
  # and currently we implement most of them.
32
32
  #
33
+ # It is NOT thread-safe!
34
+ #
33
35
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
34
36
  # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
35
37
  # License:: MIT
36
38
  class Factbase::Term
37
- attr_reader :op, :operands
39
+ # The operator of this term
40
+ # @return [Symbol] The operator
41
+ attr_reader :op
42
+
43
+ # The operands of this term
44
+ # @return [Array] The operands
45
+ attr_reader :operands
38
46
 
39
47
  require_relative 'terms/math'
40
48
  include Factbase::Term::Math
@@ -70,21 +78,41 @@ class Factbase::Term
70
78
  include Factbase::Term::Debug
71
79
 
72
80
  # Ctor.
73
- # @param [Factbase] fb Factbase
74
81
  # @param [Symbol] operator Operator
75
82
  # @param [Array] operands Operands
76
- def initialize(fb, operator, operands)
77
- @fb = fb
83
+ def initialize(operator, operands)
78
84
  @op = operator
79
85
  @operands = operands
80
86
  end
81
87
 
88
+ def redress!(type, **args)
89
+ extend type
90
+ args.each { |k, v| send(:instance_variable_set, :"@#{k}", v) }
91
+ @operands.map do |op|
92
+ if op.is_a?(Factbase::Term)
93
+ op.redress!(type, **args)
94
+ else
95
+ op
96
+ end
97
+ end
98
+ end
99
+
100
+ # Try to predict which facts from the provided list
101
+ # should be evaluated. If no prediction can be made,
102
+ # the same list is returned.
103
+ # @param [Array<Hash>] maps Records to iterate, maybe
104
+ # @return [Array<Hash>] Records to iterate
105
+ def predict(maps, _params)
106
+ maps
107
+ end
108
+
82
109
  # Does it match the fact?
83
110
  # @param [Factbase::Fact] fact The fact
84
111
  # @param [Array<Factbase::Fact>] maps All maps available
85
- # @return [bool] TRUE if matches
86
- def evaluate(fact, maps)
87
- send(@op, fact, maps)
112
+ # @param [Factbase] fb Factbase to use for sub-queries
113
+ # @return [Boolean] TRUE if matches
114
+ def evaluate(fact, maps, fb)
115
+ send(@op, fact, maps, fb)
88
116
  rescue NoMethodError => e
89
117
  raise "Probably the term '#{@op}' is not defined at #{self}:\n#{Backtrace.new(e)}"
90
118
  rescue StandardError => e
@@ -146,26 +174,26 @@ class Factbase::Term
146
174
  "(#{items.join(' ')})"
147
175
  end
148
176
 
149
- private
150
-
151
- def at(fact, maps)
177
+ def at(fact, maps, fb)
152
178
  assert_args(2)
153
- i = the_values(0, fact, maps)
179
+ i = _values(0, fact, maps, fb)
154
180
  raise "Too many values (#{i.size}) at first position, one expected" unless i.size == 1
155
181
  i = i[0]
156
182
  return nil if i.nil?
157
- v = the_values(1, fact, maps)
183
+ v = _values(1, fact, maps, fb)
158
184
  return nil if v.nil?
159
185
  v[i]
160
186
  end
161
187
 
188
+ private
189
+
162
190
  def assert_args(num)
163
191
  c = @operands.size
164
192
  raise "Too many (#{c}) operands for '#{@op}' (#{num} expected)" if c > num
165
193
  raise "Too few (#{c}) operands for '#{@op}' (#{num} expected)" if c < num
166
194
  end
167
195
 
168
- def by_symbol(pos, fact)
196
+ def _by_symbol(pos, fact)
169
197
  o = @operands[pos]
170
198
  raise "A symbol expected at ##{pos}, but '#{o}' (#{o.class}) provided" unless o.is_a?(Symbol)
171
199
  k = o.to_s
@@ -173,9 +201,9 @@ class Factbase::Term
173
201
  end
174
202
 
175
203
  # @return [Array|nil] Either array of values or NIL
176
- def the_values(pos, fact, maps)
204
+ def _values(pos, fact, maps, fb)
177
205
  v = @operands[pos]
178
- v = v.evaluate(fact, maps) if v.is_a?(Factbase::Term)
206
+ v = v.evaluate(fact, maps, fb) if v.is_a?(Factbase::Term)
179
207
  v = fact[v.to_s] if v.is_a?(Symbol)
180
208
  return v if v.nil?
181
209
  unless v.is_a?(Array)
@@ -11,30 +11,32 @@ require_relative '../../factbase'
11
11
  # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
12
12
  # License:: MIT
13
13
  module Factbase::Term::Aggregates
14
- def min(_fact, maps)
14
+ def min(_fact, maps, _fb)
15
15
  assert_args(1)
16
- best(maps) { |v, b| v < b }
16
+ _best(maps) { |v, b| v < b }
17
17
  end
18
18
 
19
- def max(_fact, maps)
19
+ def max(_fact, maps, _fb)
20
20
  assert_args(1)
21
- best(maps) { |v, b| v > b }
21
+ _best(maps) { |v, b| v > b }
22
22
  end
23
23
 
24
- def count(_fact, maps)
24
+ def count(_fact, maps, _fb)
25
25
  maps.size
26
26
  end
27
27
 
28
- def nth(_fact, maps)
28
+ def nth(_fact, maps, _fb)
29
29
  assert_args(2)
30
30
  pos = @operands[0]
31
31
  raise "An integer expected, but #{pos} provided" unless pos.is_a?(Integer)
32
32
  k = @operands[1]
33
33
  raise "A symbol expected, but #{k} provided" unless k.is_a?(Symbol)
34
- maps[pos][k.to_s]
34
+ m = maps[pos]
35
+ return nil if m.nil?
36
+ m[k.to_s]
35
37
  end
36
38
 
37
- def first(_fact, maps)
39
+ def first(_fact, maps, _fb)
38
40
  assert_args(1)
39
41
  k = @operands[0]
40
42
  raise "A symbol expected, but #{k} provided" unless k.is_a?(Symbol)
@@ -43,7 +45,7 @@ module Factbase::Term::Aggregates
43
45
  first[k.to_s]
44
46
  end
45
47
 
46
- def sum(_fact, maps)
48
+ def sum(_fact, maps, _fb)
47
49
  k = @operands[0]
48
50
  raise "A symbol expected, but '#{k}' provided" unless k.is_a?(Symbol)
49
51
  sum = 0
@@ -58,24 +60,24 @@ module Factbase::Term::Aggregates
58
60
  sum
59
61
  end
60
62
 
61
- def agg(fact, maps)
63
+ def agg(fact, maps, fb)
62
64
  assert_args(2)
63
65
  selector = @operands[0]
64
66
  raise "A term expected, but '#{selector}' provided" unless selector.is_a?(Factbase::Term)
65
67
  term = @operands[1]
66
68
  raise "A term expected, but '#{term}' provided" unless term.is_a?(Factbase::Term)
67
- subset = @fb.query(selector.to_s, maps).each(fact).to_a
68
- term.evaluate(nil, subset)
69
+ subset = fb.query(selector, maps).each(fb, fact).to_a
70
+ term.evaluate(nil, subset, fb)
69
71
  end
70
72
 
71
- def empty(fact, maps)
73
+ def empty(fact, maps, fb)
72
74
  assert_args(1)
73
75
  term = @operands[0]
74
76
  raise "A term expected, but '#{term}' provided" unless term.is_a?(Factbase::Term)
75
- @fb.query(term.to_s, maps).each(fact).to_a.empty?
77
+ fb.query(term, maps).each(fb, fact).to_a.empty?
76
78
  end
77
79
 
78
- def best(maps)
80
+ def _best(maps)
79
81
  k = @operands[0]
80
82
  raise "A symbol expected, but #{k} provided" unless k.is_a?(Symbol)
81
83
  best = nil
@@ -11,16 +11,16 @@ require_relative '../../factbase'
11
11
  # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
12
12
  # License:: MIT
13
13
  module Factbase::Term::Aliases
14
- def as(fact, maps)
14
+ def as(fact, maps, fb)
15
15
  assert_args(2)
16
16
  a = @operands[0]
17
17
  raise "A symbol expected as first argument of 'as'" unless a.is_a?(Symbol)
18
- vv = the_values(1, fact, maps)
18
+ vv = _values(1, fact, maps, fb)
19
19
  vv&.each { |v| fact.send(:"#{a}=", v) }
20
20
  true
21
21
  end
22
22
 
23
- def join(fact, maps)
23
+ def join(fact, maps, fb)
24
24
  assert_args(2)
25
25
  jumps = @operands[0]
26
26
  raise "A string expected as first argument of 'join'" unless jumps.is_a?(String)
@@ -30,7 +30,7 @@ module Factbase::Term::Aliases
30
30
  .map { |j| j.size == 1 ? [j[0], j[0]] : j }
31
31
  term = @operands[1]
32
32
  raise "A term expected, but '#{term}' provided" unless term.is_a?(Factbase::Term)
33
- subset = @fb.query(term.to_s, maps).each(fact).to_a
33
+ subset = fb.query(term, maps).each(fb, fact).to_a
34
34
  subset.each do |s|
35
35
  jumps.each do |to, from|
36
36
  s[from]&.each do |v|
@@ -11,30 +11,30 @@ require_relative '../../factbase'
11
11
  # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
12
12
  # License:: MIT
13
13
  module Factbase::Term::Casting
14
- def to_string(fact, maps)
14
+ def to_string(fact, maps, fb)
15
15
  assert_args(1)
16
- vv = the_values(0, fact, maps)
16
+ vv = _values(0, fact, maps, fb)
17
17
  return nil if vv.nil?
18
18
  vv[0].to_s
19
19
  end
20
20
 
21
- def to_integer(fact, maps)
21
+ def to_integer(fact, maps, fb)
22
22
  assert_args(1)
23
- vv = the_values(0, fact, maps)
23
+ vv = _values(0, fact, maps, fb)
24
24
  return nil if vv.nil?
25
25
  vv[0].to_i
26
26
  end
27
27
 
28
- def to_float(fact, maps)
28
+ def to_float(fact, maps, fb)
29
29
  assert_args(1)
30
- vv = the_values(0, fact, maps)
30
+ vv = _values(0, fact, maps, fb)
31
31
  return nil if vv.nil?
32
32
  vv[0].to_f
33
33
  end
34
34
 
35
- def to_time(fact, maps)
35
+ def to_time(fact, maps, fb)
36
36
  assert_args(1)
37
- vv = the_values(0, fact, maps)
37
+ vv = _values(0, fact, maps, fb)
38
38
  return nil if vv.nil?
39
39
  Time.parse(vv[0].to_s)
40
40
  end
@@ -11,11 +11,11 @@ require_relative '../../factbase'
11
11
  # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
12
12
  # License:: MIT
13
13
  module Factbase::Term::Debug
14
- def traced(fact, maps)
14
+ def traced(fact, maps, fb)
15
15
  assert_args(1)
16
16
  t = @operands[0]
17
17
  raise "A term expected, but '#{t}' provided" unless t.is_a?(Factbase::Term)
18
- r = t.evaluate(fact, maps)
18
+ r = t.evaluate(fact, maps, fb)
19
19
  puts "#{self} -> #{r}"
20
20
  r
21
21
  end
@@ -11,21 +11,21 @@ require_relative '../../factbase'
11
11
  # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
12
12
  # License:: MIT
13
13
  module Factbase::Term::Defn
14
- def defn(_fact, _maps)
14
+ def defn(_fact, _maps, _fb)
15
15
  assert_args(2)
16
16
  fn = @operands[0]
17
17
  raise "A symbol expected as first argument of 'defn'" unless fn.is_a?(Symbol)
18
18
  raise "Can't use '#{fn}' name as a term" if Factbase::Term.instance_methods(true).include?(fn)
19
19
  raise "Term '#{fn}' is already defined" if Factbase::Term.private_instance_methods(false).include?(fn)
20
20
  raise "The '#{fn}' is a bad name for a term" unless fn.match?(/^[a-z_]+$/)
21
- e = "class Factbase::Term\nprivate\ndef #{fn}(fact, maps)\n#{@operands[1]}\nend\nend"
21
+ e = "class Factbase::Term\nprivate\ndef #{fn}(fact, maps, fb)\n#{@operands[1]}\nend\nend"
22
22
  # rubocop:disable Security/Eval
23
23
  eval(e)
24
24
  # rubocop:enable Security/Eval
25
25
  true
26
26
  end
27
27
 
28
- def undef(_fact, _maps)
28
+ def undef(_fact, _maps, _fb)
29
29
  assert_args(1)
30
30
  fn = @operands[0]
31
31
  raise "A symbol expected as first argument of 'undef'" unless fn.is_a?(Symbol)