factbase 0.7.1 → 0.7.3

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: cbdaf8e1b301fbb2aa6152156c36237f3a619861f100531baf422eceef38c376
4
- data.tar.gz: b41709f25cd6652fd2e51e4550a30b99f453c9965ff14026241c7d8a9c894352
3
+ metadata.gz: 1d14f3343341431bb4e9828abfc7f2c0bdda83d2c484cf6df1dbbc49b9b442ca
4
+ data.tar.gz: 48e762c8d0c94102c8e757469c177698db655714128ae315d53696421fecce04
5
5
  SHA512:
6
- metadata.gz: 8046597926096add3f76ac9e4741d1e34eac946ddf7189fa5b3df224fb4b345d010a08feb685725083c641d883d85d4e3bd11a4593c47d2722f44d58f46f3f8f
7
- data.tar.gz: e5a33af012a4392474e2d4bdf184a5dbc995e2fa95ed4bb270b0a2b0eddedc9630aa372a72404f0bd90996ac6aab03b278b2a791be898adf084ef1dbb4967cf0
6
+ metadata.gz: 584de94a190d5a07693d788f9d36db7899b6535e353315d41a864184384f62825a0b6c0885a23a9f7332621bb96891362fdc090f2f6e11dac7663af9b5c35fef
7
+ data.tar.gz: 78b784ccb6d2800dd3aa3711dd74cd43ec801a8343f87b695d47d6d9ae195d78e9350c57900dac388304da2a91f17b0b2bbf16ba8475fb794aea000e22a4c517
data/benchmarks/simple.rb CHANGED
@@ -84,7 +84,7 @@ def txn(fb, scenario, &block)
84
84
  {
85
85
  title: "txn: `#{scenario}`",
86
86
  time: time.real,
87
- details: "modified #{modified} facts"
87
+ details: modified.to_s
88
88
  }
89
89
  end
90
90
 
@@ -26,6 +26,10 @@ class Factbase::Churn
26
26
  @inserted.zero? && @deleted.zero? && @added.zero?
27
27
  end
28
28
 
29
+ def to_i
30
+ @inserted + @deleted + @added
31
+ end
32
+
29
33
  def append(ins, del, add)
30
34
  @mutex.synchronize do
31
35
  @inserted += ins
@@ -50,7 +50,7 @@ class Factbase::Looged
50
50
  if rollback
51
51
  @loog.debug("Txn ##{id} rolled back in #{start.ago}")
52
52
  else
53
- @loog.debug("Txn ##{id} #{r.positive? ? 'modified' : 'didn\'t touch'} the factbase in #{start.ago}")
53
+ @loog.debug("Txn ##{id} touched #{r} in #{start.ago}")
54
54
  end
55
55
  r
56
56
  end
@@ -16,9 +16,9 @@ class Factbase::Taped
16
16
 
17
17
  def initialize(origin, lookup: {})
18
18
  @origin = origin
19
- @inserted = []
20
- @deleted = []
21
- @added = []
19
+ @inserted = Set.new
20
+ @deleted = Set.new
21
+ @added = Set.new
22
22
  @lookup = lookup
23
23
  end
24
24
 
@@ -37,7 +37,7 @@ class Factbase::Taped
37
37
  # rubocop:disable Lint/HashCompareByIdentity
38
38
  @lookup[map.object_id] = map
39
39
  # rubocop:enable Lint/HashCompareByIdentity
40
- @inserted.append(map.object_id)
40
+ @inserted.add(map.object_id)
41
41
  end
42
42
 
43
43
  def each
@@ -54,7 +54,7 @@ class Factbase::Taped
54
54
  r = yield m
55
55
  if r
56
56
  @lookup.delete(m.object_id)
57
- @deleted.append(m.object_id)
57
+ @deleted.add(m.object_id)
58
58
  end
59
59
  r
60
60
  end
@@ -75,7 +75,7 @@ class Factbase::Taped
75
75
 
76
76
  def []=(key, value)
77
77
  @origin[key] = value
78
- @added.append(@origin.object_id)
78
+ @added.add(@origin.object_id)
79
79
  end
80
80
  end
81
81
 
@@ -91,22 +91,25 @@ class Factbase::Taped
91
91
  @origin.each(&)
92
92
  end
93
93
 
94
+ def [](key)
95
+ @origin[key]
96
+ end
97
+
94
98
  def to_a
95
99
  @origin.to_a
96
100
  end
97
101
 
98
102
  def any?(&)
99
- p 1
100
103
  @origin.any?(&)
101
104
  end
102
105
 
103
106
  def <<(item)
104
- @added.append(@oid)
107
+ @added.add(@oid)
105
108
  @origin << (item)
106
109
  end
107
110
 
108
111
  def uniq!
109
- @added.append(@oid)
112
+ @added.add(@oid)
110
113
  @origin.uniq!
111
114
  end
112
115
  end
data/lib/factbase.rb CHANGED
@@ -64,7 +64,7 @@ require 'yaml'
64
64
  # License:: MIT
65
65
  class Factbase
66
66
  # Current version of the gem (changed by .rultor.yml on every release)
67
- VERSION = '0.7.1'
67
+ VERSION = '0.7.3'
68
68
 
69
69
  # An exception that may be thrown in a transaction, to roll it back.
70
70
  class Rollback < StandardError; end
@@ -147,7 +147,7 @@ class Factbase
147
147
  # A the end of this script, the factbase will be empty. No facts will
148
148
  # inserted and all changes that happened in the block will be rolled back.
149
149
  #
150
- # @return [Integer] How many facts have been changed (zero if rolled back)
150
+ # @return [Factbase::Churn] How many facts have been changed (zero if rolled back)
151
151
  def txn
152
152
  pairs = {}
153
153
  lookup = {}
@@ -170,13 +170,14 @@ class Factbase
170
170
  rescue Factbase::Rollback
171
171
  return 0
172
172
  end
173
- modified = []
173
+ require_relative 'factbase/churn'
174
+ churn = Factbase::Churn.new
174
175
  @mutex.synchronize do
175
176
  taped.inserted.each do |oid|
176
177
  b = taped.find_by_object_id(oid)
177
178
  next if b.nil?
178
179
  @maps << b
179
- modified << oid
180
+ churn.append(1, 0, 0)
180
181
  end
181
182
  garbage = []
182
183
  taped.added.each do |oid|
@@ -184,15 +185,15 @@ class Factbase
184
185
  next if b.nil?
185
186
  garbage << pairs[oid]
186
187
  @maps << b
187
- modified << oid
188
+ churn.append(0, 0, 1)
188
189
  end
189
190
  taped.deleted.each do |oid|
190
191
  garbage << pairs[oid]
191
- modified << oid
192
+ churn.append(0, 1, 0)
192
193
  end
193
194
  @maps.delete_if { |m| garbage.include?(m.object_id) }
194
195
  end
195
- modified.uniq.count
196
+ churn
196
197
  end
197
198
 
198
199
  # Export it into a chain of bytes.
@@ -17,6 +17,7 @@ class TestChurn < Minitest::Test
17
17
  c = Factbase::Churn.new
18
18
  c.append(1, 2, 3)
19
19
  assert_equal('1i/2d/3a', c.to_s)
20
+ assert_equal(6, c.to_i)
20
21
  end
21
22
 
22
23
  def test_checks_for_zero
@@ -45,7 +45,7 @@ class TestLooged < Minitest::Test
45
45
  end
46
46
  )
47
47
  assert_equal(1, fb.size)
48
- assert_includes(log.to_s, 'modified', log)
48
+ assert_includes(log.to_s, 'touched', log)
49
49
  end
50
50
 
51
51
  def test_with_txn_rollback
@@ -61,16 +61,16 @@ class TestLooged < Minitest::Test
61
61
  log = Loog::Buffer.new
62
62
  fb = Factbase::Looged.new(Factbase.new, log)
63
63
  fb.insert.foo = 1
64
- assert_equal(0, fb.txn { |fbt| fbt.query('(always)').each.to_a }, log)
65
- assert_equal(1, fb.txn { |fbt| fbt.query('(always)').each.to_a[0].foo = 42 })
66
- assert_includes(log.to_s, 'modified', log)
64
+ assert_equal(0, fb.txn { |fbt| fbt.query('(always)').each.to_a }.to_i, log)
65
+ assert_equal(1, fb.txn { |fbt| fbt.query('(always)').each.to_a[0].foo = 42 }.to_i)
66
+ assert_includes(log.to_s, 'touched', log)
67
67
  end
68
68
 
69
69
  def test_with_empty_txn
70
70
  log = Loog::Buffer.new
71
71
  fb = Factbase::Looged.new(Factbase.new, log)
72
- assert_equal(0, fb.txn { |fbt| fbt.query('(always)').each.to_a })
73
- assert_includes(log.to_s, 'didn\'t touch', log)
72
+ assert_equal(0, fb.txn { |fbt| fbt.query('(always)').each.to_a }.to_i)
73
+ assert_includes(log.to_s, 'touched', log)
74
74
  end
75
75
 
76
76
  def test_returns_int
@@ -26,10 +26,11 @@ class TestQuery < Minitest::Test
26
26
  end
27
27
 
28
28
  def test_complex_parsing
29
- maps = []
30
- maps << { 'num' => [42], 'name' => ['Jeff'] }
31
- maps << { 'pi' => [3.14], 'num' => [42, 66, 0], 'name' => ['peter'] }
32
- maps << { 'time' => [Time.now - 100], 'num' => [0], 'hi' => [4], 'nome' => ['Walter'] }
29
+ maps = [
30
+ { 'num' => [42], 'name' => ['Jeff'] },
31
+ { 'pi' => [3.14], 'num' => [42, 66, 0], 'name' => ['peter'] },
32
+ { 'time' => [Time.now - 100], 'num' => [0], 'hi' => [4], 'nome' => ['Walter'] }
33
+ ]
33
34
  {
34
35
  '(eq num 444)' => 0,
35
36
  '(eq hi 4)' => 1,
@@ -71,7 +72,11 @@ class TestQuery < Minitest::Test
71
72
  "(or (eq num +66) (lt time #{(Time.now - 200).utc.iso8601}))" => 1,
72
73
  '(eq 3 (agg (eq num $num) (count)))' => 1
73
74
  }.each do |q, r|
74
- assert_equal(r, Factbase::Query.new(Factbase.new, maps, Mutex.new, q).each.to_a.size, q)
75
+ fb = Factbase.new(maps)
76
+ assert_equal(r, fb.query(q).each.to_a.size, q)
77
+ fb.txn do |fbt|
78
+ assert_equal(r, fbt.query(q).each.to_a.size, q)
79
+ end
75
80
  end
76
81
  end
77
82
 
@@ -84,19 +89,21 @@ class TestQuery < Minitest::Test
84
89
  end
85
90
 
86
91
  def test_simple_deleting
87
- maps = []
88
- maps << { 'foo' => [42] }
89
- maps << { 'bar' => [4, 5] }
90
- maps << { 'bar' => [5] }
92
+ maps = [
93
+ { 'foo' => [42] },
94
+ { 'bar' => [4, 5] },
95
+ { 'bar' => [5] }
96
+ ]
91
97
  q = Factbase::Query.new(Factbase.new, maps, Mutex.new, '(eq bar 5)')
92
98
  assert_equal(2, q.delete!)
93
99
  assert_equal(1, maps.size)
94
100
  end
95
101
 
96
102
  def test_reading_one
97
- maps = []
98
- maps << { 'foo' => [42] }
99
- maps << { 'bar' => [4, 5] }
103
+ maps = [
104
+ { 'foo' => [42] },
105
+ { 'bar' => [4, 5] }
106
+ ]
100
107
  {
101
108
  '(agg (exists foo) (first foo))' => [42],
102
109
  '(agg (exists z) (first z))' => nil,
@@ -114,10 +121,11 @@ class TestQuery < Minitest::Test
114
121
  end
115
122
 
116
123
  def test_deleting_nothing
117
- maps = []
118
- maps << { 'foo' => [42] }
119
- maps << { 'bar' => [4, 5] }
120
- maps << { 'bar' => [5] }
124
+ maps = [
125
+ { 'foo' => [42] },
126
+ { 'bar' => [4, 5] },
127
+ { 'bar' => [5] }
128
+ ]
121
129
  q = Factbase::Query.new(Factbase.new, maps, Mutex.new, '(never)')
122
130
  assert_equal(0, q.delete!)
123
131
  assert_equal(3, maps.size)
@@ -144,9 +152,10 @@ class TestQuery < Minitest::Test
144
152
  end
145
153
 
146
154
  def test_with_params
147
- maps = []
148
- maps << { 'foo' => [42] }
149
- maps << { 'foo' => [17] }
155
+ maps = [
156
+ { 'foo' => [42] },
157
+ { 'foo' => [17] }
158
+ ]
150
159
  found = 0
151
160
  Factbase::Query.new(Factbase.new, maps, Mutex.new, '(eq foo $bar)').each(bar: [42]) do
152
161
  found += 1
@@ -157,14 +166,12 @@ class TestQuery < Minitest::Test
157
166
  end
158
167
 
159
168
  def test_with_nil_alias
160
- maps = []
161
- maps << { 'foo' => [42] }
169
+ maps = [{ 'foo' => [42] }]
162
170
  assert_nil(Factbase::Query.new(Factbase.new, maps, Mutex.new, '(as bar (plus xxx 3))').each.to_a[0]['bar'])
163
171
  end
164
172
 
165
173
  def test_get_all_properties
166
- maps = []
167
- maps << { 'foo' => [42] }
174
+ maps = [{ 'foo' => [42] }]
168
175
  f = Factbase::Query.new(Factbase.new, maps, Mutex.new, '(always)').each.to_a[0]
169
176
  assert_includes(f.all_properties, 'foo')
170
177
  end
@@ -33,4 +33,14 @@ class TestTaped < Minitest::Test
33
33
  assert_equal(1, t.added.size)
34
34
  assert_equal(h.object_id, t.added.first)
35
35
  end
36
+
37
+ def test_tracks_addition_uniquely
38
+ h = { f: 5 }
39
+ t = Factbase::Taped.new([h])
40
+ t.each do |m|
41
+ m[:bar] = 66
42
+ m[:foo] = 77
43
+ end
44
+ assert_equal(1, t.added.size)
45
+ end
36
46
  end
@@ -104,11 +104,11 @@ class TestFactbase < Minitest::Test
104
104
 
105
105
  def test_txn_returns_boolean
106
106
  fb = Factbase.new
107
- assert_equal(1, fb.txn(&:insert))
108
- assert_equal(1, fb.txn { |fbt| fbt.insert.bar = 42 })
109
- assert_equal(0, fb.txn { |fbt| fbt.query('(always)').each.to_a })
110
- assert_equal(2, fb.txn { |fbt| fbt.query('(always)').each { |f| f.hello = 33 } })
111
- assert_equal(1, fb.txn { |fbt| fbt.query('(always)').each.to_a[0].zzz = 33 })
107
+ assert_equal(1, fb.txn(&:insert).to_i)
108
+ assert_equal(1, fb.txn { |fbt| fbt.insert.bar = 42 }.to_i)
109
+ assert_equal(0, fb.txn { |fbt| fbt.query('(always)').each.to_a }.to_i)
110
+ assert_equal(2, fb.txn { |fbt| fbt.query('(always)').each { |f| f.hello = 33 } }.to_i)
111
+ assert_equal(1, fb.txn { |fbt| fbt.query('(always)').each.to_a[0].zzz = 33 }.to_i)
112
112
  end
113
113
 
114
114
  def test_appends_in_txn
@@ -137,11 +137,12 @@ class TestFactbase < Minitest::Test
137
137
 
138
138
  def test_deals_with_arrays_in_txn
139
139
  fb = Factbase.new
140
- f = fb.insert
141
- f.foo = 1
142
- f.foo = 2
140
+ n = fb.insert
141
+ n.foo = 1
142
+ n.foo = 2
143
143
  fb.txn do |fbt|
144
- assert_equal(1, fbt.query('(gt foo 0)').each.to_a.size)
144
+ f = fbt.query('(gt foo 0)').each.to_a.first
145
+ assert_equal(1, f.foo)
145
146
  end
146
147
  end
147
148
 
@@ -197,9 +198,13 @@ class TestFactbase < Minitest::Test
197
198
 
198
199
  def test_txn_inside_query
199
200
  fb = Factbase.new
200
- fb.insert.foo = 42
201
+ fact = fb.insert
202
+ fact.foo = 42
203
+ fact.foo = 555
201
204
  fb.query('(exists foo)').each do |f|
202
205
  fb.txn do |fbt|
206
+ assert_equal(42, fb.query('(always)').each.to_a.first.foo)
207
+ assert_equal(42, fb.query('(always)').each.to_a.first['foo'].first)
203
208
  fbt.insert.bar = 33
204
209
  end
205
210
  f.xyz = 1
@@ -207,15 +212,48 @@ class TestFactbase < Minitest::Test
207
212
  assert_equal(1, fb.query('(exists xyz)').each.to_a.size)
208
213
  end
209
214
 
215
+ def test_evals_complex_txn
216
+ fb = Factbase.new
217
+ n = fb.insert
218
+ n.foo = 42
219
+ n.bar = 555
220
+ fb.txn do |fbt|
221
+ fbt.insert.foo = 333
222
+ fbt.insert.bar = 333
223
+ fbt.query('(eq bar 555)').delete!
224
+ fbt.insert.bar = 555
225
+ fbt.query('(eq bar 777)').delete! # nothing to be deleted
226
+ n1 = fbt.insert
227
+ n1.bar = 9
228
+ n1.bar = 99
229
+ end
230
+ assert_equal(4, fb.query('(always)').each.to_a.size)
231
+ end
232
+
210
233
  def test_txn_with_rollback
211
234
  fb = Factbase.new
235
+ n = fb.insert
236
+ n.bar = 55
212
237
  modified =
213
238
  fb.txn do |fbt|
214
239
  fbt.insert.bar = 33
240
+ fbt.query('(eq bar 55)').each.to_a.first.boom = 44
215
241
  raise Factbase::Rollback
216
242
  end
217
- assert_equal(0, modified)
218
- assert_equal(0, fb.query('(always)').each.to_a.size)
243
+ assert_equal(0, modified.to_i)
244
+ assert_equal(1, fb.query('(always)').each.to_a.size)
245
+ assert_equal(0, fb.query('(exists boom)').each.to_a.size)
246
+ end
247
+
248
+ def test_modifies_existing_fact_in_txn
249
+ fb = Factbase.new
250
+ n = fb.insert
251
+ n.foo = 1
252
+ fb.txn do |fbt|
253
+ f = fbt.query('(always)').each.to_a.first
254
+ f.bar = 2
255
+ end
256
+ assert_equal(1, fb.query('(eq foo 1)').each.to_a.size)
219
257
  end
220
258
 
221
259
  def test_simple_concurrent_inserts
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.7.1
4
+ version: 0.7.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko