factbase 0.5.0 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
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