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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c801d7ec256766a03b081de5949367aef74dc991b67207ae30c6e6229f11c425
4
- data.tar.gz: 2cfc12996cc7e4d85fad86377ddc9b87916d5d2a1cbe5542ba8128488c1e0049
3
+ metadata.gz: f3c7e0abf7bec6e53f050b4eeaeddd2b9954ee77b2a5f9a0332394c995d91ba9
4
+ data.tar.gz: 5da4b8574d7b1a93fd67f3d833bf51952eb5b3d9c303b9e20a084dc5953d71bc
5
5
  SHA512:
6
- metadata.gz: 6d2d1ec011eaf2d34f00068fa71809bf8bd3fe794b6097eeb1292ad25f1a6916e319490a137fa4c1908c00ce640f19c8efff27444ca277e72048daf5e688c3ef
7
- data.tar.gz: 7f20423de58a836eb68261a8d7ad45ec00056a4f991b8f34c68dd4833a42bbb0009bca75a4a8092112cfb80599e2be04e5cca9a9bfe18b3ae18102622d98f9a6
6
+ metadata.gz: f472603e32966c098979c2a461d94cd60f1f6213c4cea4abae225694d59ae88df252bbd42dcfb3292efb97352618cfa35fe56bef86bf87310dc74b4dbce65a20
7
+ data.tar.gz: fb91b271a5d57c59268dd386f8960e1c3c3c1577ea80e5c9bc3b623eaa6c559b15ce326f23ec9035e795d08ce647af353f4ae12aef5a34a124286f41fdf79130
@@ -32,7 +32,7 @@ jobs:
32
32
  strategy:
33
33
  matrix:
34
34
  os: [ubuntu-20.04, macos-12, windows-2022]
35
- ruby: [3.2]
35
+ ruby: [3.2, 3.3]
36
36
  runs-on: ${{ matrix.os }}
37
37
  steps:
38
38
  - uses: actions/checkout@v4
data/.rubocop.yml CHANGED
@@ -43,7 +43,7 @@ Metrics/BlockLength:
43
43
  Metrics/CyclomaticComplexity:
44
44
  Max: 25
45
45
  Metrics/PerceivedComplexity:
46
- Max: 25
46
+ Max: 30
47
47
  Metrics/ClassLength:
48
48
  Enabled: false
49
49
  Layout/EmptyLineAfterGuardClause:
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.6.3.1)
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.14)
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
- * `(nonil a b)` returns `b` if `a` is `nil`
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 = '>=2.3'
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 = 'Fact base in memory and on disc'
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
@@ -44,7 +44,9 @@ class Factbase::Syntax
44
44
  def to_term
45
45
  build.simplify
46
46
  rescue StandardError => e
47
- raise "#{e.message} in \"#{@query}\""
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
- @query.to_s.gsub(/#.*$/, '').chars.each do |c|
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?(/^[a-z][a-zA-Z0-9_]*$/)
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 nonil(fact, maps)
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.36'
52
+ VERSION = '0.0.38'
33
53
 
34
54
  # Constructor.
35
55
  def initialize(facts = [])
@@ -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 (nonil (at 0 (prev num)) 0)))' => 3,
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 hello) 0)' => 3,
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!' 33)",
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
- assert(
110
- assert_raises(q) do
111
- Factbase::Syntax.new(q).to_term
112
- end.message.include?(q)
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
 
@@ -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 test_nonil
182
- t = Factbase::Term.new(:nonil, [Factbase::Term.new(:at, [5, :foo]), 42])
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
 
@@ -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.write(f.path, f1.export)
53
- f2.import(File.read(f.path))
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.36
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-26 00:00:00.000000000 Z
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: Fact base in memory and on disc
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: '2.3'
145
+ version: '3.0'
142
146
  required_rubygems_version: !ruby/object:Gem::Requirement
143
147
  requirements:
144
148
  - - ">="