factbase 0.0.36 → 0.0.38
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 +4 -4
- data/.github/workflows/rake.yml +1 -1
- data/.rubocop.yml +1 -1
- data/Gemfile.lock +2 -2
- data/README.md +1 -1
- data/factbase.gemspec +7 -3
- data/lib/factbase/fact.rb +10 -0
- data/lib/factbase/syntax.rb +9 -3
- data/lib/factbase/term.rb +10 -1
- data/lib/factbase.rb +21 -1
- data/test/factbase/test_query.rb +2 -2
- data/test/factbase/test_syntax.rb +8 -7
- data/test/factbase/test_term.rb +2 -2
- data/test/test_factbase.rb +2 -2
- metadata +8 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f3c7e0abf7bec6e53f050b4eeaeddd2b9954ee77b2a5f9a0332394c995d91ba9
|
4
|
+
data.tar.gz: 5da4b8574d7b1a93fd67f3d833bf51952eb5b3d9c303b9e20a084dc5953d71bc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f472603e32966c098979c2a461d94cd60f1f6213c4cea4abae225694d59ae88df252bbd42dcfb3292efb97352618cfa35fe56bef86bf87310dc74b4dbce65a20
|
7
|
+
data.tar.gz: fb91b271a5d57c59268dd386f8960e1c3c3c1577ea80e5c9bc3b623eaa6c559b15ce326f23ec9035e795d08ce647af353f4ae12aef5a34a124286f41fdf79130
|
data/.github/workflows/rake.yml
CHANGED
data/.rubocop.yml
CHANGED
data/Gemfile.lock
CHANGED
@@ -101,7 +101,7 @@ GEM
|
|
101
101
|
zeitwerk (~> 2.6)
|
102
102
|
rainbow (3.1.1)
|
103
103
|
rake (13.2.1)
|
104
|
-
rdoc (6.
|
104
|
+
rdoc (6.7.0)
|
105
105
|
psych (>= 4.0.0)
|
106
106
|
regexp_parser (2.9.2)
|
107
107
|
reline (0.5.7)
|
@@ -171,7 +171,7 @@ GEM
|
|
171
171
|
webrick (1.8.1)
|
172
172
|
yaml (0.3.0)
|
173
173
|
yard (0.9.36)
|
174
|
-
zeitwerk (2.6.
|
174
|
+
zeitwerk (2.6.15)
|
175
175
|
|
176
176
|
PLATFORMS
|
177
177
|
arm64-darwin-22
|
data/README.md
CHANGED
@@ -71,7 +71,7 @@ There are some terms available in a query:
|
|
71
71
|
* `(many a)` return true if there are many values in the `a` property
|
72
72
|
* `(one a)` returns true if there is only one value in the `a` property
|
73
73
|
* `(at i a)` returns the `i`-th value of the `a` property
|
74
|
-
* `(
|
74
|
+
* `(either a b)` returns `b` if `a` is `nil`
|
75
75
|
* `(matches a re)` returns true when `a` matches regular expression `re`
|
76
76
|
* `(defn foo "self.to_s")` defines a new term using Ruby syntax and returns true
|
77
77
|
|
data/factbase.gemspec
CHANGED
@@ -25,17 +25,21 @@ require_relative 'lib/factbase'
|
|
25
25
|
|
26
26
|
Gem::Specification.new do |s|
|
27
27
|
s.required_rubygems_version = Gem::Requirement.new('>= 0') if s.respond_to? :required_rubygems_version=
|
28
|
-
s.required_ruby_version = '>=
|
28
|
+
s.required_ruby_version = '>=3.0'
|
29
29
|
s.name = 'factbase'
|
30
30
|
s.version = Factbase::VERSION
|
31
31
|
s.license = 'MIT'
|
32
32
|
s.summary = 'Factbase'
|
33
|
-
s.description =
|
33
|
+
s.description =
|
34
|
+
'A primitive in-memory collection of key-value records ' \
|
35
|
+
'known as "facts," with an ability to insert facts, add properties ' \
|
36
|
+
'to facts, and delete facts. There is no ability to modify facts. ' \
|
37
|
+
'It is also possible to find facts using Lisp-alike query predicates. ' \
|
38
|
+
'An entire factbase may be exported to a binary file and imported back.'
|
34
39
|
s.authors = ['Yegor Bugayenko']
|
35
40
|
s.email = 'yegor256@gmail.com'
|
36
41
|
s.homepage = 'http://github.com/yegor256/factbase.rb'
|
37
42
|
s.files = `git ls-files`.split($RS)
|
38
|
-
s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
39
43
|
s.rdoc_options = ['--charset=UTF-8']
|
40
44
|
s.extra_rdoc_files = ['README.md', 'LICENSE.txt']
|
41
45
|
s.add_runtime_dependency 'json', '~> 2.7'
|
data/lib/factbase/fact.rb
CHANGED
@@ -28,10 +28,20 @@ require_relative '../factbase'
|
|
28
28
|
#
|
29
29
|
# This is an internal class, it is not supposed to be instantiated directly.
|
30
30
|
#
|
31
|
+
# It is possible to use for testing directly, for example to make a
|
32
|
+
# fact with a single key/value pair inside:
|
33
|
+
#
|
34
|
+
# require 'factbase/fact'
|
35
|
+
# f = Factbase::Fact.new(Mutex.new, { 'foo' => [42, 256, 'Hello, world!'] })
|
36
|
+
# assert_equal(42, f.foo)
|
37
|
+
#
|
31
38
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
32
39
|
# Copyright:: Copyright (c) 2024 Yegor Bugayenko
|
33
40
|
# License:: MIT
|
34
41
|
class Factbase::Fact
|
42
|
+
# Ctor.
|
43
|
+
# @param [Mutex] mutex A mutex to use for maps synchronization
|
44
|
+
# @param [Hash] map A map of key/value pairs
|
35
45
|
def initialize(mutex, map)
|
36
46
|
@mutex = mutex
|
37
47
|
@map = map
|
data/lib/factbase/syntax.rb
CHANGED
@@ -44,7 +44,9 @@ class Factbase::Syntax
|
|
44
44
|
def to_term
|
45
45
|
build.simplify
|
46
46
|
rescue StandardError => e
|
47
|
-
|
47
|
+
err = "#{e.message} in #{@query}"
|
48
|
+
err = "#{err}, tokens: #{@tokens}" unless @tokens.nil?
|
49
|
+
raise err
|
48
50
|
end
|
49
51
|
|
50
52
|
private
|
@@ -95,7 +97,11 @@ class Factbase::Syntax
|
|
95
97
|
list = []
|
96
98
|
acc = ''
|
97
99
|
string = false
|
98
|
-
|
100
|
+
comment = false
|
101
|
+
@query.to_s.chars.each do |c|
|
102
|
+
comment = true if !string && c == '#'
|
103
|
+
comment = false if comment && c == "\n"
|
104
|
+
next if comment
|
99
105
|
if ['\'', '"'].include?(c)
|
100
106
|
if string && acc[acc.length - 1] == '\\'
|
101
107
|
acc = acc[0..-2]
|
@@ -136,7 +142,7 @@ class Factbase::Syntax
|
|
136
142
|
elsif t.match?(/^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$/)
|
137
143
|
Time.parse(t)
|
138
144
|
else
|
139
|
-
raise "Wrong symbol format (#{t})" unless t.match?(/^[
|
145
|
+
raise "Wrong symbol format (#{t})" unless t.match?(/^[_a-z][a-zA-Z0-9_]*$/)
|
140
146
|
t.to_sym
|
141
147
|
end
|
142
148
|
end
|
data/lib/factbase/term.rb
CHANGED
@@ -27,6 +27,15 @@ require_relative 'fact'
|
|
27
27
|
#
|
28
28
|
# This is an internal class, it is not supposed to be instantiated directly.
|
29
29
|
#
|
30
|
+
# It is possible to use for testing directly, for example to make a
|
31
|
+
# term with two arguments:
|
32
|
+
#
|
33
|
+
# require 'factbase/fact'
|
34
|
+
# require 'factbase/term'
|
35
|
+
# f = Factbase::Fact.new(Mutex.new, { 'foo' => [42, 256, 'Hello, world!'] })
|
36
|
+
# t = Factbase::Term.new(:lt, [:foo, 50])
|
37
|
+
# assert(t.evaluate(f))
|
38
|
+
#
|
30
39
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
31
40
|
# Copyright:: Copyright (c) 2024 Yegor Bugayenko
|
32
41
|
# License:: MIT
|
@@ -149,7 +158,7 @@ class Factbase::Term
|
|
149
158
|
by_symbol(0, fact).nil?
|
150
159
|
end
|
151
160
|
|
152
|
-
def
|
161
|
+
def either(fact, maps)
|
153
162
|
assert_args(2)
|
154
163
|
v = the_values(0, fact, maps)
|
155
164
|
return v unless v.nil?
|
data/lib/factbase.rb
CHANGED
@@ -24,12 +24,32 @@ require 'json'
|
|
24
24
|
require 'yaml'
|
25
25
|
|
26
26
|
# Factbase.
|
27
|
+
#
|
28
|
+
# This is an entry point to a factbase:
|
29
|
+
#
|
30
|
+
# fb = Factbase.new
|
31
|
+
# f = fb.insert # new fact created
|
32
|
+
# f.name = 'Jeff Lebowski'
|
33
|
+
# f.age = 42
|
34
|
+
# found = f.query('(gt 20 age)').each.to_a[0]
|
35
|
+
# assert(found.age == 42)
|
36
|
+
#
|
37
|
+
# A factbase may be exported to a file and then imported back:
|
38
|
+
#
|
39
|
+
# fb1 = Factbase.new
|
40
|
+
# File.writebin(file, fb1.export)
|
41
|
+
# fb2 = Factbase.new # it's empty
|
42
|
+
# fb2.import(File.readbin(file))
|
43
|
+
#
|
44
|
+
# It's important to use +writebin+ and +readbin+, because the content is
|
45
|
+
# a chain of bytes, not a text.
|
46
|
+
#
|
27
47
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
28
48
|
# Copyright:: Copyright (c) 2024 Yegor Bugayenko
|
29
49
|
# License:: MIT
|
30
50
|
class Factbase
|
31
51
|
# Current version of the gem (changed by .rultor.yml on every release)
|
32
|
-
VERSION = '0.0.
|
52
|
+
VERSION = '0.0.38'
|
33
53
|
|
34
54
|
# Constructor.
|
35
55
|
def initialize(facts = [])
|
data/test/factbase/test_query.rb
CHANGED
@@ -59,7 +59,7 @@ class TestQuery < Minitest::Test
|
|
59
59
|
'(when (eq num 0) (exists time))' => 2,
|
60
60
|
'(many num)' => 1,
|
61
61
|
'(one num)' => 2,
|
62
|
-
'(gt num (minus 1 (
|
62
|
+
'(gt num (minus 1 (either (at 0 (prev num)) 0)))' => 3,
|
63
63
|
'(and (not (many num)) (eq num (plus 21 +21)))' => 1,
|
64
64
|
'(and (not (many num)) (eq num (minus -100 -142)))' => 1,
|
65
65
|
'(and (one num) (eq num (times 7 6)))' => 1,
|
@@ -67,7 +67,7 @@ class TestQuery < Minitest::Test
|
|
67
67
|
'(gt (size num) 2)' => 1,
|
68
68
|
'(matches name "^[a-z]+$")' => 1,
|
69
69
|
'(lt (size num) 2)' => 2,
|
70
|
-
'(eq (size
|
70
|
+
'(eq (size _hello) 0)' => 3,
|
71
71
|
'(eq num pi)' => 0,
|
72
72
|
'(absent time)' => 2,
|
73
73
|
'(eq pi (agg (eq num 0) (sum pi)))' => 1,
|
@@ -42,10 +42,12 @@ class TestSyntax < Minitest::Test
|
|
42
42
|
def test_simple_parsing
|
43
43
|
[
|
44
44
|
'(foo)',
|
45
|
-
'(foo (bar) (zz 77) )',
|
45
|
+
'(foo (bar) (zz 77) ) # hey',
|
46
|
+
"# hello\n\n\n(foo (bar))",
|
46
47
|
"(eq foo \n\n 'Hello, world!'\n)\n",
|
47
48
|
"(eq x 'Hello, \\' \n) \\' ( world!')",
|
48
49
|
"# this is a comment\n(eq foo # test\n 42)\n\n# another comment\n",
|
50
|
+
"(foo 'Hello,\n\nworld!\r\t\n')\n",
|
49
51
|
"(or ( a 4) (b 5) (always) (and (always) (c 5) \t\t(r 7 w8s w8is 'Foo')))"
|
50
52
|
].each do |q|
|
51
53
|
Factbase::Syntax.new(q).to_term
|
@@ -60,7 +62,7 @@ class TestSyntax < Minitest::Test
|
|
60
62
|
"(r 'Dude\\'s Friend')",
|
61
63
|
"(r 'I\\'m \\\"good\\\"')",
|
62
64
|
'(foo x y z)',
|
63
|
-
"(foo x y z t f 42 'Hi
|
65
|
+
"(foo x y z t f 42 'Hi!# you' 33)",
|
64
66
|
'(foo (x) y z)',
|
65
67
|
'(eq t 2024-05-25T19:43:48Z)',
|
66
68
|
'(eq t 2024-05-25T19:43:48Z)',
|
@@ -106,11 +108,10 @@ class TestSyntax < Minitest::Test
|
|
106
108
|
')',
|
107
109
|
'"'
|
108
110
|
].each do |q|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
)
|
111
|
+
msg = assert_raises(q) do
|
112
|
+
Factbase::Syntax.new(q).to_term
|
113
|
+
end.message
|
114
|
+
assert(msg.include?(q), msg)
|
114
115
|
end
|
115
116
|
end
|
116
117
|
|
data/test/factbase/test_term.rb
CHANGED
@@ -178,8 +178,8 @@ class TestTerm < Minitest::Test
|
|
178
178
|
assert_equal(5, t.evaluate(fact('foo' => [4, 5]), []))
|
179
179
|
end
|
180
180
|
|
181
|
-
def
|
182
|
-
t = Factbase::Term.new(:
|
181
|
+
def test_either
|
182
|
+
t = Factbase::Term.new(:either, [Factbase::Term.new(:at, [5, :foo]), 42])
|
183
183
|
assert_equal([42], t.evaluate(fact('foo' => 4), []))
|
184
184
|
end
|
185
185
|
|
data/test/test_factbase.rb
CHANGED
@@ -49,8 +49,8 @@ class TestFactbase < Minitest::Test
|
|
49
49
|
f2 = Factbase.new
|
50
50
|
f1.insert.foo = 42
|
51
51
|
Tempfile.open do |f|
|
52
|
-
File.
|
53
|
-
f2.import(File.
|
52
|
+
File.binwrite(f.path, f1.export)
|
53
|
+
f2.import(File.binread(f.path))
|
54
54
|
end
|
55
55
|
assert_equal(1, f2.query('(eq foo 42)').each.to_a.count)
|
56
56
|
end
|
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.38
|
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-
|
11
|
+
date: 2024-05-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|
@@ -66,7 +66,11 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0.3'
|
69
|
-
description:
|
69
|
+
description: A primitive in-memory collection of key-value records known as "facts,"
|
70
|
+
with an ability to insert facts, add properties to facts, and delete facts. There
|
71
|
+
is no ability to modify facts. It is also possible to find facts using Lisp-alike
|
72
|
+
query predicates. An entire factbase may be exported to a binary file and imported
|
73
|
+
back.
|
70
74
|
email: yegor256@gmail.com
|
71
75
|
executables: []
|
72
76
|
extensions: []
|
@@ -138,7 +142,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
138
142
|
requirements:
|
139
143
|
- - ">="
|
140
144
|
- !ruby/object:Gem::Version
|
141
|
-
version: '
|
145
|
+
version: '3.0'
|
142
146
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
143
147
|
requirements:
|
144
148
|
- - ">="
|