factbase 0.0.17 → 0.0.19

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: d7ba6b0db6da2ae4a2075896ff5b8365716d881eab694e0c848063cfb0b19270
4
- data.tar.gz: 53e1f409d20404d60827545927f1b88797708889d18fc0177d176a7ae93e9a68
3
+ metadata.gz: bf6bf5824dca2b686d94a18b4341e2fbcd08adf286e228043418c98e2633673a
4
+ data.tar.gz: e37dcd3ad0fc483ed24586e353bc48d9afc6ec42384ef39f0cab9d30a85ede93
5
5
  SHA512:
6
- metadata.gz: 5f2db2983906007c6d4e12bb63b730e277d055c0f4768f41f6bc3ac68a6bccf6fa59226fa8ab417c41f0f53dfa163af59d7732e9082800494268e3d047de6f0c
7
- data.tar.gz: 9c77ce233f61637e0bec39b72da43fdc5403d0e098b4f4505747bce634233810acaaa88e65aa59c6ded4f8aef870b25823f04f051999ec9e221a26b5520960f9
6
+ metadata.gz: 00d59f7a614600ba776343545188b3ba4a0ea5f6f4470da577cd7506b6286a1f28f6232c03d47425d235132d9d1d694d5a212dcfd0ffcf3096269e1da900b6ab
7
+ data.tar.gz: 4572157aab7d5d5844905a1bee96e002233efdbf2667c245438c746f765c8c9aa41ede733c6c24ec9fa01728304a2e9b8d5e18d4d46ac0a04bd8d8666d70178c
@@ -32,7 +32,7 @@ jobs:
32
32
  strategy:
33
33
  matrix:
34
34
  os: [ubuntu-20.04, macos-12]
35
- ruby: [2.7, 3.2]
35
+ ruby: [3.2]
36
36
  runs-on: ${{ matrix.os }}
37
37
  steps:
38
38
  - uses: actions/checkout@v4
data/README.md CHANGED
@@ -32,11 +32,12 @@ end
32
32
  You can save the factbase to disc and load it back:
33
33
 
34
34
  ```ruby
35
+ file = '/tmp/simple.fb'
35
36
  f1 = Factbase.new
36
37
  f1.insert
37
- File.save('/tmp/db.txt', f1.export)
38
+ File.save(file, f1.export)
38
39
  f2 = Factbase.new
39
- f2.import(File.read('/tmp/db.txt'))
40
+ f2.import(File.read(file))
40
41
  ```
41
42
 
42
43
  ## How to contribute
@@ -44,7 +45,7 @@ f2.import(File.read('/tmp/db.txt'))
44
45
  Read [these guidelines](https://www.yegor256.com/2014/04/15/github-guidelines.html).
45
46
  Make sure you build is green before you contribute
46
47
  your pull request. You will need to have
47
- [Ruby](https://www.ruby-lang.org/en/) 2.3+ and
48
+ [Ruby](https://www.ruby-lang.org/en/) 3.2+ and
48
49
  [Bundler](https://bundler.io/) installed. Then:
49
50
 
50
51
  ```bash
data/factbase.gemspec CHANGED
@@ -26,7 +26,7 @@ Gem::Specification.new do |s|
26
26
  s.required_rubygems_version = Gem::Requirement.new('>= 0') if s.respond_to? :required_rubygems_version=
27
27
  s.required_ruby_version = '>=2.3'
28
28
  s.name = 'factbase'
29
- s.version = '0.0.17'
29
+ s.version = '0.0.19'
30
30
  s.license = 'MIT'
31
31
  s.summary = 'Factbase'
32
32
  s.description = 'Fact base in memory and on disc'
data/lib/factbase/fact.rb CHANGED
@@ -21,6 +21,7 @@
21
21
  # SOFTWARE.
22
22
 
23
23
  require 'json'
24
+ require 'time'
24
25
  require_relative '../factbase'
25
26
 
26
27
  # Fact.
@@ -33,10 +34,13 @@ class Factbase::Fact
33
34
  @map = map
34
35
  end
35
36
 
37
+ # Convert it to a string.
38
+ # @return [String] String representation of it (in JSON)
36
39
  def to_s
37
40
  @map.to_json
38
41
  end
39
42
 
43
+ # When a method is missing, this method is called.
40
44
  def method_missing(*args)
41
45
  k = args[0].to_s
42
46
  if k.end_with?('=')
@@ -46,6 +50,7 @@ class Factbase::Fact
46
50
  raise "Prop value can't be nil" if v.nil?
47
51
  raise "Prop value can't be empty" if v == ''
48
52
  raise "Prop type '#{v.class}' is not allowed" unless [String, Integer, Float, Time].include?(v.class)
53
+ v = v.utc if v.is_a?(Time)
49
54
  @mutex.synchronize do
50
55
  before = @map[kk]
51
56
  return if before == v
@@ -84,7 +84,8 @@ class Factbase::Looged
84
84
  def method_missing(*args)
85
85
  r = @fact.method_missing(*args)
86
86
  k = args[0].to_s
87
- @loog.debug("Set '#{k[0..-2]}' to '#{args[1]}'") if k.end_with?('=')
87
+ v = args[1]
88
+ @loog.debug("Set '#{k[0..-2]}' to '#{v.to_s.inspect}' (#{v.class})") if k.end_with?('=')
88
89
  r
89
90
  end
90
91
 
@@ -109,13 +110,23 @@ class Factbase::Looged
109
110
 
110
111
  def each(&)
111
112
  r = @query.each(&)
112
- @loog.debug("Found #{r} facts by '#{@expr}'")
113
+ raise 'Invalid return from query.each' unless r.is_a?(Integer)
114
+ if r.zero?
115
+ @loog.debug("Nothing found by '#{@expr}'")
116
+ else
117
+ @loog.debug("Found #{r} fact(s) by '#{@expr}'")
118
+ end
113
119
  r
114
120
  end
115
121
 
116
122
  def delete!
117
123
  r = @query.delete!
118
- @loog.debug("Deleted #{r} facts by '#{@expr}'")
124
+ raise 'Invalid return from query.delete!' unless r.is_a?(Integer)
125
+ if r.zero?
126
+ @loog.debug("Nothing deleted by '#{@expr}'")
127
+ else
128
+ @loog.debug("Deleted #{r} fact(s) by '#{@expr}'")
129
+ end
119
130
  r
120
131
  end
121
132
  end
@@ -29,6 +29,8 @@ require_relative 'term'
29
29
  # Copyright:: Copyright (c) 2024 Yegor Bugayenko
30
30
  # License:: MIT
31
31
  class Factbase::Syntax
32
+ # Ctor.
33
+ # @param [String] query The query, for example "(eq id 42)"
32
34
  def initialize(query)
33
35
  @query = query
34
36
  end
data/lib/factbase/term.rb CHANGED
@@ -30,18 +30,23 @@ require_relative 'fact'
30
30
  class Factbase::Term
31
31
  attr_reader :op, :operands
32
32
 
33
+ # Ctor.
34
+ # @param [Symbol] operator Operator
35
+ # @param [Array] operands Operands
33
36
  def initialize(operator, operands)
34
37
  @op = operator
35
38
  @operands = operands
36
39
  end
37
40
 
38
41
  # Does it match the fact?
39
- # @param [Factbase::Fact] The fact
42
+ # @param [Factbase::Fact] fact The fact
40
43
  # @return [bool] TRUE if matches
41
44
  def matches?(fact)
42
45
  send(@op, fact)
43
46
  end
44
47
 
48
+ # Turns it into a string.
49
+ # @return [String] The string of it
45
50
  def to_s
46
51
  items = []
47
52
  items << @op
@@ -85,13 +90,17 @@ class Factbase::Term
85
90
 
86
91
  def exists(fact)
87
92
  assert_args(1)
88
- k = @operands[0].to_s
93
+ o = @operands[0]
94
+ raise "A symbol expected: #{o}" unless o.is_a?(Symbol)
95
+ k = o.to_s
89
96
  !fact[k].nil?
90
97
  end
91
98
 
92
99
  def absent(fact)
93
100
  assert_args(1)
94
- k = @operands[0].to_s
101
+ o = @operands[0]
102
+ raise "A symbol expected: #{o}" unless o.is_a?(Symbol)
103
+ k = o.to_s
95
104
  fact[k].nil?
96
105
  end
97
106
 
@@ -109,7 +118,9 @@ class Factbase::Term
109
118
 
110
119
  def arithmetic(op, fact)
111
120
  assert_args(2)
112
- k = @operands[0].to_s
121
+ o = @operands[0]
122
+ raise "A symbol expected by #{op}: #{o}" unless o.is_a?(Symbol)
123
+ k = o.to_s
113
124
  v = fact[k]
114
125
  return false if v.nil?
115
126
  v = [v] unless v.is_a?(Array)
@@ -73,4 +73,12 @@ class TestFact < Minitest::Test
73
73
  f.send('foo_bar=', 42)
74
74
  assert_equal(42, f.foo_bar, f.to_s)
75
75
  end
76
+
77
+ def test_time_in_utc
78
+ f = Factbase::Fact.new(Mutex.new, {})
79
+ t = Time.now
80
+ f.foo = t
81
+ assert_equal(t.utc, f.foo)
82
+ assert_equal(t.utc.to_s, f.foo.to_s)
83
+ end
76
84
  end
@@ -43,4 +43,30 @@ class TestLooged < Minitest::Test
43
43
  assert_equal(2, found)
44
44
  assert_equal(2, fb.size)
45
45
  end
46
+
47
+ def test_returns_int
48
+ fb = Factbase.new
49
+ fb.insert
50
+ fb.insert
51
+ assert_equal(2, Factbase::Looged.new(fb, Loog::NULL).query('()').each(&:to_s))
52
+ end
53
+
54
+ def test_proper_logging
55
+ log = Loog::Buffer.new
56
+ fb = Factbase::Looged.new(Factbase.new, log)
57
+ fb.insert
58
+ fb.insert.bar = 3
59
+ fb.insert
60
+ fb.query('(exists bar)').each(&:to_s)
61
+ fb.query('(not (exists bar))').delete!
62
+ [
63
+ 'Inserted fact #1',
64
+ 'Inserted fact #2',
65
+ 'Set \'bar\' to \'"3"\' (Integer)',
66
+ 'Found 1 fact(s) by \'(exists bar)\'',
67
+ 'Deleted 2 fact(s) by \'(not (exists bar))\''
68
+ ].each do |s|
69
+ assert(log.to_s.include?("#{s}\n"), "#{log}\n")
70
+ end
71
+ end
46
72
  end
@@ -77,4 +77,11 @@ class TestQuery < Minitest::Test
77
77
  maps << { 'foo' => [42] }
78
78
  assert_equal(1, Factbase::Query.new(maps, Mutex.new, '(eq foo 42)').each.to_a.size)
79
79
  end
80
+
81
+ def test_returns_int
82
+ maps = []
83
+ maps << { 'foo' => 1 }
84
+ q = Factbase::Query.new(maps, Mutex.new, '(eq foo 1)')
85
+ assert_equal(1, q.each(&:to_s))
86
+ end
80
87
  end
@@ -29,14 +29,14 @@ require_relative '../../lib/factbase/term'
29
29
  # License:: MIT
30
30
  class TestTerm < Minitest::Test
31
31
  def test_simple_matching
32
- t = Factbase::Term.new(:eq, ['foo', 42])
32
+ t = Factbase::Term.new(:eq, [:foo, 42])
33
33
  assert(t.matches?(fact('foo' => [42])))
34
34
  assert(!t.matches?(fact('foo' => 'Hello!')))
35
35
  assert(!t.matches?(fact('bar' => ['Hello!'])))
36
36
  end
37
37
 
38
38
  def test_eq_matching
39
- t = Factbase::Term.new(:eq, ['foo', 42])
39
+ t = Factbase::Term.new(:eq, [:foo, 42])
40
40
  assert(t.matches?(fact('foo' => 42)))
41
41
  assert(t.matches?(fact('foo' => [10, 5, 6, -8, 'hey', 42, 9, 'fdsf'])))
42
42
  assert(!t.matches?(fact('foo' => [100])))
@@ -45,7 +45,7 @@ class TestTerm < Minitest::Test
45
45
  end
46
46
 
47
47
  def test_lt_matching
48
- t = Factbase::Term.new(:lt, ['foo', 42])
48
+ t = Factbase::Term.new(:lt, [:foo, 42])
49
49
  assert(t.matches?(fact('foo' => [10])))
50
50
  assert(!t.matches?(fact('foo' => [100])))
51
51
  assert(!t.matches?(fact('foo' => 100)))
@@ -53,7 +53,7 @@ class TestTerm < Minitest::Test
53
53
  end
54
54
 
55
55
  def test_gt_matching
56
- t = Factbase::Term.new(:gt, ['foo', 42])
56
+ t = Factbase::Term.new(:gt, [:foo, 42])
57
57
  assert(t.matches?(fact('foo' => [100])))
58
58
  assert(t.matches?(fact('foo' => 100)))
59
59
  assert(!t.matches?(fact('foo' => [10])))
@@ -62,14 +62,14 @@ class TestTerm < Minitest::Test
62
62
  end
63
63
 
64
64
  def test_lt_matching_time
65
- t = Factbase::Term.new(:lt, ['foo', Time.now])
65
+ t = Factbase::Term.new(:lt, [:foo, Time.now])
66
66
  assert(t.matches?(fact('foo' => [Time.now - 100])))
67
67
  assert(!t.matches?(fact('foo' => [Time.now + 100])))
68
68
  assert(!t.matches?(fact('bar' => [100])))
69
69
  end
70
70
 
71
71
  def test_gt_matching_time
72
- t = Factbase::Term.new(:gt, ['foo', Time.now])
72
+ t = Factbase::Term.new(:gt, [:foo, Time.now])
73
73
  assert(t.matches?(fact('foo' => [Time.now + 100])))
74
74
  assert(!t.matches?(fact('foo' => [Time.now - 100])))
75
75
  assert(!t.matches?(fact('bar' => [100])))
@@ -81,19 +81,19 @@ class TestTerm < Minitest::Test
81
81
  end
82
82
 
83
83
  def test_not_eq_matching
84
- t = Factbase::Term.new(:not, [Factbase::Term.new(:eq, ['foo', 100])])
84
+ t = Factbase::Term.new(:not, [Factbase::Term.new(:eq, [:foo, 100])])
85
85
  assert(t.matches?(fact('foo' => [42, 12, -90])))
86
86
  assert(!t.matches?(fact('foo' => 100)))
87
87
  end
88
88
 
89
89
  def test_exists_matching
90
- t = Factbase::Term.new(:exists, ['foo'])
90
+ t = Factbase::Term.new(:exists, [:foo])
91
91
  assert(t.matches?(fact('foo' => [42, 12, -90])))
92
92
  assert(!t.matches?(fact('bar' => 100)))
93
93
  end
94
94
 
95
95
  def test_absent_matching
96
- t = Factbase::Term.new(:absent, ['foo'])
96
+ t = Factbase::Term.new(:absent, [:foo])
97
97
  assert(t.matches?(fact('z' => [42, 12, -90])))
98
98
  assert(!t.matches?(fact('foo' => 100)))
99
99
  end
@@ -102,8 +102,8 @@ class TestTerm < Minitest::Test
102
102
  t = Factbase::Term.new(
103
103
  :or,
104
104
  [
105
- Factbase::Term.new(:eq, ['foo', 4]),
106
- Factbase::Term.new(:eq, ['bar', 5])
105
+ Factbase::Term.new(:eq, [:foo, 4]),
106
+ Factbase::Term.new(:eq, [:bar, 5])
107
107
  ]
108
108
  )
109
109
  assert(t.matches?(fact('foo' => [4])))
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.0.17
4
+ version: 0.0.19
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko