factbase 0.0.34 → 0.0.35

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: 20c84ac9e67311e6333da8c6da3d69ae445aad64686017a69bc702f50d703223
4
- data.tar.gz: c275ca48a224b42f09e71551b563585036b866f99acf8dd58e13311bdaa0beec
3
+ metadata.gz: fc97bfbf967aa07fa189c2d3c696eb8d963f4647c06fb5d97ca4b0e2b4edb4db
4
+ data.tar.gz: b38987779b663f76cbf9876b4e117cd1fabb3e1c2ef07bd6ab433be0dede9a44
5
5
  SHA512:
6
- metadata.gz: 1ef21c5e33b3cf5f67e7f5edb56110e9f4d62c970cbc52158ca3922661d9a3d3ed411c26b95b2be97c1d6b6a4a5ebb5c521c87401325da42e585bd61f31b403f
7
- data.tar.gz: 9f02e0addedcff827c76f7c5a58cae70c84b73c02522ab429ba0607bf967f0412c8a82ba1b8caa49c0c0329498f0f440dc95e861fed563362bd1f7295bea52ba
6
+ metadata.gz: 97cdc3269d4fbfea8d7246f6cee197be5bdf8b1e463573940b9be2384cbc2718a454e7f1291c5efc11554fc59c84a97c9349def6413d0ee54192118820cdb252
7
+ data.tar.gz: '049a1b2ae928bae38e1079f4e67d4f29a3efe3201e27c28c92f58bf4a2eda519a62a9a18095d857792337b19775e319921f8494643718a406bf6c377e36eefa6'
data/lib/factbase/fact.rb CHANGED
@@ -25,6 +25,9 @@ require 'time'
25
25
  require_relative '../factbase'
26
26
 
27
27
  # Fact.
28
+ #
29
+ # This is an internal class, it is not supposed to be instantiated directly.
30
+ #
28
31
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
29
32
  # Copyright:: Copyright (c) 2024 Yegor Bugayenko
30
33
  # License:: MIT
@@ -37,7 +40,7 @@ class Factbase::Fact
37
40
  # Convert it to a string.
38
41
  # @return [String] String representation of it (in JSON)
39
42
  def to_s
40
- @map.to_json
43
+ "[ #{@map.map { |k, v| "#{k}: #{v}" }.join(', ')} ]"
41
44
  end
42
45
 
43
46
  # When a method is missing, this method is called.
data/lib/factbase/inv.rb CHANGED
@@ -61,6 +61,9 @@ class Factbase::Inv
61
61
  end
62
62
 
63
63
  # Fact decorator.
64
+ #
65
+ # This is an internal class, it is not supposed to be instantiated directly.
66
+ #
64
67
  class Fact
65
68
  def initialize(fact, block)
66
69
  @fact = fact
@@ -89,6 +92,9 @@ class Factbase::Inv
89
92
  end
90
93
 
91
94
  # Query decorator.
95
+ #
96
+ # This is an internal class, it is not supposed to be instantiated directly.
97
+ #
92
98
  class Query
93
99
  def initialize(query, block)
94
100
  @query = query
@@ -63,6 +63,9 @@ class Factbase::Looged
63
63
  end
64
64
 
65
65
  # Fact decorator.
66
+ #
67
+ # This is an internal class, it is not supposed to be instantiated directly.
68
+ #
66
69
  class Fact
67
70
  MAX_LENGTH = 64
68
71
 
@@ -98,6 +101,9 @@ class Factbase::Looged
98
101
  end
99
102
 
100
103
  # Query decorator.
104
+ #
105
+ # This is an internal class, it is not supposed to be instantiated directly.
106
+ #
101
107
  class Query
102
108
  def initialize(query, expr, loog)
103
109
  @query = query
@@ -25,6 +25,9 @@ require_relative 'syntax'
25
25
  require_relative 'fact'
26
26
 
27
27
  # Query.
28
+ #
29
+ # This is an internal class, it is not supposed to be instantiated directly.
30
+ #
28
31
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
29
32
  # Copyright:: Copyright (c) 2024 Yegor Bugayenko
30
33
  # License:: MIT
@@ -40,11 +43,13 @@ class Factbase::Query
40
43
  # @return [Integer] Total number of facts yielded
41
44
  def each
42
45
  return to_enum(__method__) unless block_given?
43
- term = Factbase::Syntax.new(@query).to_term.on(@maps)
46
+ term = Factbase::Syntax.new(@query).to_term
44
47
  yielded = 0
45
48
  @maps.each do |m|
46
49
  f = Factbase::Fact.new(@mutex, m)
47
- next unless term.evaluate(f)
50
+ r = term.evaluate(f, @maps)
51
+ raise 'Unexpected evaluation result, must be boolean' unless r.is_a?(TrueClass) || r.is_a?(FalseClass)
52
+ next unless r
48
53
  yield f
49
54
  yielded += 1
50
55
  end
@@ -59,7 +64,7 @@ class Factbase::Query
59
64
  @mutex.synchronize do
60
65
  @maps.delete_if do |m|
61
66
  f = Factbase::Fact.new(@mutex, m)
62
- if term.evaluate(f)
67
+ if term.evaluate(f, @maps)
63
68
  deleted += 1
64
69
  true
65
70
  else
@@ -62,6 +62,9 @@ class Factbase::Rules
62
62
  end
63
63
 
64
64
  # Fact decorator.
65
+ #
66
+ # This is an internal class, it is not supposed to be instantiated directly.
67
+ #
65
68
  class Fact
66
69
  def initialize(fact, check)
67
70
  @fact = fact
@@ -91,6 +94,9 @@ class Factbase::Rules
91
94
  end
92
95
 
93
96
  # Query decorator.
97
+ #
98
+ # This is an internal class, it is not supposed to be instantiated directly.
99
+ #
94
100
  class Query
95
101
  def initialize(query, check)
96
102
  @query = query
@@ -110,6 +116,9 @@ class Factbase::Rules
110
116
  end
111
117
 
112
118
  # Check one fact.
119
+ #
120
+ # This is an internal class, it is not supposed to be instantiated directly.
121
+ #
113
122
  class Check
114
123
  def initialize(fb, expr)
115
124
  @fb = fb
@@ -117,7 +126,7 @@ class Factbase::Rules
117
126
  end
118
127
 
119
128
  def it(fact)
120
- return if Factbase::Syntax.new(@expr).to_term.evaluate(fact)
129
+ return if Factbase::Syntax.new(@expr).to_term.evaluate(fact, [])
121
130
  raise "The fact is in invalid state: #{fact}"
122
131
  end
123
132
  end
@@ -26,6 +26,9 @@ require_relative 'fact'
26
26
  require_relative 'term'
27
27
 
28
28
  # Syntax.
29
+ #
30
+ # This is an internal class, it is not supposed to be instantiated directly.
31
+ #
29
32
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
30
33
  # Copyright:: Copyright (c) 2024 Yegor Bugayenko
31
34
  # License:: MIT
data/lib/factbase/term.rb CHANGED
@@ -24,6 +24,9 @@ require_relative '../factbase'
24
24
  require_relative 'fact'
25
25
 
26
26
  # Term.
27
+ #
28
+ # This is an internal class, it is not supposed to be instantiated directly.
29
+ #
27
30
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
28
31
  # Copyright:: Copyright (c) 2024 Yegor Bugayenko
29
32
  # License:: MIT
@@ -40,23 +43,12 @@ class Factbase::Term
40
43
 
41
44
  # Does it match the fact?
42
45
  # @param [Factbase::Fact] fact The fact
46
+ # @param [Array<Factbase::Fact>] maps All maps available
43
47
  # @return [bool] TRUE if matches
44
- def evaluate(fact)
45
- send(@op, fact)
46
- rescue NoMethodError => _e
47
- raise "Term '#{@op}' is not defined"
48
- end
49
-
50
- # Put it into the context: let it see the entire array of maps.
51
- # @param [Array] maps The maps
52
- # @return [Factbase::Term] Itself
53
- def on(maps)
54
- m = "#{@op}_on"
55
- send(m, maps) if respond_to?(m, true)
56
- @operands.each do |o|
57
- o.on(maps) if o.is_a?(Factbase::Term)
58
- end
59
- self
48
+ def evaluate(fact, maps)
49
+ send(@op, fact, maps)
50
+ rescue NoMethodError => e
51
+ raise "Term '#{@op}' is not defined: #{e.message}"
60
52
  end
61
53
 
62
54
  # Simplify it if possible.
@@ -89,31 +81,31 @@ class Factbase::Term
89
81
 
90
82
  private
91
83
 
92
- def always(_fact)
84
+ def always(_fact, _maps)
93
85
  assert_args(0)
94
86
  true
95
87
  end
96
88
 
97
- def never(_fact)
89
+ def never(_fact, _maps)
98
90
  assert_args(0)
99
91
  false
100
92
  end
101
93
 
102
- def not(fact)
94
+ def not(fact, maps)
103
95
  assert_args(1)
104
- !only_bool(the_value(0, fact))
96
+ !only_bool(the_value(0, fact, maps))
105
97
  end
106
98
 
107
- def or(fact)
99
+ def or(fact, maps)
108
100
  (0..@operands.size - 1).each do |i|
109
- return true if only_bool(the_value(i, fact))
101
+ return true if only_bool(the_value(i, fact, maps))
110
102
  end
111
103
  false
112
104
  end
113
105
 
114
- def and(fact)
106
+ def and(fact, maps)
115
107
  (0..@operands.size - 1).each do |i|
116
- return false unless only_bool(the_value(i, fact))
108
+ return false unless only_bool(the_value(i, fact, maps))
117
109
  end
118
110
  true
119
111
  end
@@ -140,36 +132,36 @@ class Factbase::Term
140
132
  and_or_simplify
141
133
  end
142
134
 
143
- def when(fact)
135
+ def when(fact, maps)
144
136
  assert_args(2)
145
137
  a = @operands[0]
146
138
  b = @operands[1]
147
- !a.evaluate(fact) || (a.evaluate(fact) && b.evaluate(fact))
139
+ !a.evaluate(fact, maps) || (a.evaluate(fact, maps) && b.evaluate(fact, maps))
148
140
  end
149
141
 
150
- def exists(fact)
142
+ def exists(fact, _maps)
151
143
  assert_args(1)
152
144
  !by_symbol(0, fact).nil?
153
145
  end
154
146
 
155
- def absent(fact)
147
+ def absent(fact, _maps)
156
148
  assert_args(1)
157
149
  by_symbol(0, fact).nil?
158
150
  end
159
151
 
160
- def eq(fact)
161
- arithmetic(:==, fact)
152
+ def eq(fact, maps)
153
+ arithmetic(:==, fact, maps)
162
154
  end
163
155
 
164
- def lt(fact)
165
- arithmetic(:<, fact)
156
+ def lt(fact, maps)
157
+ arithmetic(:<, fact, maps)
166
158
  end
167
159
 
168
- def gt(fact)
169
- arithmetic(:>, fact)
160
+ def gt(fact, maps)
161
+ arithmetic(:>, fact, maps)
170
162
  end
171
163
 
172
- def size(fact)
164
+ def size(fact, _maps)
173
165
  assert_args(1)
174
166
  v = by_symbol(0, fact)
175
167
  return 0 if v.nil?
@@ -177,29 +169,29 @@ class Factbase::Term
177
169
  v.size
178
170
  end
179
171
 
180
- def type(fact)
172
+ def type(fact, _maps)
181
173
  assert_args(1)
182
174
  v = by_symbol(0, fact)
183
175
  return 'nil' if v.nil?
184
176
  v.class.to_s
185
177
  end
186
178
 
187
- def matches(fact)
179
+ def matches(fact, maps)
188
180
  assert_args(2)
189
- str = the_value(0, fact)
190
- raise 'String is nil' if str.nil?
181
+ str = the_value(0, fact, maps)
182
+ return false if str.nil?
191
183
  raise 'Exactly one string expected' unless str.size == 1
192
- re = the_value(1, fact)
184
+ re = the_value(1, fact, maps)
193
185
  raise 'Regexp is nil' if re.nil?
194
186
  raise 'Exactly one regexp expected' unless re.size == 1
195
187
  str[0].to_s.match?(re[0])
196
188
  end
197
189
 
198
- def arithmetic(op, fact)
190
+ def arithmetic(op, fact, maps)
199
191
  assert_args(2)
200
- lefts = the_value(0, fact)
192
+ lefts = the_value(0, fact, maps)
201
193
  return false if lefts.nil?
202
- rights = the_value(1, fact)
194
+ rights = the_value(1, fact, maps)
203
195
  return false if rights.nil?
204
196
  lefts.any? do |l|
205
197
  l = l.floor if l.is_a?(Time) && op == :==
@@ -210,34 +202,58 @@ class Factbase::Term
210
202
  end
211
203
  end
212
204
 
213
- def defn(_fact)
205
+ def defn(_fact, _maps)
214
206
  fn = @operands[0]
215
207
  raise 'A symbol expected as first argument of defn' unless fn.is_a?(Symbol)
216
- e = "class Factbase::Term\nprivate\ndef #{fn}(fact)\n#{@operands[1]}\nend\nend"
208
+ e = "class Factbase::Term\nprivate\ndef #{fn}(fact, maps)\n#{@operands[1]}\nend\nend"
217
209
  # rubocop:disable Security/Eval
218
210
  eval(e)
219
211
  # rubocop:enable Security/Eval
220
212
  true
221
213
  end
222
214
 
223
- def min(fact)
224
- vv = the_value(0, fact)
225
- return nil if vv.nil?
226
- vv.any? { |v| v == @min }
215
+ def min(_fact, maps)
216
+ @min ||= best(maps) { |v, b| v < b }
227
217
  end
228
218
 
229
- def max(fact)
230
- vv = the_value(0, fact)
231
- return nil if vv.nil?
232
- vv.any? { |v| v == @max }
219
+ def max(_fact, maps)
220
+ @max ||= best(maps) { |v, b| v > b }
233
221
  end
234
222
 
235
- def max_on(maps)
236
- @max = best(maps) { |v, b| v > b }
223
+ def count(_fact, maps)
224
+ @count ||= maps.size
237
225
  end
238
226
 
239
- def min_on(maps)
240
- @min = best(maps) { |v, b| v < b }
227
+ def sum(_fact, maps)
228
+ @sum ||=
229
+ begin
230
+ k = @operands[0]
231
+ raise "A symbol expected, but provided: #{k}" unless k.is_a?(Symbol)
232
+ sum = 0
233
+ maps.each do |m|
234
+ vv = m[k.to_s]
235
+ next if vv.nil?
236
+ vv = [vv] unless vv.is_a?(Array)
237
+ vv.each do |v|
238
+ sum += v
239
+ end
240
+ end
241
+ sum
242
+ end
243
+ end
244
+
245
+ def agg(_fact, maps)
246
+ selector = @operands[0]
247
+ raise "A term expected, but #{selector} provided" unless selector.is_a?(Factbase::Term)
248
+ term = @operands[1]
249
+ raise "A term expected, but #{term} provided" unless term.is_a?(Factbase::Term)
250
+ subset = maps.select { |m| selector.evaluate(m, maps) }
251
+ @agg ||=
252
+ if subset.empty?
253
+ term.evaluate(Factbase::Fact.new(Mutex.new, {}), subset)
254
+ else
255
+ term.evaluate(subset.first, subset)
256
+ end
241
257
  end
242
258
 
243
259
  def assert_args(num)
@@ -253,9 +269,9 @@ class Factbase::Term
253
269
  fact[k]
254
270
  end
255
271
 
256
- def the_value(pos, fact)
272
+ def the_value(pos, fact, maps)
257
273
  v = @operands[pos]
258
- v = v.evaluate(fact) if v.is_a?(Factbase::Term)
274
+ v = v.evaluate(fact, maps) if v.is_a?(Factbase::Term)
259
275
  v = fact[v.to_s] if v.is_a?(Symbol)
260
276
  return v if v.nil?
261
277
  v = [v] unless v.is_a?(Array)
@@ -25,6 +25,13 @@ require 'time'
25
25
  require_relative '../factbase'
26
26
 
27
27
  # Factbase to JSON converter.
28
+ #
29
+ # This class helps converting an entire Factbase to YAML format, for example:
30
+ #
31
+ # require 'factbase/to_json'
32
+ # fb = Factbase.new
33
+ # puts Factbase::ToJSON.new(fb).json
34
+ #
28
35
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
29
36
  # Copyright:: Copyright (c) 2024 Yegor Bugayenko
30
37
  # License:: MIT
@@ -25,6 +25,13 @@ require 'time'
25
25
  require_relative '../factbase'
26
26
 
27
27
  # Factbase to XML converter.
28
+ #
29
+ # This class helps converting an entire Factbase to YAML format, for example:
30
+ #
31
+ # require 'factbase/to_xml'
32
+ # fb = Factbase.new
33
+ # puts Factbase::ToXML.new(fb).xml
34
+ #
28
35
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
29
36
  # Copyright:: Copyright (c) 2024 Yegor Bugayenko
30
37
  # License:: MIT
@@ -25,6 +25,13 @@ require 'time'
25
25
  require_relative '../factbase'
26
26
 
27
27
  # Factbase to YAML converter.
28
+ #
29
+ # This class helps converting an entire Factbase to YAML format, for example:
30
+ #
31
+ # require 'factbase/to_yaml'
32
+ # fb = Factbase.new
33
+ # puts Factbase::ToYAML.new(fb).yaml
34
+ #
28
35
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
29
36
  # Copyright:: Copyright (c) 2024 Yegor Bugayenko
30
37
  # License:: MIT
@@ -23,6 +23,22 @@
23
23
  require_relative '../factbase'
24
24
 
25
25
  # Tuples.
26
+ #
27
+ # With the help of this class, it's possible to select a few facts
28
+ # from a factbase at a time, which depend on each other. For example,
29
+ # it's necessary to find a fact where the +name+ is set and then find
30
+ # another fact, where the salary is the +salary+ is the same as in the
31
+ # first found fact. Here is how:
32
+ #
33
+ # Factbase::Tuples.new(qt, ['(exists name)', '(eq salary, {f0.salary})']).each do |a, b|
34
+ # puts a.name
35
+ # puts b.salary
36
+ # end
37
+ #
38
+ # Here, the +{f0.salary}+ is a special substitution place, which is replaced
39
+ # by the +salary+ of the fact that is found by the previous query. The indexing
40
+ # of queries starts from zero.
41
+ #
26
42
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
27
43
  # Copyright:: Copyright (c) 2024 Yegor Bugayenko
28
44
  # License:: MIT
data/lib/factbase.rb CHANGED
@@ -29,7 +29,7 @@ require 'yaml'
29
29
  # License:: MIT
30
30
  class Factbase
31
31
  # Current version of the gem (changed by .rultor.yml on every release)
32
- VERSION = '0.0.34'
32
+ VERSION = '0.0.35'
33
33
 
34
34
  # Constructor.
35
35
  def initialize(facts = [])
@@ -44,8 +44,8 @@ class TestQuery < Minitest::Test
44
44
 
45
45
  def test_complex_parsing
46
46
  maps = []
47
- maps << { 'num' => 42 }
48
- maps << { 'pi' => 3.14, 'num' => [42, 66, 0] }
47
+ maps << { 'num' => 42, 'name' => 'Jeff' }
48
+ maps << { 'pi' => 3.14, 'num' => [42, 66, 0], 'name' => 'peter' }
49
49
  maps << { 'time' => Time.now - 100, 'num' => 0 }
50
50
  {
51
51
  '(eq num 444)' => 0,
@@ -55,15 +55,17 @@ class TestQuery < Minitest::Test
55
55
  '(exists pi)' => 1,
56
56
  '(not (exists hello))' => 3,
57
57
  '(eq "Integer" (type num))' => 2,
58
+ '(when (eq num 0) (exists time))' => 2,
58
59
  '(gt (size num) 2)' => 1,
60
+ '(matches name "^[a-z]+$")' => 1,
59
61
  '(lt (size num) 2)' => 2,
60
62
  '(eq (size hello) 0)' => 3,
61
63
  '(eq num pi)' => 0,
62
64
  '(absent time)' => 2,
63
- '(max num)' => 1,
64
- '(and (exists time) (max num))' => 0,
65
- '(and (exists pi) (max num))' => 1,
66
- '(min time)' => 1,
65
+ '(eq pi (agg (eq num 0) (sum pi)))' => 1,
66
+ '(eq num (agg (exists oops) (count)))' => 2,
67
+ '(lt num (agg (eq num 0) (max pi)))' => 2,
68
+ '(eq time (min time))' => 1,
67
69
  '(and (absent time) (exists pi))' => 1,
68
70
  "(and (exists time) (not (\t\texists pi)))" => 1,
69
71
  "(or (eq num 66) (lt time #{(Time.now - 200).utc.iso8601}))" => 1
@@ -83,7 +83,7 @@ class TestSyntax < Minitest::Test
83
83
  '(or (eq bar 888) (eq z 1))' => true,
84
84
  "(or (gt bar 100) (eq foo 'Hello, world!'))" => true
85
85
  }.each do |k, v|
86
- assert_equal(v, Factbase::Syntax.new(k).to_term.evaluate(m), k)
86
+ assert_equal(v, Factbase::Syntax.new(k).to_term.evaluate(m, []), k)
87
87
  end
88
88
  end
89
89
 
@@ -30,107 +30,107 @@ require_relative '../../lib/factbase/term'
30
30
  class TestTerm < Minitest::Test
31
31
  def test_simple_matching
32
32
  t = Factbase::Term.new(:eq, [:foo, 42])
33
- assert(t.evaluate(fact('foo' => [42])))
34
- assert(!t.evaluate(fact('foo' => 'Hello!')))
35
- assert(!t.evaluate(fact('bar' => ['Hello!'])))
33
+ assert(t.evaluate(fact('foo' => [42]), []))
34
+ assert(!t.evaluate(fact('foo' => 'Hello!'), []))
35
+ assert(!t.evaluate(fact('bar' => ['Hello!']), []))
36
36
  end
37
37
 
38
38
  def test_eq_matching
39
39
  t = Factbase::Term.new(:eq, [:foo, 42])
40
- assert(t.evaluate(fact('foo' => 42)))
41
- assert(t.evaluate(fact('foo' => [10, 5, 6, -8, 'hey', 42, 9, 'fdsf'])))
42
- assert(!t.evaluate(fact('foo' => [100])))
43
- assert(!t.evaluate(fact('foo' => [])))
44
- assert(!t.evaluate(fact('bar' => [])))
40
+ assert(t.evaluate(fact('foo' => 42), []))
41
+ assert(t.evaluate(fact('foo' => [10, 5, 6, -8, 'hey', 42, 9, 'fdsf']), []))
42
+ assert(!t.evaluate(fact('foo' => [100]), []))
43
+ assert(!t.evaluate(fact('foo' => []), []))
44
+ assert(!t.evaluate(fact('bar' => []), []))
45
45
  end
46
46
 
47
47
  def test_eq_matching_time
48
48
  now = Time.now
49
49
  t = Factbase::Term.new(:eq, [:foo, Time.parse(now.iso8601)])
50
- assert(t.evaluate(fact('foo' => now)))
51
- assert(t.evaluate(fact('foo' => [now, Time.now])))
50
+ assert(t.evaluate(fact('foo' => now), []))
51
+ assert(t.evaluate(fact('foo' => [now, Time.now]), []))
52
52
  end
53
53
 
54
54
  def test_lt_matching
55
55
  t = Factbase::Term.new(:lt, [:foo, 42])
56
- assert(t.evaluate(fact('foo' => [10])))
57
- assert(!t.evaluate(fact('foo' => [100])))
58
- assert(!t.evaluate(fact('foo' => 100)))
59
- assert(!t.evaluate(fact('bar' => 100)))
56
+ assert(t.evaluate(fact('foo' => [10]), []))
57
+ assert(!t.evaluate(fact('foo' => [100]), []))
58
+ assert(!t.evaluate(fact('foo' => 100), []))
59
+ assert(!t.evaluate(fact('bar' => 100), []))
60
60
  end
61
61
 
62
62
  def test_gt_matching
63
63
  t = Factbase::Term.new(:gt, [:foo, 42])
64
- assert(t.evaluate(fact('foo' => [100])))
65
- assert(t.evaluate(fact('foo' => 100)))
66
- assert(!t.evaluate(fact('foo' => [10])))
67
- assert(!t.evaluate(fact('foo' => 10)))
68
- assert(!t.evaluate(fact('bar' => 10)))
64
+ assert(t.evaluate(fact('foo' => [100]), []))
65
+ assert(t.evaluate(fact('foo' => 100), []))
66
+ assert(!t.evaluate(fact('foo' => [10]), []))
67
+ assert(!t.evaluate(fact('foo' => 10), []))
68
+ assert(!t.evaluate(fact('bar' => 10), []))
69
69
  end
70
70
 
71
71
  def test_lt_matching_time
72
72
  t = Factbase::Term.new(:lt, [:foo, Time.now])
73
- assert(t.evaluate(fact('foo' => [Time.now - 100])))
74
- assert(!t.evaluate(fact('foo' => [Time.now + 100])))
75
- assert(!t.evaluate(fact('bar' => [100])))
73
+ assert(t.evaluate(fact('foo' => [Time.now - 100]), []))
74
+ assert(!t.evaluate(fact('foo' => [Time.now + 100]), []))
75
+ assert(!t.evaluate(fact('bar' => [100]), []))
76
76
  end
77
77
 
78
78
  def test_gt_matching_time
79
79
  t = Factbase::Term.new(:gt, [:foo, Time.now])
80
- assert(t.evaluate(fact('foo' => [Time.now + 100])))
81
- assert(!t.evaluate(fact('foo' => [Time.now - 100])))
82
- assert(!t.evaluate(fact('bar' => [100])))
80
+ assert(t.evaluate(fact('foo' => [Time.now + 100]), []))
81
+ assert(!t.evaluate(fact('foo' => [Time.now - 100]), []))
82
+ assert(!t.evaluate(fact('bar' => [100]), []))
83
83
  end
84
84
 
85
85
  def test_false_matching
86
86
  t = Factbase::Term.new(:never, [])
87
- assert(!t.evaluate(fact('foo' => [100])))
87
+ assert(!t.evaluate(fact('foo' => [100]), []))
88
88
  end
89
89
 
90
90
  def test_not_matching
91
91
  t = Factbase::Term.new(:not, [Factbase::Term.new(:always, [])])
92
- assert(!t.evaluate(fact('foo' => [100])))
92
+ assert(!t.evaluate(fact('foo' => [100]), []))
93
93
  end
94
94
 
95
95
  def test_not_eq_matching
96
96
  t = Factbase::Term.new(:not, [Factbase::Term.new(:eq, [:foo, 100])])
97
- assert(t.evaluate(fact('foo' => [42, 12, -90])))
98
- assert(!t.evaluate(fact('foo' => 100)))
97
+ assert(t.evaluate(fact('foo' => [42, 12, -90]), []))
98
+ assert(!t.evaluate(fact('foo' => 100), []))
99
99
  end
100
100
 
101
101
  def test_size_matching
102
102
  t = Factbase::Term.new(:size, [:foo])
103
- assert_equal(3, t.evaluate(fact('foo' => [42, 12, -90])))
104
- assert_equal(0, t.evaluate(fact('bar' => 100)))
103
+ assert_equal(3, t.evaluate(fact('foo' => [42, 12, -90]), []))
104
+ assert_equal(0, t.evaluate(fact('bar' => 100), []))
105
105
  end
106
106
 
107
107
  def test_exists_matching
108
108
  t = Factbase::Term.new(:exists, [:foo])
109
- assert(t.evaluate(fact('foo' => [42, 12, -90])))
110
- assert(!t.evaluate(fact('bar' => 100)))
109
+ assert(t.evaluate(fact('foo' => [42, 12, -90]), []))
110
+ assert(!t.evaluate(fact('bar' => 100), []))
111
111
  end
112
112
 
113
113
  def test_absent_matching
114
114
  t = Factbase::Term.new(:absent, [:foo])
115
- assert(t.evaluate(fact('z' => [42, 12, -90])))
116
- assert(!t.evaluate(fact('foo' => 100)))
115
+ assert(t.evaluate(fact('z' => [42, 12, -90]), []))
116
+ assert(!t.evaluate(fact('foo' => 100), []))
117
117
  end
118
118
 
119
119
  def test_type_matching
120
120
  t = Factbase::Term.new(:type, [:foo])
121
- assert_equal('Integer', t.evaluate(fact('foo' => 42)))
122
- assert_equal('Array', t.evaluate(fact('foo' => [1, 2, 3])))
123
- assert_equal('String', t.evaluate(fact('foo' => 'Hello, world!')))
124
- assert_equal('Float', t.evaluate(fact('foo' => 3.14)))
125
- assert_equal('Time', t.evaluate(fact('foo' => Time.now)))
126
- assert_equal('nil', t.evaluate(fact))
121
+ assert_equal('Integer', t.evaluate(fact('foo' => 42), []))
122
+ assert_equal('Array', t.evaluate(fact('foo' => [1, 2, 3]), []))
123
+ assert_equal('String', t.evaluate(fact('foo' => 'Hello, world!'), []))
124
+ assert_equal('Float', t.evaluate(fact('foo' => 3.14), []))
125
+ assert_equal('Time', t.evaluate(fact('foo' => Time.now), []))
126
+ assert_equal('nil', t.evaluate(fact, []))
127
127
  end
128
128
 
129
129
  def test_regexp_matching
130
130
  t = Factbase::Term.new(:matches, [:foo, '[a-z]+'])
131
- assert(t.evaluate(fact('foo' => 'hello')))
132
- assert(t.evaluate(fact('foo' => 'hello 42')))
133
- assert(!t.evaluate(fact('foo' => 42)))
131
+ assert(t.evaluate(fact('foo' => 'hello'), []))
132
+ assert(t.evaluate(fact('foo' => 'hello 42'), []))
133
+ assert(!t.evaluate(fact('foo' => 42), []))
134
134
  end
135
135
 
136
136
  def test_or_matching
@@ -141,9 +141,9 @@ class TestTerm < Minitest::Test
141
141
  Factbase::Term.new(:eq, [:bar, 5])
142
142
  ]
143
143
  )
144
- assert(t.evaluate(fact('foo' => [4])))
145
- assert(t.evaluate(fact('bar' => [5])))
146
- assert(!t.evaluate(fact('bar' => [42])))
144
+ assert(t.evaluate(fact('foo' => [4]), []))
145
+ assert(t.evaluate(fact('bar' => [5]), []))
146
+ assert(!t.evaluate(fact('bar' => [42]), []))
147
147
  end
148
148
 
149
149
  def test_when_matching
@@ -154,16 +154,16 @@ class TestTerm < Minitest::Test
154
154
  Factbase::Term.new(:eq, [:bar, 5])
155
155
  ]
156
156
  )
157
- assert(t.evaluate(fact('foo' => 4, 'bar' => 5)))
158
- assert(!t.evaluate(fact('foo' => 4)))
159
- assert(t.evaluate(fact('foo' => 5, 'bar' => 5)))
157
+ assert(t.evaluate(fact('foo' => 4, 'bar' => 5), []))
158
+ assert(!t.evaluate(fact('foo' => 4), []))
159
+ assert(t.evaluate(fact('foo' => 5, 'bar' => 5), []))
160
160
  end
161
161
 
162
162
  def test_defn_simple
163
163
  t = Factbase::Term.new(:defn, [:foo, 'self.to_s'])
164
- assert_equal(true, t.evaluate(fact('foo' => 4)))
164
+ assert_equal(true, t.evaluate(fact('foo' => 4), []))
165
165
  t1 = Factbase::Term.new(:foo, ['hello, world!'])
166
- assert_equal('(foo \'hello, world!\')', t1.evaluate(fact))
166
+ assert_equal('(foo \'hello, world!\')', t1.evaluate(fact, []))
167
167
  end
168
168
 
169
169
  private
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: factbase
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.34
4
+ version: 0.0.35
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-05-25 00:00:00.000000000 Z
11
+ date: 2024-05-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json