factbase 0.0.46 → 0.0.47
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/lib/factbase/fact.rb +4 -5
- data/lib/factbase/flatten.rb +44 -0
- data/lib/factbase/looged.rb +2 -2
- data/lib/factbase/to_json.rb +2 -2
- data/lib/factbase/to_xml.rb +2 -2
- data/lib/factbase/to_yaml.rb +2 -2
- data/lib/factbase.rb +6 -6
- data/test/factbase/test_fact.rb +12 -1
- data/test/factbase/test_flatten.rb +39 -0
- data/test/factbase/test_looged.rb +9 -0
- data/test/factbase/test_to_yaml.rb +3 -3
- data/test/test_factbase.rb +48 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3b16ed7e90c154dfde3992d7a812c124caf6492c4f3600da01e93069e82be50f
|
4
|
+
data.tar.gz: 8cc18bca8d1a1eb3b52fdb314b38f87818796faf93d05395764d5968aa615a93
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9376cb5f9cecf2fc5fb47296512285b8105b08fbe9683f3722995aac7bab5440a417d443f378dfc62c300f3b79d136f6365835843aabb3559eaf09f203404880
|
7
|
+
data.tar.gz: 59f4f461487d02b604b0614d571bbc10405a15c4d86ec92dedd8633d5b12a01bd92d546622aad23bd3d4caf65ff4a27ce7a18a6b1a65c6e2d54cbc0874f41d91
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Single-Table NoSQL In-Memory Database
|
1
|
+
# Single-Table NoSQL-ish In-Memory Database
|
2
2
|
|
3
3
|
[![DevOps By Rultor.com](http://www.rultor.com/b/yegor256/factbase)](http://www.rultor.com/p/yegor256/factbase)
|
4
4
|
[![We recommend RubyMine](https://www.elegantobjects.org/rubymine.svg)](https://www.jetbrains.com/ruby/)
|
@@ -18,7 +18,7 @@ It is possible to delete a fact, but impossible to delete a property
|
|
18
18
|
from a fact.
|
19
19
|
|
20
20
|
**ATTENTION**: The current implemention is naive and,
|
21
|
-
because of that, very slow
|
21
|
+
because of that, **very slow**. I will be very happy
|
22
22
|
if you suggest a better implementation without the change of the interface.
|
23
23
|
The `Factbase::query()` method is what mostly needs performance optimization:
|
24
24
|
currently it simply iterates through all facts in the factbase in order
|
data/lib/factbase/fact.rb
CHANGED
@@ -72,12 +72,11 @@ class Factbase::Fact
|
|
72
72
|
before = @map[kk]
|
73
73
|
return if before == v
|
74
74
|
if before.nil?
|
75
|
-
@map[kk] = v
|
76
|
-
|
75
|
+
@map[kk] = [v]
|
76
|
+
else
|
77
|
+
@map[kk] << v
|
78
|
+
@map[kk].uniq!
|
77
79
|
end
|
78
|
-
@map[kk] = [@map[kk]] unless @map[kk].is_a?(Array)
|
79
|
-
@map[kk] << v
|
80
|
-
@map[kk].uniq!
|
81
80
|
end
|
82
81
|
nil
|
83
82
|
elsif k == '[]'
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright (c) 2024 Yegor Bugayenko
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the 'Software'), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in all
|
13
|
+
# copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
# SOFTWARE.
|
22
|
+
|
23
|
+
require_relative '../factbase'
|
24
|
+
|
25
|
+
# Make maps suitable for printing.
|
26
|
+
#
|
27
|
+
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
28
|
+
# Copyright:: Copyright (c) 2024 Yegor Bugayenko
|
29
|
+
# License:: MIT
|
30
|
+
class Factbase::Flatten
|
31
|
+
# Constructor.
|
32
|
+
def initialize(maps)
|
33
|
+
@maps = maps
|
34
|
+
end
|
35
|
+
|
36
|
+
# Improve the maps.
|
37
|
+
# @return [Array<HashMap>] The hashmaps, but improved
|
38
|
+
def it
|
39
|
+
@maps
|
40
|
+
.sort_by { |m| m[@sorter] }
|
41
|
+
.map { |m| m.sort.to_h }
|
42
|
+
.map { |m| m.transform_values { |v| v.size == 1 ? v[0] : v } }
|
43
|
+
end
|
44
|
+
end
|
data/lib/factbase/looged.rb
CHANGED
@@ -55,9 +55,9 @@ class Factbase::Looged
|
|
55
55
|
def txn(this = self, &)
|
56
56
|
before = @fb.size
|
57
57
|
tail = nil
|
58
|
-
r = @fb.txn(this) do |
|
58
|
+
r = @fb.txn(this) do |fbt|
|
59
59
|
tail = Factbase::Looged.elapsed do
|
60
|
-
yield
|
60
|
+
yield fbt
|
61
61
|
end
|
62
62
|
end
|
63
63
|
@loog.debug("Txn #{r ? 'modified' : 'didn\'t touch'} #{before} facts #{tail}")
|
data/lib/factbase/to_json.rb
CHANGED
@@ -22,6 +22,7 @@
|
|
22
22
|
|
23
23
|
require 'json'
|
24
24
|
require_relative '../factbase'
|
25
|
+
require_relative '../factbase/flatten'
|
25
26
|
|
26
27
|
# Factbase to JSON converter.
|
27
28
|
#
|
@@ -44,7 +45,6 @@ class Factbase::ToJSON
|
|
44
45
|
# Convert the entire factbase into JSON.
|
45
46
|
# @return [String] The factbase in JSON format
|
46
47
|
def json
|
47
|
-
|
48
|
-
maps.sort_by { |m| m[@sorter] }.map { |m| m.sort.to_h }.to_json
|
48
|
+
Factbase::Flatten.new(Marshal.load(@fb.export)).it.to_json
|
49
49
|
end
|
50
50
|
end
|
data/lib/factbase/to_xml.rb
CHANGED
@@ -23,6 +23,7 @@
|
|
23
23
|
require 'nokogiri'
|
24
24
|
require 'time'
|
25
25
|
require_relative '../factbase'
|
26
|
+
require_relative '../factbase/flatten'
|
26
27
|
|
27
28
|
# Factbase to XML converter.
|
28
29
|
#
|
@@ -46,14 +47,13 @@ class Factbase::ToXML
|
|
46
47
|
# @return [String] The factbase in XML format
|
47
48
|
def xml
|
48
49
|
bytes = @fb.export
|
49
|
-
maps = Marshal.load(bytes)
|
50
50
|
meta = {
|
51
51
|
version: Factbase::VERSION,
|
52
52
|
size: bytes.size
|
53
53
|
}
|
54
54
|
Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
|
55
55
|
xml.fb(meta) do
|
56
|
-
|
56
|
+
Factbase::Flatten.new(Marshal.load(bytes)).it.each do |m|
|
57
57
|
xml.f_ do
|
58
58
|
m.sort.to_h.each do |k, vv|
|
59
59
|
if vv.is_a?(Array)
|
data/lib/factbase/to_yaml.rb
CHANGED
@@ -22,6 +22,7 @@
|
|
22
22
|
|
23
23
|
require 'yaml'
|
24
24
|
require_relative '../factbase'
|
25
|
+
require_relative '../factbase/flatten'
|
25
26
|
|
26
27
|
# Factbase to YAML converter.
|
27
28
|
#
|
@@ -44,7 +45,6 @@ class Factbase::ToYAML
|
|
44
45
|
# Convert the entire factbase into YAML.
|
45
46
|
# @return [String] The factbase in YAML format
|
46
47
|
def yaml
|
47
|
-
|
48
|
-
YAML.dump({ 'facts' => maps.sort_by { |m| m[@sorter] }.map { |m| m.sort.to_h } })
|
48
|
+
YAML.dump(Factbase::Flatten.new(Marshal.load(@fb.export)).it)
|
49
49
|
end
|
50
50
|
end
|
data/lib/factbase.rb
CHANGED
@@ -79,7 +79,7 @@ require 'yaml'
|
|
79
79
|
# License:: MIT
|
80
80
|
class Factbase
|
81
81
|
# Current version of the gem (changed by .rultor.yml on every release)
|
82
|
-
VERSION = '0.0.
|
82
|
+
VERSION = '0.0.47'
|
83
83
|
|
84
84
|
# An exception that may be thrown in a transaction, to roll it back.
|
85
85
|
class Rollback < StandardError; end
|
@@ -91,10 +91,10 @@ class Factbase
|
|
91
91
|
@mutex = Mutex.new
|
92
92
|
end
|
93
93
|
|
94
|
-
# Make a duplicate of this factbase.
|
94
|
+
# Make a deep duplicate of this factbase.
|
95
95
|
# @return [Factbase] A new factbase
|
96
96
|
def dup
|
97
|
-
Factbase.new(@maps.dup)
|
97
|
+
Factbase.new(@maps.map { |m| m.transform_values(&:dup) })
|
98
98
|
end
|
99
99
|
|
100
100
|
# Size.
|
@@ -175,9 +175,9 @@ class Factbase
|
|
175
175
|
@maps << {}
|
176
176
|
modified = true
|
177
177
|
end
|
178
|
-
m.each do |k,
|
179
|
-
next if @maps[i][k] ==
|
180
|
-
@maps[i][k] =
|
178
|
+
m.each do |k, vv|
|
179
|
+
next if @maps[i][k] == vv
|
180
|
+
@maps[i][k] = vv
|
181
181
|
modified = true
|
182
182
|
end
|
183
183
|
end
|
data/test/factbase/test_fact.rb
CHANGED
@@ -29,6 +29,17 @@ require_relative '../../lib/factbase/fact'
|
|
29
29
|
# Copyright:: Copyright (c) 2024 Yegor Bugayenko
|
30
30
|
# License:: MIT
|
31
31
|
class TestFact < Minitest::Test
|
32
|
+
def test_injects_data_correctly
|
33
|
+
map = {}
|
34
|
+
f = Factbase::Fact.new(Mutex.new, map)
|
35
|
+
f.foo = 1
|
36
|
+
f.bar = 2
|
37
|
+
f.bar = 3
|
38
|
+
assert_equal(2, map.size)
|
39
|
+
assert_equal([1], map['foo'])
|
40
|
+
assert_equal([2, 3], map['bar'])
|
41
|
+
end
|
42
|
+
|
32
43
|
def test_simple_resetting
|
33
44
|
map = {}
|
34
45
|
f = Factbase::Fact.new(Mutex.new, map)
|
@@ -89,7 +100,7 @@ class TestFact < Minitest::Test
|
|
89
100
|
f = Factbase::Fact.new(Mutex.new, map)
|
90
101
|
f.foo = 42
|
91
102
|
f.foo = 42
|
92
|
-
assert_equal(42, map['foo'])
|
103
|
+
assert_equal([42], map['foo'])
|
93
104
|
end
|
94
105
|
|
95
106
|
def test_time_in_utc
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright (c) 2024 Yegor Bugayenko
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the 'Software'), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in all
|
13
|
+
# copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
# SOFTWARE.
|
22
|
+
|
23
|
+
require 'minitest/autorun'
|
24
|
+
require_relative '../../lib/factbase'
|
25
|
+
require_relative '../../lib/factbase/flatten'
|
26
|
+
|
27
|
+
# Test.
|
28
|
+
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
29
|
+
# Copyright:: Copyright (c) 2024 Yegor Bugayenko
|
30
|
+
# License:: MIT
|
31
|
+
class TestFlatten < Minitest::Test
|
32
|
+
def test_mapping
|
33
|
+
maps = [{ 'b' => [42] }, { 'a' => 33 }, { 'c' => %w[hey you] }]
|
34
|
+
to = Factbase::Flatten.new(maps).it
|
35
|
+
assert(33, to[0]['a'])
|
36
|
+
assert(42, to[1]['b'])
|
37
|
+
assert(2, to[2]['c'].size)
|
38
|
+
end
|
39
|
+
end
|
@@ -57,6 +57,15 @@ class TestLooged < Minitest::Test
|
|
57
57
|
assert(log.to_s.include?('modified'), log)
|
58
58
|
end
|
59
59
|
|
60
|
+
def test_with_modifying_txn
|
61
|
+
log = Loog::Buffer.new
|
62
|
+
fb = Factbase::Looged.new(Factbase.new, log)
|
63
|
+
fb.insert.foo = 1
|
64
|
+
assert(!fb.txn { |fbt| fbt.query('(always)').each.to_a }, log)
|
65
|
+
assert(fb.txn { |fbt| fbt.query('(always)').each.to_a[0].foo = 42 }, log)
|
66
|
+
assert(log.to_s.include?('modified'), log)
|
67
|
+
end
|
68
|
+
|
60
69
|
def test_with_empty_txn
|
61
70
|
log = Loog::Buffer.new
|
62
71
|
fb = Factbase::Looged.new(Factbase.new, log)
|
@@ -39,9 +39,9 @@ class TestToYAML < Minitest::Test
|
|
39
39
|
fb.insert._id = 2
|
40
40
|
to = Factbase::ToYAML.new(fb)
|
41
41
|
yaml = YAML.load(to.yaml)
|
42
|
-
assert_equal(2, yaml
|
43
|
-
assert_equal(42, yaml[
|
44
|
-
assert_equal(256, yaml[
|
42
|
+
assert_equal(2, yaml.size)
|
43
|
+
assert_equal(42, yaml[0]['foo'][0])
|
44
|
+
assert_equal(256, yaml[0]['foo'][1])
|
45
45
|
end
|
46
46
|
|
47
47
|
def test_sorts_keys
|
data/test/test_factbase.rb
CHANGED
@@ -23,12 +23,31 @@
|
|
23
23
|
require 'minitest/autorun'
|
24
24
|
require 'loog'
|
25
25
|
require_relative '../lib/factbase'
|
26
|
+
require_relative '../lib/factbase/rules'
|
27
|
+
require_relative '../lib/factbase/inv'
|
28
|
+
require_relative '../lib/factbase/pre'
|
29
|
+
require_relative '../lib/factbase/looged'
|
26
30
|
|
27
31
|
# Factbase main module test.
|
28
32
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
29
33
|
# Copyright:: Copyright (c) 2024 Yegor Bugayenko
|
30
34
|
# License:: MIT
|
31
35
|
class TestFactbase < Minitest::Test
|
36
|
+
def test_injects_data_correctly
|
37
|
+
maps = []
|
38
|
+
fb = Factbase.new(maps)
|
39
|
+
fb.insert
|
40
|
+
f = fb.insert
|
41
|
+
f.foo = 1
|
42
|
+
f.bar = 2
|
43
|
+
f.bar = 3
|
44
|
+
assert_equal(2, maps.size)
|
45
|
+
assert_equal(0, maps[0].size)
|
46
|
+
assert_equal(2, maps[1].size)
|
47
|
+
assert_equal([1], maps[1]['foo'])
|
48
|
+
assert_equal([2, 3], maps[1]['bar'])
|
49
|
+
end
|
50
|
+
|
32
51
|
def test_simple_setting
|
33
52
|
fb = Factbase.new
|
34
53
|
fb.insert
|
@@ -44,6 +63,22 @@ class TestFactbase < Minitest::Test
|
|
44
63
|
assert_equal(2, fb.size)
|
45
64
|
end
|
46
65
|
|
66
|
+
def test_modify_via_query
|
67
|
+
fb = Factbase.new
|
68
|
+
fb.insert.bar = 1
|
69
|
+
fb.query('(exists bar)').each do |f|
|
70
|
+
f.bar = 42
|
71
|
+
assert_equal([1, 42], f['bar'])
|
72
|
+
end
|
73
|
+
found = 0
|
74
|
+
fb.query('(always)').each do |f|
|
75
|
+
assert_equal([1, 42], f['bar'])
|
76
|
+
found += 1
|
77
|
+
end
|
78
|
+
assert_equal(1, found)
|
79
|
+
assert_equal([1, 42], fb.query('(always)').each.to_a[0]['bar'])
|
80
|
+
end
|
81
|
+
|
47
82
|
def test_serialize_and_deserialize
|
48
83
|
f1 = Factbase.new
|
49
84
|
f2 = Factbase.new
|
@@ -78,6 +113,8 @@ class TestFactbase < Minitest::Test
|
|
78
113
|
assert(fb.txn(&:insert).is_a?(TrueClass))
|
79
114
|
assert(fb.txn { |fbt| fbt.insert.bar = 42 })
|
80
115
|
assert(!fb.txn { |fbt| fbt.query('(always)').each.to_a })
|
116
|
+
assert(fb.txn { |fbt| fbt.query('(always)').each { |f| f.hello = 33 } })
|
117
|
+
assert(fb.txn { |fbt| fbt.query('(always)').each.to_a[0].zzz = 33 })
|
81
118
|
end
|
82
119
|
|
83
120
|
def test_run_txn
|
@@ -98,6 +135,17 @@ class TestFactbase < Minitest::Test
|
|
98
135
|
assert_equal(2, fb.size)
|
99
136
|
end
|
100
137
|
|
138
|
+
def test_run_txn_via_query
|
139
|
+
fb = Factbase.new
|
140
|
+
fb.insert.foo = 1
|
141
|
+
assert(
|
142
|
+
fb.txn do |fbt|
|
143
|
+
fbt.query('(always)').each { |f| f.foo = 42 }
|
144
|
+
end
|
145
|
+
)
|
146
|
+
assert_equal([1, 42], fb.query('(always)').each.to_a[0]['foo'])
|
147
|
+
end
|
148
|
+
|
101
149
|
def test_run_txn_with_inv
|
102
150
|
fb = Factbase::Inv.new(Factbase.new) { |_p, v| throw 'oops' if v == 42 }
|
103
151
|
fb.insert.bar = 3
|
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.
|
4
|
+
version: 0.0.47
|
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-06-
|
11
|
+
date: 2024-06-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|
@@ -102,6 +102,7 @@ files:
|
|
102
102
|
- factbase.gemspec
|
103
103
|
- lib/factbase.rb
|
104
104
|
- lib/factbase/fact.rb
|
105
|
+
- lib/factbase/flatten.rb
|
105
106
|
- lib/factbase/inv.rb
|
106
107
|
- lib/factbase/looged.rb
|
107
108
|
- lib/factbase/pre.rb
|
@@ -115,6 +116,7 @@ files:
|
|
115
116
|
- lib/factbase/tuples.rb
|
116
117
|
- renovate.json
|
117
118
|
- test/factbase/test_fact.rb
|
119
|
+
- test/factbase/test_flatten.rb
|
118
120
|
- test/factbase/test_inv.rb
|
119
121
|
- test/factbase/test_looged.rb
|
120
122
|
- test/factbase/test_pre.rb
|