factbase 0.12.0 → 0.13.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: f31200ddcd8de0d737dbabea9aefc051794f86bbae0891f8d624545912972add
4
- data.tar.gz: 7c1688856cde3e72f8e9f53e846b39c7cef508a0df95cc88d0631bfae36ce07f
3
+ metadata.gz: cb06b280801b081a1ae2c02147b4c960017c52a0cdb2c38dcd895cff53943344
4
+ data.tar.gz: cde4f73ddf30a7d4b6d9f9b8dadf823c948f6bb5fa672ad180867a46ef7b9ce2
5
5
  SHA512:
6
- metadata.gz: 26c83d3a288f8106bfe17856bccb9c1bde6b130384e9a4546456a76c1b6a69fff9c49954789aaeee6413f7cfae638d6f1320f1de4f7ac000b3c552b31449ebbd
7
- data.tar.gz: 761d577bc112c4d542667508924406570ff449fbab87af3a18fc0ce7c8a94cccf6d40f400b1b1c3f6bd76d95a51678a680fd17feeee0b89caacbac2b56e8e6f6
6
+ metadata.gz: 42072d2fd692798e3dd25a16167416cc6afd601d12998a1d0a5d79b2ab18d66bc09fcf40ae11e2a53af4df6fd9f5cba56a6adf32b94edd11e5122c9992304bbc
7
+ data.tar.gz: e6ec47e6c0715ac9c8148bc5ac25e36f6e009dd7da34c32386a7b5d97dcdf11c6ed6e5ed17f27ca03e2ad454bf5dacae0624ec7ab5119a4396f4a4134a6f5b11
@@ -16,4 +16,4 @@ jobs:
16
16
  runs-on: ubuntu-24.04
17
17
  steps:
18
18
  - uses: actions/checkout@v4
19
- - uses: crate-ci/typos@v1.33.1
19
+ - uses: crate-ci/typos@v1.34.0
data/Gemfile CHANGED
@@ -11,7 +11,7 @@ gem 'minitest-reporters', '~>1.7', require: false
11
11
  gem 'os', '~>1.1', require: false
12
12
  gem 'qbash', '~>0.4', require: false
13
13
  gem 'rake', '~>13.2', require: false
14
- gem 'rdoc', '~>6.13', require: false
14
+ gem 'rdoc', '6.14.1', require: false # GPL
15
15
  gem 'rubocop', '~>1.74', require: false
16
16
  gem 'rubocop-minitest', '~>0.38', require: false
17
17
  gem 'rubocop-performance', '~>1.25', require: false
data/Gemfile.lock CHANGED
@@ -24,7 +24,7 @@ GEM
24
24
  date (3.4.1)
25
25
  decoor (0.1.0)
26
26
  docile (1.4.1)
27
- elapsed (0.0.1)
27
+ elapsed (0.1.0)
28
28
  loog (> 0)
29
29
  tago (> 0)
30
30
  ellipsized (0.3.0)
@@ -67,7 +67,7 @@ GEM
67
67
  racc (1.8.1)
68
68
  rainbow (3.1.1)
69
69
  rake (13.3.0)
70
- rdoc (6.14.1)
70
+ rdoc (6.14.2)
71
71
  erb
72
72
  psych (>= 4.0.0)
73
73
  regexp_parser (2.10.0)
@@ -134,7 +134,7 @@ DEPENDENCIES
134
134
  os (~> 1.1)
135
135
  qbash (~> 0.4)
136
136
  rake (~> 13.2)
137
- rdoc (~> 6.13)
137
+ rdoc (= 6.14.1)
138
138
  rubocop (~> 1.74)
139
139
  rubocop-minitest (~> 0.38)
140
140
  rubocop-performance (~> 1.25)
@@ -6,7 +6,7 @@
6
6
  require_relative '../lib/factbase'
7
7
 
8
8
  def bench_query(bmk, fb)
9
- total = 500
9
+ total = 20_000
10
10
  total.times do |i|
11
11
  f = fb.insert
12
12
  f.id = i
@@ -15,6 +15,7 @@ def bench_query(bmk, fb)
15
15
  f.cost = rand(1..100)
16
16
  f.foo = rand(0.0..100.0).round(3)
17
17
  f.bar = rand(100..300)
18
+ f.blue = 1 if rand > 0.5
18
19
  f.seenBy = "User#{i}" if i.even?
19
20
  f.zzz = "Extra#{i}" if (i % 10).zero?
20
21
  end
@@ -25,6 +26,7 @@ def bench_query(bmk, fb)
25
26
  '(gt cost 50)',
26
27
  '(eq title \'Object Thinking 5000\')',
27
28
  '(and (eq foo 42.998) (or (gt bar 200) (absent zzz)))',
29
+ '(and (exists foo) (not (exists blue)))',
28
30
  '(eq id (agg (always) (max id)))',
29
31
  '(join "c<=cost,b<=bar" (eq id (agg (always) (max id))))'
30
32
  ].each do |q|
@@ -120,11 +120,20 @@ module Factbase::IndexedTerm
120
120
  end
121
121
  r
122
122
  when :not
123
- r = @operands.first.predict(maps, params)
123
+ if @idx[key].nil?
124
+ yes = @operands.first.predict(maps, params)
125
+ if yes.nil?
126
+ @idx[key] = { r: nil }
127
+ else
128
+ yes = yes.to_a.to_set
129
+ @idx[key] = { r: maps.to_a.reject { |m| yes.include?(m) } }
130
+ end
131
+ end
132
+ r = @idx[key][:r]
124
133
  if r.nil?
125
134
  nil
126
135
  else
127
- (maps & []) | (maps.to_a - r.to_a)
136
+ (maps & []) | r
128
137
  end
129
138
  end
130
139
  end
@@ -14,7 +14,7 @@ require_relative '../factbase/syntax'
14
14
  # to insert a fact without this property to lead to a runtime error. Here is how:
15
15
  #
16
16
  # fb = Factbase.new
17
- # fb = Factabase::Rules.new(fb, '(exists foo)')
17
+ # fb = Factbase::Rules.new(fb, '(exists foo)')
18
18
  # fb.txn do |fbt|
19
19
  # f = fbt.insert
20
20
  # f.bar = 3 # No exception here
@@ -28,9 +28,9 @@ module Factbase::Aggregates
28
28
  def nth(_fact, maps, _fb)
29
29
  assert_args(2)
30
30
  pos = @operands[0]
31
- raise "An integer expected, but #{pos} provided" unless pos.is_a?(Integer)
31
+ raise "An integer is expected, but #{pos} provided" unless pos.is_a?(Integer)
32
32
  k = @operands[1]
33
- raise "A symbol expected, but #{k} provided" unless k.is_a?(Symbol)
33
+ raise "A symbol is expected, but #{k} provided" unless k.is_a?(Symbol)
34
34
  m = maps[pos]
35
35
  return nil if m.nil?
36
36
  m[k.to_s]
@@ -39,7 +39,7 @@ module Factbase::Aggregates
39
39
  def first(_fact, maps, _fb)
40
40
  assert_args(1)
41
41
  k = @operands[0]
42
- raise "A symbol expected, but #{k} provided" unless k.is_a?(Symbol)
42
+ raise "A symbol is expected, but #{k} provided" unless k.is_a?(Symbol)
43
43
  first = maps[0]
44
44
  return nil if first.nil?
45
45
  first[k.to_s]
@@ -47,7 +47,7 @@ module Factbase::Aggregates
47
47
 
48
48
  def sum(_fact, maps, _fb)
49
49
  k = @operands[0]
50
- raise "A symbol expected, but '#{k}' provided" unless k.is_a?(Symbol)
50
+ raise "A symbol is expected, but '#{k}' provided" unless k.is_a?(Symbol)
51
51
  sum = 0
52
52
  maps.each do |m|
53
53
  vv = m[k.to_s]
@@ -63,9 +63,9 @@ module Factbase::Aggregates
63
63
  def agg(fact, maps, fb)
64
64
  assert_args(2)
65
65
  selector = @operands[0]
66
- raise "A term expected, but '#{selector}' provided" unless selector.is_a?(Factbase::Term)
66
+ raise "A term is expected, but '#{selector}' provided" unless selector.is_a?(Factbase::Term)
67
67
  term = @operands[1]
68
- raise "A term expected, but '#{term}' provided" unless term.is_a?(Factbase::Term)
68
+ raise "A term is expected, but '#{term}' provided" unless term.is_a?(Factbase::Term)
69
69
  subset = fb.query(selector, maps).each(fb, fact).to_a
70
70
  term.evaluate(nil, subset, fb)
71
71
  end
@@ -73,7 +73,7 @@ module Factbase::Aggregates
73
73
  def empty(fact, maps, fb)
74
74
  assert_args(1)
75
75
  term = @operands[0]
76
- raise "A term expected, but '#{term}' provided" unless term.is_a?(Factbase::Term)
76
+ raise "A term is expected, but '#{term}' provided" unless term.is_a?(Factbase::Term)
77
77
  # rubocop:disable Lint/UnreachableLoop
78
78
  fb.query(term, maps).each(fb, fact) do
79
79
  return false
@@ -84,7 +84,7 @@ module Factbase::Aggregates
84
84
 
85
85
  def _best(maps)
86
86
  k = @operands[0]
87
- raise "A symbol expected, but #{k} provided" unless k.is_a?(Symbol)
87
+ raise "A symbol is expected, but #{k} provided" unless k.is_a?(Symbol)
88
88
  best = nil
89
89
  maps.each do |m|
90
90
  vv = m[k.to_s]
@@ -14,7 +14,7 @@ module Factbase::Aliases
14
14
  def as(fact, maps, fb)
15
15
  assert_args(2)
16
16
  a = @operands[0]
17
- raise "A symbol expected as first argument of 'as'" unless a.is_a?(Symbol)
17
+ raise "A symbol is expected as first argument of 'as'" unless a.is_a?(Symbol)
18
18
  vv = _values(1, fact, maps, fb)
19
19
  vv&.each { |v| fact.send(:"#{a}=", v) }
20
20
  true
@@ -23,13 +23,13 @@ module Factbase::Aliases
23
23
  def join(fact, maps, fb)
24
24
  assert_args(2)
25
25
  jumps = @operands[0]
26
- raise "A string expected as first argument of 'join'" unless jumps.is_a?(String)
26
+ raise "A string is expected as first argument of 'join'" unless jumps.is_a?(String)
27
27
  jumps = jumps.split(',')
28
28
  .map(&:strip)
29
29
  .map { |j| j.split('<=').map(&:strip) }
30
30
  .map { |j| j.size == 1 ? [j[0], j[0]] : j }
31
31
  term = @operands[1]
32
- raise "A term expected, but '#{term}' provided" unless term.is_a?(Factbase::Term)
32
+ raise "A term is expected, but '#{term}' provided" unless term.is_a?(Factbase::Term)
33
33
  subset = fb.query(term, maps).each(fb, fact).to_a
34
34
  subset.each do |s|
35
35
  jumps.each do |to, from|
@@ -14,7 +14,7 @@ module Factbase::Debug
14
14
  def traced(fact, maps, fb)
15
15
  assert_args(1)
16
16
  t = @operands[0]
17
- raise "A term expected, but '#{t}' provided" unless t.is_a?(Factbase::Term)
17
+ raise "A term is expected, but '#{t}' provided" unless t.is_a?(Factbase::Term)
18
18
  r = t.evaluate(fact, maps, fb)
19
19
  puts "#{self} -> #{r}"
20
20
  r
@@ -25,12 +25,12 @@ module Factbase::Debug
25
25
  message = @operands[0]
26
26
  unless message.is_a?(String)
27
27
  raise ArgumentError,
28
- "A string expected as first argument of 'assert', but '#{message}' provided"
28
+ "A string is expected as first argument of 'assert', but '#{message}' provided"
29
29
  end
30
30
  t = @operands[1]
31
31
  unless t.is_a?(Factbase::Term)
32
32
  raise ArgumentError,
33
- "A term expected as second argument of 'assert', but '#{t}' provided"
33
+ "A term is expected as second argument of 'assert', but '#{t}' provided"
34
34
  end
35
35
  result = t.evaluate(fact, maps, fb)
36
36
  # Convert result to boolean-like evaluation
@@ -119,6 +119,6 @@ module Factbase::Logical
119
119
  val = val[0] if val.respond_to?(:each)
120
120
  return false if val.nil?
121
121
  return val if val.is_a?(TrueClass) || val.is_a?(FalseClass)
122
- raise "Boolean expected, while #{val.class} received from #{@operands[pos]}"
122
+ raise "Boolean is expected, while #{val.class} received from #{@operands[pos]}"
123
123
  end
124
124
  end
@@ -25,10 +25,10 @@ module Factbase::Strings
25
25
  assert_args(2)
26
26
  str = _values(0, fact, maps, fb)
27
27
  return false if str.nil?
28
- raise 'Exactly one string expected' unless str.size == 1
28
+ raise 'Exactly one string is expected' unless str.size == 1
29
29
  re = _values(1, fact, maps, fb)
30
30
  raise 'Regexp is nil' if re.nil?
31
- raise 'Exactly one regexp expected' unless re.size == 1
31
+ raise 'Exactly one regexp is expected' unless re.size == 1
32
32
  str[0].to_s.match?(re[0])
33
33
  end
34
34
  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.12.0' unless const_defined?(:VERSION)
12
+ VERSION = '0.13.0' unless const_defined?(:VERSION)
13
13
  end
data/lib/factbase.rb CHANGED
@@ -102,8 +102,8 @@ class Factbase
102
102
  #
103
103
  # A fact, when inserted, is empty. It doesn't contain any properties.
104
104
  #
105
- # The operation is thread-safe, meaning that you different threads may
106
- # insert facts parallel without breaking the consistency of the factbase.
105
+ # The operation is thread-safe, meaning that different threads may
106
+ # insert facts in parallel without breaking the consistency of the factbase.
107
107
  #
108
108
  # @return [Factbase::Fact] The fact just inserted
109
109
  def insert
@@ -177,7 +177,12 @@ class Factbase
177
177
  taped = Factbase::Taped.new(before)
178
178
  catch :commit do
179
179
  require_relative 'factbase/light'
180
- yield Factbase::Light.new(Factbase.new(taped))
180
+ commit = false
181
+ catch :rollback do
182
+ yield Factbase::Light.new(Factbase.new(taped))
183
+ commit = true
184
+ end
185
+ return 0 unless commit
181
186
  rescue Factbase::Rollback
182
187
  return 0
183
188
  end
@@ -20,7 +20,7 @@ class TestDebug < Factbase::Test
20
20
 
21
21
  def test_traced_raises
22
22
  e = assert_raises(StandardError) { Factbase::Term.new(:traced, ['foo']).evaluate(fact, [], Factbase.new) }
23
- assert_match(/A term expected, but 'foo' provided/, e.message)
23
+ assert_match(/A term is expected, but 'foo' provided/, e.message)
24
24
  end
25
25
 
26
26
  def test_traced_raises_when_too_many_args
@@ -81,7 +81,7 @@ class TestDebug < Factbase::Test
81
81
  assert_raises(StandardError) do
82
82
  Factbase::Term.new(:assert, [123, Factbase::Term.new(:gt, [:foo, 0])]).evaluate(fact, [], Factbase.new)
83
83
  end
84
- assert_match(/A string expected as first argument of 'assert', but '123' provided/, e.message)
84
+ assert_match(/A string is expected as first argument of 'assert', but '123' provided/, e.message)
85
85
  end
86
86
 
87
87
  def test_assert_raises_when_second_arg_not_term
@@ -89,7 +89,7 @@ class TestDebug < Factbase::Test
89
89
  assert_raises(StandardError) do
90
90
  Factbase::Term.new(:assert, %w[message not_a_term]).evaluate(fact, [], Factbase.new)
91
91
  end
92
- assert_match(/A term expected as second argument of 'assert', but 'not_a_term' provided/, e.message)
92
+ assert_match(/A term is expected as second argument of 'assert', but 'not_a_term' provided/, e.message)
93
93
  end
94
94
 
95
95
  def test_assert_raises_when_too_few_args
@@ -486,6 +486,15 @@ class TestFactbase < Factbase::Test
486
486
  assert_equal(1, fb.size)
487
487
  end
488
488
 
489
+ def test_rolls_back
490
+ fb = Factbase.new
491
+ fb.txn do |fbt|
492
+ fbt.insert.foo = 1
493
+ throw :rollback
494
+ end
495
+ assert_equal(0, fb.size)
496
+ end
497
+
489
498
  def test_get_raise_for_empty_fact
490
499
  fb = Factbase.new
491
500
  fb.txn do |fbt|
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.12.0
4
+ version: 0.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko