factbase 0.0.36 → 0.0.38
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
- - ">="
|