factbase 0.5.0 → 0.5.2

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: 88beb6f254052cd02b10e5c562bf1fe940c3a488274cbab3f4fcc61ef8ede0a7
4
- data.tar.gz: 983417ddfaf3f8b2f4930f1bb4565e4bc9d19d84ad26636df39d7bbc810ac3c1
3
+ metadata.gz: f4cc974ce82891e109f03740f7562f798e955da874dc2b1a91b0858bda7206b0
4
+ data.tar.gz: 7aac5d75a5e10fca5a6f74e9fc2e5b83ecaf9cc9d669e3384c52b508b732b45f
5
5
  SHA512:
6
- metadata.gz: 577a14d1021f170224be23a8afe4427a30565c2a78356e21f582031f80a65f44e314270e1e152c4d929f7b2b92e39cecccdca1c01bb37ebb1071b77e80170288
7
- data.tar.gz: 956b6147cf616f2376ffffd6a171b75e129f268e4c9858ba85270f2fde86e7686601f346cfbd40487f4c03562c065ce5a105a730b2b9f4e21bbd0fcc3f07113d
6
+ metadata.gz: 436eb0bc06eabc25825b36fdf8293f2654219e42a0470512d42bc075670a25d51936006303a67a4c183b7208ef1a9c8a940ab227af838fc4b4f8ae8020be4c4e
7
+ data.tar.gz: e7ea774220cdef5e471cb66c666c3cea20ee0672991b1e0fa5797b60f31e5430c9a0e9b4a40298d68654d067b1c1c1c8318fff492ba0162217cdb9724af8133e
data/Gemfile.lock CHANGED
@@ -157,7 +157,7 @@ GEM
157
157
  rubocop-ast (>= 1.36.2, < 2.0)
158
158
  ruby-progressbar (~> 1.7)
159
159
  unicode-display_width (>= 2.4.0, < 4.0)
160
- rubocop-ast (1.37.0)
160
+ rubocop-ast (1.38.0)
161
161
  parser (>= 3.3.1.0)
162
162
  rubocop-minitest (0.36.0)
163
163
  rubocop (>= 1.61, < 2.0)
data/README.md CHANGED
@@ -184,19 +184,19 @@ This is the result of the benchmark:
184
184
 
185
185
  <!-- benchmark_begin -->
186
186
  | Action | Seconds | Details |
187
- | --- | --: | --: |
188
- | `fb.insert()` | 7.800 | Inserted 100000 facts |
189
- | `(gt time '2024-03-23T03:21:43Z')` | 0.070 | Found 100000 fact(s) |
190
- | `(gt cost 50)` | 0.072 | Found 49779 fact(s) |
191
- | `(eq title 'Object Thinking 5000')` | 0.050 | Found 1 fact(s) |
192
- | `(and (eq foo 42.998) (or (gt bar 200) (absent zzz)))` | 0.060 | Found 0 fact(s) |
187
+ | --- | --: | --- |
188
+ | `fb.insert()` | 7.874 | Inserted 100000 facts |
189
+ | `(gt time '2024-03-23T03:21:43Z')` | 0.072 | Found 100000 fact(s) |
190
+ | `(gt cost 50)` | 0.069 | Found 50030 fact(s) |
191
+ | `(eq title 'Object Thinking 5000')` | 0.051 | Found 1 fact(s) |
192
+ | `(and (eq foo 42.998) (or (gt bar 200) (absent zzz)))` | 0.059 | Found 2 fact(s) |
193
193
  | `(eq id (agg (always) (max id)))` | 0.131 | Found 1 fact(s) |
194
- | `(join "c<=cost,b<=bar" (eq id (agg (always) (max id))))` | 0.708 | Found 100000 fact(s) |
195
- | `.export()` + `.import()` | 2.110 | 11407716 bytes |
194
+ | `(join "c<=cost,b<=bar" (eq id (agg (always) (max id))))` | 0.695 | Found 100000 fact(s) |
195
+ | `.export()` + `.import()` | 1.931 | 11407636 bytes |
196
196
 
197
197
  The results were calculated in [this GHA job][benchmark-gha]
198
- on 2025-01-27 at 16:25,
198
+ on 2025-01-27 at 16:51,
199
199
  on Linux with 4 CPUs.
200
200
  <!-- benchmark_end -->
201
201
 
202
- [benchmark-gha]: https://github.com/yegor256/factbase/actions/runs/12993508174
202
+ [benchmark-gha]: https://github.com/yegor256/factbase/actions/runs/12994018323
@@ -20,6 +20,7 @@
20
20
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  # SOFTWARE.
22
22
 
23
+ require 'backtrace'
23
24
  require 'time'
24
25
  require_relative '../factbase'
25
26
  require_relative 'fact'
@@ -45,12 +46,19 @@ require_relative 'term_once'
45
46
  # License:: MIT
46
47
  class Factbase::Syntax
47
48
  # Ctor.
49
+ #
50
+ # The class provided as the +term+ argument must have a three-argument
51
+ # constructor, similar to the class +Factbase::Term+. Also, it must be
52
+ # a child of +Factbase::Term+.
53
+ #
48
54
  # @param [Factbase] fb Factbase
49
55
  # @param [String] query The query, for example "(eq id 42)"
50
56
  # @param [Class] term The class to instantiate to make every term
51
57
  def initialize(fb, query, term: Factbase::Term)
52
58
  @fb = fb
53
59
  @query = query
60
+ raise "Term must be a Class, while #{term.class.name} provided" unless term.is_a?(Class)
61
+ raise "The 'term' must be a child of Factbase::Term, while #{term.name} provided" unless term <= Factbase::Term
54
62
  @term = term
55
63
  end
56
64
 
@@ -64,7 +72,7 @@ class Factbase::Syntax
64
72
  t
65
73
  end
66
74
  rescue StandardError => e
67
- err = "#{e.message} (#{e.backtrace[1]}) in \"#{@query}\""
75
+ err = "#{e.message} (#{Backtrace.new(e)}) in \"#{@query}\""
68
76
  err = "#{err}, tokens: #{@tokens}" unless @tokens.nil?
69
77
  raise err
70
78
  end
@@ -77,11 +85,11 @@ class Factbase::Syntax
77
85
  @tokens ||= to_tokens
78
86
  raise 'No tokens' if @tokens.empty?
79
87
  @ast ||= to_ast(@tokens, 0)
80
- raise 'Too many terms' if @ast[1] != @tokens.size
81
- term = @ast[0]
82
- raise 'No terms found' if term.nil?
83
- raise 'Not a term' unless term.is_a?(@term)
84
- term
88
+ raise "Too many terms (#{@ast[1]} != #{@tokens.size})" if @ast[1] != @tokens.size
89
+ t = @ast[0]
90
+ raise 'No terms found in the AST' if t.nil?
91
+ raise "#{t.class.name} is not an instance of #{@term}, thus not a proper term" unless t.is_a?(@term)
92
+ t
85
93
  end
86
94
 
87
95
  # Reads the stream of tokens, starting at the +at+ position. If the
@@ -109,7 +117,9 @@ class Factbase::Syntax
109
117
  operands << operand
110
118
  break if tokens[at] == :close
111
119
  end
112
- [Factbase::TermOnce.new(@term.new(@fb, op, operands), @fb.cache), at + 1]
120
+ t = @term.new(@fb, op, operands)
121
+ t = Factbase::TermOnce.new(t, @fb.cache) if t.instance_of?(Factbase::Term)
122
+ [t, at + 1]
113
123
  end
114
124
 
115
125
  # Turns a query into an array of tokens.
@@ -164,7 +174,7 @@ class Factbase::Syntax
164
174
  elsif t.match?(/^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$/)
165
175
  Time.parse(t)
166
176
  else
167
- raise "Wrong symbol format (#{t})" unless t.match?(/^[_a-z\$][a-zA-Z0-9_]*$/)
177
+ raise "Wrong symbol format (#{t})" unless t.match?(/^([_a-z][a-zA-Z0-9_]*|\$[a-z]+)$/)
168
178
  t.to_sym
169
179
  end
170
180
  end
data/lib/factbase.rb CHANGED
@@ -81,7 +81,7 @@ require 'yaml'
81
81
  # License:: MIT
82
82
  class Factbase
83
83
  # Current version of the gem (changed by .rultor.yml on every release)
84
- VERSION = '0.5.0'
84
+ VERSION = '0.5.2'
85
85
 
86
86
  # An exception that may be thrown in a transaction, to roll it back.
87
87
  class Rollback < StandardError; end
@@ -114,6 +114,7 @@ class TestSyntax < Minitest::Test
114
114
  '',
115
115
  '()',
116
116
  '(foo',
117
+ '(foo $)',
117
118
  '(foo 1) (bar 2)',
118
119
  'some text',
119
120
  '"hello, world!',
@@ -144,4 +145,26 @@ class TestSyntax < Minitest::Test
144
145
  assert_equal(t, Factbase::Syntax.new(Factbase.new, s).to_term.to_s)
145
146
  end
146
147
  end
148
+
149
+ def test_fails_when_term_is_not_a_class
150
+ assert_raises(StandardError) { Factbase::Syntax.new(Factbase.new, '(foo 1)', term: 'hello') }
151
+ end
152
+
153
+ def test_fails_when_term_is_wrong_class
154
+ assert_raises(StandardError) { Factbase::Syntax.new(Factbase.new, '(bar 1)', term: String).to_term }
155
+ end
156
+
157
+ def test_fails_when_term_is_incorrectly_defined_class
158
+ assert_includes(
159
+ assert_raises(StandardError) { Factbase::Syntax.new(Factbase.new, '(bar 1)', term: FakeTerm).to_term }.message,
160
+ 'wrong number of arguments'
161
+ )
162
+ end
163
+
164
+ class FakeTerm < Factbase::Term
165
+ def initialize(invalid)
166
+ super
167
+ @x = invalid
168
+ end
169
+ end
147
170
  end
@@ -294,20 +294,21 @@ class TestFactbase < Minitest::Test
294
294
  # See details here https://github.com/yegor256/factbase/actions/runs/10492255419/job/29068637032
295
295
  def test_concurrent_transactions_inserts
296
296
  skip('Does not work')
297
+ total = 100
297
298
  fb = Factbase.new
298
- Threads.new(100).assert do |i|
299
+ Threads.new(total).assert do |i|
299
300
  fb.txn do |fbt|
300
301
  fact = fbt.insert
301
302
  fact.thread_id = i
302
303
  end
303
304
  end
304
- assert_equal(100, fb.size)
305
- assert_equal(100, fb.query('(exists thread_id)').each.to_a.size)
305
+ assert_equal(total, fb.size)
306
+ assert_equal(total, fb.query('(exists thread_id)').each.to_a.size)
306
307
  end
307
308
 
308
309
  def test_concurrent_transactions_with_rollbacks
309
310
  fb = Factbase.new
310
- Threads.new(100).assert do |i|
311
+ Threads.new.assert do |i|
311
312
  fb.txn do |fbt|
312
313
  fact = fbt.insert
313
314
  fact.thread_id = i
@@ -319,7 +320,8 @@ class TestFactbase < Minitest::Test
319
320
 
320
321
  def test_concurrent_transactions_successful
321
322
  fb = Factbase.new
322
- Threads.new(100).assert do |i|
323
+ total = 100
324
+ Threads.new(total).assert do |i|
323
325
  fb.txn do |fbt|
324
326
  fact = fbt.insert
325
327
  fact.thread_id = i
@@ -327,7 +329,7 @@ class TestFactbase < Minitest::Test
327
329
  end
328
330
  end
329
331
  facts = fb.query('(exists thread_id)').each.to_a
330
- assert_equal(100, facts.size)
332
+ assert_equal(total, facts.size)
331
333
  facts.each do |fact|
332
334
  assert_equal(fact.value, fact.thread_id * 10)
333
335
  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.5.0
4
+ version: 0.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-01-27 00:00:00.000000000 Z
11
+ date: 2025-01-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: backtrace