factbase 0.7.5 → 0.9.0

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.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/Gemfile +1 -1
  4. data/Gemfile.lock +3 -3
  5. data/README.md +24 -27
  6. data/REUSE.toml +7 -2
  7. data/Rakefile +8 -1
  8. data/benchmark/bench_factbase.rb +1 -1
  9. data/fixtures/stories/agg.yml +17 -0
  10. data/fixtures/stories/always.yml +16 -0
  11. data/fixtures/stories/as.yml +16 -0
  12. data/fixtures/stories/count.yml +18 -0
  13. data/fixtures/stories/eq.yml +30 -0
  14. data/fixtures/stories/gt.yml +18 -0
  15. data/fixtures/stories/join.yml +19 -0
  16. data/fixtures/stories/max.yml +14 -0
  17. data/fixtures/stories/min.yml +14 -0
  18. data/fixtures/stories/nth.yml +14 -0
  19. data/fixtures/stories/or.yml +18 -0
  20. data/fixtures/stories/sprintf.yml +12 -0
  21. data/fixtures/stories/sum.yml +14 -0
  22. data/lib/factbase/cached/cached_fact.rb +28 -0
  23. data/lib/factbase/cached/cached_factbase.rb +64 -0
  24. data/lib/factbase/cached/cached_query.rb +61 -0
  25. data/lib/factbase/cached/cached_term.rb +25 -0
  26. data/lib/factbase/fact.rb +13 -13
  27. data/lib/factbase/indexed/indexed_fact.rb +28 -0
  28. data/lib/factbase/indexed/indexed_factbase.rb +64 -0
  29. data/lib/factbase/indexed/indexed_query.rb +56 -0
  30. data/lib/factbase/indexed/indexed_term.rb +60 -0
  31. data/lib/factbase/light.rb +7 -6
  32. data/lib/factbase/logged.rb +70 -35
  33. data/lib/factbase/query.rb +29 -34
  34. data/lib/factbase/rules.rb +15 -14
  35. data/lib/factbase/sync/sync_factbase.rb +57 -0
  36. data/lib/factbase/sync/sync_query.rb +61 -0
  37. data/lib/factbase/syntax.rb +16 -26
  38. data/lib/factbase/tallied.rb +10 -9
  39. data/lib/factbase/taped.rb +8 -0
  40. data/lib/factbase/tee.rb +2 -0
  41. data/lib/factbase/term.rb +45 -17
  42. data/lib/factbase/terms/aggregates.rb +17 -15
  43. data/lib/factbase/terms/aliases.rb +4 -4
  44. data/lib/factbase/terms/casting.rb +8 -8
  45. data/lib/factbase/terms/debug.rb +2 -2
  46. data/lib/factbase/terms/defn.rb +3 -3
  47. data/lib/factbase/terms/logical.rb +53 -14
  48. data/lib/factbase/terms/math.rb +26 -26
  49. data/lib/factbase/terms/meta.rb +14 -14
  50. data/lib/factbase/terms/ordering.rb +4 -4
  51. data/lib/factbase/terms/strings.rb +8 -8
  52. data/lib/factbase/terms/system.rb +3 -3
  53. data/lib/factbase.rb +67 -55
  54. data/test/factbase/cached/test_cached_factbase.rb +22 -0
  55. data/test/factbase/cached/test_cached_query.rb +79 -0
  56. data/test/factbase/indexed/test_indexed_query.rb +175 -0
  57. data/test/factbase/sync/test_sync_query.rb +30 -0
  58. data/test/factbase/terms/test_aggregates.rb +5 -5
  59. data/test/factbase/terms/test_aliases.rb +7 -7
  60. data/test/factbase/terms/test_casting.rb +8 -8
  61. data/test/factbase/terms/test_debug.rb +6 -6
  62. data/test/factbase/terms/test_defn.rb +14 -14
  63. data/test/factbase/terms/test_logical.rb +17 -19
  64. data/test/factbase/terms/test_math.rb +63 -61
  65. data/test/factbase/terms/test_meta.rb +36 -36
  66. data/test/factbase/terms/test_ordering.rb +9 -9
  67. data/test/factbase/terms/test_strings.rb +10 -10
  68. data/test/factbase/terms/test_system.rb +6 -6
  69. data/test/factbase/test_accum.rb +5 -5
  70. data/test/factbase/test_fact.rb +12 -12
  71. data/test/factbase/test_logged.rb +7 -0
  72. data/test/factbase/test_query.rb +99 -37
  73. data/test/factbase/test_rules.rb +1 -1
  74. data/test/factbase/test_syntax.rb +24 -11
  75. data/test/factbase/test_tee.rb +8 -8
  76. data/test/factbase/test_term.rb +39 -30
  77. data/test/test__helper.rb +2 -2
  78. data/test/test_factbase.rb +6 -0
  79. metadata +29 -4
  80. data/lib/factbase/query_once.rb +0 -54
  81. data/lib/factbase/term_once.rb +0 -67
@@ -12,59 +12,59 @@ require_relative '../../../lib/factbase/term'
12
12
  # License:: MIT
13
13
  class TestMeta < Factbase::Test
14
14
  def test_exists
15
- t = Factbase::Term.new(Factbase.new, :exists, [:foo])
16
- assert(t.evaluate(fact('foo' => 41), []))
17
- refute(t.evaluate(fact('bar' => 41), []))
15
+ t = Factbase::Term.new(:exists, [:foo])
16
+ assert(t.evaluate(fact('foo' => 41), [], Factbase.new))
17
+ refute(t.evaluate(fact('bar' => 41), [], Factbase.new))
18
18
  end
19
19
 
20
20
  def test_absent
21
- t = Factbase::Term.new(Factbase.new, :absent, [:foo])
22
- refute(t.evaluate(fact('foo' => 41), []))
23
- assert(t.evaluate(fact('bar' => 41), []))
21
+ t = Factbase::Term.new(:absent, [:foo])
22
+ refute(t.evaluate(fact('foo' => 41), [], Factbase.new))
23
+ assert(t.evaluate(fact('bar' => 41), [], Factbase.new))
24
24
  end
25
25
 
26
26
  def test_size
27
- t = Factbase::Term.new(Factbase.new, :size, [:foo])
28
- assert_equal(1, t.evaluate(fact('foo' => 41), []))
29
- assert_equal(0, t.evaluate(fact('foo' => nil), []))
30
- assert_equal(4, t.evaluate(fact('foo' => [1, 2, 3, 4]), []))
31
- assert_equal(0, t.evaluate(fact('foo' => []), []))
32
- assert_equal(1, t.evaluate(fact('foo' => ''), []))
27
+ t = Factbase::Term.new(:size, [:foo])
28
+ assert_equal(1, t.evaluate(fact('foo' => 41), [], Factbase.new))
29
+ assert_equal(0, t.evaluate(fact('foo' => nil), [], Factbase.new))
30
+ assert_equal(4, t.evaluate(fact('foo' => [1, 2, 3, 4]), [], Factbase.new))
31
+ assert_equal(0, t.evaluate(fact('foo' => []), [], Factbase.new))
32
+ assert_equal(1, t.evaluate(fact('foo' => ''), [], Factbase.new))
33
33
  end
34
34
 
35
35
  def test_type
36
- t = Factbase::Term.new(Factbase.new, :type, [:foo])
37
- assert_equal('nil', t.evaluate(fact('foo' => nil), []))
38
- assert_equal('Integer', t.evaluate(fact('foo' => [1]), []))
39
- assert_equal('Array', t.evaluate(fact('foo' => [1, 2]), []))
40
- assert_equal('String', t.evaluate(fact('foo' => 'bar'), []))
41
- assert_equal('Float', t.evaluate(fact('foo' => 2.1), []))
42
- assert_equal('Time', t.evaluate(fact('foo' => Time.now), []))
36
+ t = Factbase::Term.new(:type, [:foo])
37
+ assert_equal('nil', t.evaluate(fact('foo' => nil), [], Factbase.new))
38
+ assert_equal('Integer', t.evaluate(fact('foo' => [1]), [], Factbase.new))
39
+ assert_equal('Array', t.evaluate(fact('foo' => [1, 2]), [], Factbase.new))
40
+ assert_equal('String', t.evaluate(fact('foo' => 'bar'), [], Factbase.new))
41
+ assert_equal('Float', t.evaluate(fact('foo' => 2.1), [], Factbase.new))
42
+ assert_equal('Time', t.evaluate(fact('foo' => Time.now), [], Factbase.new))
43
43
  end
44
44
 
45
45
  def test_nil
46
- t = Factbase::Term.new(Factbase.new, :nil, [:foo])
47
- assert(t.evaluate(fact('foo' => nil), []))
48
- refute(t.evaluate(fact('foo' => true), []))
49
- refute(t.evaluate(fact('foo' => 'bar'), []))
46
+ t = Factbase::Term.new(:nil, [:foo])
47
+ assert(t.evaluate(fact('foo' => nil), [], Factbase.new))
48
+ refute(t.evaluate(fact('foo' => true), [], Factbase.new))
49
+ refute(t.evaluate(fact('foo' => 'bar'), [], Factbase.new))
50
50
  end
51
51
 
52
52
  def test_many
53
- t = Factbase::Term.new(Factbase.new, :many, [:foo])
54
- refute(t.evaluate(fact('foo' => nil), []))
55
- refute(t.evaluate(fact('foo' => 1), []))
56
- refute(t.evaluate(fact('foo' => '1234'), []))
57
- assert(t.evaluate(fact('foo' => [1, 3, 5]), []))
58
- refute(t.evaluate(fact('foo' => []), []))
53
+ t = Factbase::Term.new(:many, [:foo])
54
+ refute(t.evaluate(fact('foo' => nil), [], Factbase.new))
55
+ refute(t.evaluate(fact('foo' => 1), [], Factbase.new))
56
+ refute(t.evaluate(fact('foo' => '1234'), [], Factbase.new))
57
+ assert(t.evaluate(fact('foo' => [1, 3, 5]), [], Factbase.new))
58
+ refute(t.evaluate(fact('foo' => []), [], Factbase.new))
59
59
  end
60
60
 
61
61
  def test_one
62
- t = Factbase::Term.new(Factbase.new, :one, [:foo])
63
- assert(t.evaluate(fact('foo' => 1), []))
64
- assert(t.evaluate(fact('foo' => '1234'), []))
65
- assert(t.evaluate(fact('foo' => [1]), []))
66
- refute(t.evaluate(fact('foo' => nil), []))
67
- refute(t.evaluate(fact('foo' => [1, 3, 5]), []))
68
- refute(t.evaluate(fact('foo' => []), []))
62
+ t = Factbase::Term.new(:one, [:foo])
63
+ assert(t.evaluate(fact('foo' => 1), [], Factbase.new))
64
+ assert(t.evaluate(fact('foo' => '1234'), [], Factbase.new))
65
+ assert(t.evaluate(fact('foo' => [1]), [], Factbase.new))
66
+ refute(t.evaluate(fact('foo' => nil), [], Factbase.new))
67
+ refute(t.evaluate(fact('foo' => [1, 3, 5]), [], Factbase.new))
68
+ refute(t.evaluate(fact('foo' => []), [], Factbase.new))
69
69
  end
70
70
  end
@@ -12,17 +12,17 @@ require_relative '../../../lib/factbase/term'
12
12
  # License:: MIT
13
13
  class TestOrdering < Factbase::Test
14
14
  def test_prev
15
- t = Factbase::Term.new(Factbase.new, :prev, [:foo])
16
- assert_nil(t.evaluate(fact('foo' => 41), []))
17
- assert_equal([41], t.evaluate(fact('foo' => 5), []))
18
- assert_equal([5], t.evaluate(fact('foo' => 6), []))
15
+ t = Factbase::Term.new(:prev, [:foo])
16
+ assert_nil(t.evaluate(fact('foo' => 41), [], Factbase.new))
17
+ assert_equal([41], t.evaluate(fact('foo' => 5), [], Factbase.new))
18
+ assert_equal([5], t.evaluate(fact('foo' => 6), [], Factbase.new))
19
19
  end
20
20
 
21
21
  def test_unique
22
- t = Factbase::Term.new(Factbase.new, :unique, [:foo])
23
- refute(t.evaluate(fact, []))
24
- assert(t.evaluate(fact('foo' => 41), []))
25
- refute(t.evaluate(fact('foo' => 41), []))
26
- assert(t.evaluate(fact('foo' => 1), []))
22
+ t = Factbase::Term.new(:unique, [:foo])
23
+ refute(t.evaluate(fact, [], Factbase.new))
24
+ assert(t.evaluate(fact('foo' => 41), [], Factbase.new))
25
+ refute(t.evaluate(fact('foo' => 41), [], Factbase.new))
26
+ assert(t.evaluate(fact('foo' => 1), [], Factbase.new))
27
27
  end
28
28
  end
@@ -12,25 +12,25 @@ require_relative '../../../lib/factbase/term'
12
12
  # License:: MIT
13
13
  class TestStrings < Factbase::Test
14
14
  def test_regexp_matching
15
- t = Factbase::Term.new(Factbase.new, :matches, [:foo, '[a-z]+'])
16
- assert(t.evaluate(fact('foo' => 'hello'), []))
17
- assert(t.evaluate(fact('foo' => 'hello 42'), []))
18
- refute(t.evaluate(fact('foo' => 42), []))
15
+ t = Factbase::Term.new(:matches, [:foo, '[a-z]+'])
16
+ assert(t.evaluate(fact('foo' => 'hello'), [], Factbase.new))
17
+ assert(t.evaluate(fact('foo' => 'hello 42'), [], Factbase.new))
18
+ refute(t.evaluate(fact('foo' => 42), [], Factbase.new))
19
19
  end
20
20
 
21
21
  def test_concat
22
- t = Factbase::Term.new(Factbase.new, :concat, [42, 'hi', 3.14, :hey, Time.now])
23
- s = t.evaluate(fact, [])
22
+ t = Factbase::Term.new(:concat, [42, 'hi', 3.14, :hey, Time.now])
23
+ s = t.evaluate(fact, [], Factbase.new)
24
24
  assert(s.start_with?('42hi3.14'))
25
25
  end
26
26
 
27
27
  def test_concat_empty
28
- t = Factbase::Term.new(Factbase.new, :concat, [])
29
- assert_equal('', t.evaluate(fact, []))
28
+ t = Factbase::Term.new(:concat, [])
29
+ assert_equal('', t.evaluate(fact, [], Factbase.new))
30
30
  end
31
31
 
32
32
  def test_sprintf
33
- t = Factbase::Term.new(Factbase.new, :sprintf, ['hi, %s!', 'Jeff'])
34
- assert_equal('hi, Jeff!', t.evaluate(fact, []))
33
+ t = Factbase::Term.new(:sprintf, ['hi, %s!', 'Jeff'])
34
+ assert_equal('hi, Jeff!', t.evaluate(fact, [], Factbase.new))
35
35
  end
36
36
  end
@@ -13,19 +13,19 @@ require_relative '../../../lib/factbase/term'
13
13
  class TestSystem < Factbase::Test
14
14
  def test_env
15
15
  ENV.store('FOO', 'bar')
16
- t = Factbase::Term.new(Factbase.new, :env, ['FOO', ''])
17
- assert_equal('bar', t.evaluate(fact, []))
16
+ t = Factbase::Term.new(:env, ['FOO', ''])
17
+ assert_equal('bar', t.evaluate(fact, [], Factbase.new))
18
18
  end
19
19
 
20
20
  def test_default
21
21
  ENV.delete('FOO')
22
- t = Factbase::Term.new(Factbase.new, :env, ['FOO', 'мой друг'])
23
- assert_equal('мой друг', t.evaluate(fact, []))
22
+ t = Factbase::Term.new(:env, ['FOO', 'мой друг'])
23
+ assert_equal('мой друг', t.evaluate(fact, [], Factbase.new))
24
24
  end
25
25
 
26
26
  def test_when_default_is_absent
27
27
  ENV.delete('FOO')
28
- t = Factbase::Term.new(Factbase.new, :env, ['FOO'])
29
- assert_raises(StandardError) { t.evaluate(fact, []) }
28
+ t = Factbase::Term.new(:env, ['FOO'])
29
+ assert_raises(StandardError) { t.evaluate(fact, [], Factbase.new) }
30
30
  end
31
31
  end
@@ -15,7 +15,7 @@ require_relative '../../lib/factbase/fact'
15
15
  class TestAccum < Factbase::Test
16
16
  def test_holds_props
17
17
  map = {}
18
- f = Factbase::Fact.new(Factbase.new, Mutex.new, map)
18
+ f = Factbase::Fact.new(map)
19
19
  props = {}
20
20
  a = Factbase::Accum.new(f, props, false)
21
21
  a.foo = 42
@@ -26,7 +26,7 @@ class TestAccum < Factbase::Test
26
26
 
27
27
  def test_passes_props
28
28
  map = {}
29
- f = Factbase::Fact.new(Factbase.new, Mutex.new, map)
29
+ f = Factbase::Fact.new(map)
30
30
  props = {}
31
31
  a = Factbase::Accum.new(f, props, true)
32
32
  a.foo = 42
@@ -37,7 +37,7 @@ class TestAccum < Factbase::Test
37
37
 
38
38
  def test_appends_props
39
39
  map = {}
40
- f = Factbase::Fact.new(Factbase.new, Mutex.new, map)
40
+ f = Factbase::Fact.new(map)
41
41
  f.foo = 42
42
42
  props = {}
43
43
  a = Factbase::Accum.new(f, props, false)
@@ -46,14 +46,14 @@ class TestAccum < Factbase::Test
46
46
  end
47
47
 
48
48
  def test_empties
49
- f = Factbase::Fact.new(Factbase.new, Mutex.new, {})
49
+ f = Factbase::Fact.new({})
50
50
  a = Factbase::Accum.new(f, {}, false)
51
51
  assert_nil(a['foo'])
52
52
  end
53
53
 
54
54
  def test_prints_to_string
55
55
  map = {}
56
- f = Factbase::Fact.new(Factbase.new, Mutex.new, map)
56
+ f = Factbase::Fact.new(map)
57
57
  props = {}
58
58
  a = Factbase::Accum.new(f, props, true)
59
59
  a.foo = 42
@@ -14,7 +14,7 @@ require_relative '../../lib/factbase/fact'
14
14
  class TestFact < Factbase::Test
15
15
  def test_injects_data_correctly
16
16
  map = {}
17
- f = Factbase::Fact.new(Factbase.new, Mutex.new, map)
17
+ f = Factbase::Fact.new(map)
18
18
  f.foo = 1
19
19
  f.bar = 2
20
20
  f.bar = 3
@@ -25,7 +25,7 @@ class TestFact < Factbase::Test
25
25
 
26
26
  def test_simple_resetting
27
27
  map = {}
28
- f = Factbase::Fact.new(Factbase.new, Mutex.new, map)
28
+ f = Factbase::Fact.new(map)
29
29
  f.foo = 42
30
30
  assert_equal(42, f.foo, f.to_s)
31
31
  f.foo = 256
@@ -35,7 +35,7 @@ class TestFact < Factbase::Test
35
35
 
36
36
  def test_keeps_values_unique
37
37
  map = {}
38
- f = Factbase::Fact.new(Factbase.new, Mutex.new, map)
38
+ f = Factbase::Fact.new(map)
39
39
  f.foo = 42
40
40
  f.foo = 'Hello'
41
41
  assert_equal(2, map['foo'].size)
@@ -44,28 +44,28 @@ class TestFact < Factbase::Test
44
44
  end
45
45
 
46
46
  def test_fails_when_empty
47
- f = Factbase::Fact.new(Factbase.new, Mutex.new, {})
47
+ f = Factbase::Fact.new({})
48
48
  assert_raises(StandardError) do
49
49
  f.something
50
50
  end
51
51
  end
52
52
 
53
53
  def test_fails_when_setting_nil
54
- f = Factbase::Fact.new(Factbase.new, Mutex.new, {})
54
+ f = Factbase::Fact.new({})
55
55
  assert_raises(StandardError) do
56
56
  f.foo = nil
57
57
  end
58
58
  end
59
59
 
60
60
  def test_fails_when_setting_empty
61
- f = Factbase::Fact.new(Factbase.new, Mutex.new, {})
61
+ f = Factbase::Fact.new({})
62
62
  assert_raises(StandardError) do
63
63
  f.foo = ''
64
64
  end
65
65
  end
66
66
 
67
67
  def test_fails_when_not_found
68
- f = Factbase::Fact.new(Factbase.new, Mutex.new, {})
68
+ f = Factbase::Fact.new({})
69
69
  f.first = 42
70
70
  assert_raises(StandardError) do
71
71
  f.second
@@ -73,21 +73,21 @@ class TestFact < Factbase::Test
73
73
  end
74
74
 
75
75
  def test_set_by_name
76
- f = Factbase::Fact.new(Factbase.new, Mutex.new, {})
76
+ f = Factbase::Fact.new({})
77
77
  f.send(:_foo_bar=, 42)
78
78
  assert_equal(42, f._foo_bar, f.to_s)
79
79
  end
80
80
 
81
81
  def test_set_twice_same_value
82
82
  map = {}
83
- f = Factbase::Fact.new(Factbase.new, Mutex.new, map)
83
+ f = Factbase::Fact.new(map)
84
84
  f.foo = 42
85
85
  f.foo = 42
86
86
  assert_equal([42], map['foo'])
87
87
  end
88
88
 
89
89
  def test_time_in_utc
90
- f = Factbase::Fact.new(Factbase.new, Mutex.new, {})
90
+ f = Factbase::Fact.new({})
91
91
  t = Time.now
92
92
  f.foo = t
93
93
  assert_equal(t.utc, f.foo)
@@ -95,13 +95,13 @@ class TestFact < Factbase::Test
95
95
  end
96
96
 
97
97
  def test_some_names_are_prohibited
98
- f = Factbase::Fact.new(Factbase.new, Mutex.new, {})
98
+ f = Factbase::Fact.new({})
99
99
  assert_raises(StandardError) { f.to_s = 42 }
100
100
  assert_raises(StandardError) { f.class = 42 }
101
101
  end
102
102
 
103
103
  def test_get_all_properties
104
- f = Factbase::Fact.new(Factbase.new, Mutex.new, {})
104
+ f = Factbase::Fact.new({})
105
105
  f.foo = 42
106
106
  assert_includes(f.all_properties, 'foo')
107
107
  end
@@ -48,6 +48,13 @@ class TestLogged < Factbase::Test
48
48
  assert_includes(log.to_s, 'touched', log)
49
49
  end
50
50
 
51
+ def test_with_slow_txn
52
+ log = Loog::Buffer.new
53
+ fb = Factbase::Logged.new(Factbase.new, log, time_tolerate: 0.1)
54
+ fb.txn { sleep 0.4 }
55
+ assert_includes(log.to_s, '(slow!)', log)
56
+ end
57
+
51
58
  def test_with_txn_rollback
52
59
  log = Loog::Buffer.new
53
60
  fb = Factbase::Logged.new(Factbase.new, log)
@@ -7,16 +7,56 @@ require_relative '../test__helper'
7
7
  require 'time'
8
8
  require_relative '../../lib/factbase'
9
9
  require_relative '../../lib/factbase/query'
10
+ require_relative '../../lib/factbase/cached/cached_factbase'
11
+ require_relative '../../lib/factbase/indexed/indexed_factbase'
12
+ require_relative '../../lib/factbase/sync/sync_factbase'
10
13
 
11
14
  # Query test.
12
15
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
13
16
  # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
14
17
  # License:: MIT
15
18
  class TestQuery < Factbase::Test
19
+ def test_stories
20
+ with_factbases do |badge, fb|
21
+ Dir[File.join(__dir__, '../../fixtures/stories/**/*.yml')].each do |fixture|
22
+ base = File.basename(fixture)
23
+ story = YAML.load_file(fixture)
24
+ 2.times do
25
+ fb.query('(always)').delete!
26
+ story['facts'].each do |y|
27
+ f = fb.insert
28
+ y.each do |k, vv|
29
+ vv = [vv] unless vv.is_a?(Array)
30
+ vv.each do |v|
31
+ f.send(:"#{k}=", v)
32
+ end
33
+ end
34
+ end
35
+ story['queries'].each do |q|
36
+ qry = q['query']
37
+ if q['size']
38
+ size = q['size']
39
+ assert_equal(size, fb.query(qry).each.to_a.size, "#{base}: #{qry} at #{badge}")
40
+ fb.txn do |fbt|
41
+ assert_equal(size, fbt.query(qry).each.to_a.size, "#{base}: #{qry} at #{badge} (in txn)")
42
+ end
43
+ else
44
+ ret = q['one']
45
+ assert_equal(ret, fb.query(qry).one, "#{base}: #{qry} at #{badge}")
46
+ fb.txn do |fbt|
47
+ assert_equal(ret, fbt.query(qry).one, "#{base}: #{qry} at #{badge} (in txn)")
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+
16
56
  def test_simple_parsing
17
57
  maps = []
18
58
  maps << { 'foo' => [42] }
19
- q = Factbase::Query.new(Factbase.new, maps, Mutex.new, '(eq foo 42)')
59
+ q = Factbase::Query.new(maps, '(eq foo 42)', Factbase.new)
20
60
  assert_equal(
21
61
  1,
22
62
  q.each do |f|
@@ -26,12 +66,7 @@ class TestQuery < Factbase::Test
26
66
  end
27
67
 
28
68
  def test_complex_parsing
29
- maps = [
30
- { 'num' => [42], 'name' => ['Jeff'] },
31
- { 'pi' => [3.14], 'num' => [42, 66, 0], 'name' => ['peter'] },
32
- { 'time' => [Time.now - 100], 'num' => [0], 'hi' => [4], 'nome' => ['Walter'] }
33
- ]
34
- {
69
+ queries = {
35
70
  '(eq num 444)' => 0,
36
71
  '(eq hi 4)' => 1,
37
72
  '(eq time 0)' => 0,
@@ -71,11 +106,18 @@ class TestQuery < Factbase::Test
71
106
  '(undef something)' => 3,
72
107
  "(or (eq num +66) (lt time #{(Time.now - 200).utc.iso8601}))" => 1,
73
108
  '(eq 3 (agg (eq num $num) (count)))' => 1
74
- }.each do |q, r|
75
- fb = Factbase.new(maps)
76
- assert_equal(r, fb.query(q).each.to_a.size, q)
77
- fb.txn do |fbt|
78
- assert_equal(r, fbt.query(q).each.to_a.size, q)
109
+ }
110
+ maps = [
111
+ { 'num' => [42], 'name' => ['Jeff'] },
112
+ { 'num' => [42, 66, 0], 'pi' => [3.14], 'name' => ['peter'] },
113
+ { 'num' => [0], 'time' => [Time.now - 100], 'hi' => [4], 'nome' => ['Walter'] }
114
+ ]
115
+ with_factbases(maps) do |badge, fb|
116
+ queries.each do |q, r|
117
+ assert_equal(r, fb.query(q).each.to_a.size, "#{q} in #{badge}")
118
+ fb.txn do |fbt|
119
+ assert_equal(r, fbt.query(q).each.to_a.size, "#{q} in #{badge} (txn)")
120
+ end
79
121
  end
80
122
  end
81
123
  end
@@ -84,7 +126,7 @@ class TestQuery < Factbase::Test
84
126
  maps = []
85
127
  now = Time.now.utc
86
128
  maps << { 'foo' => [now] }
87
- q = Factbase::Query.new(Factbase.new, maps, Mutex.new, "(eq foo #{now.iso8601})")
129
+ q = Factbase::Query.new(maps, "(eq foo #{now.iso8601})", Factbase.new)
88
130
  assert_equal(1, q.each.to_a.size)
89
131
  end
90
132
 
@@ -94,7 +136,7 @@ class TestQuery < Factbase::Test
94
136
  { 'bar' => [4, 5] },
95
137
  { 'bar' => [5] }
96
138
  ]
97
- q = Factbase::Query.new(Factbase.new, maps, Mutex.new, '(eq bar 5)')
139
+ q = Factbase::Query.new(maps, '(eq bar 5)', Factbase.new)
98
140
  assert_equal(2, q.delete!)
99
141
  assert_equal(1, maps.size)
100
142
  end
@@ -104,18 +146,20 @@ class TestQuery < Factbase::Test
104
146
  { 'foo' => [42] },
105
147
  { 'bar' => [4, 5] }
106
148
  ]
107
- {
108
- '(agg (exists foo) (first foo))' => [42],
109
- '(agg (exists z) (first z))' => nil,
110
- '(agg (always) (count))' => 2,
111
- '(agg (eq bar $v) (count))' => 1,
112
- '(agg (eq z 40) (count))' => 0
113
- }.each do |q, expected|
114
- result = Factbase::Query.new(Factbase.new, maps, Mutex.new, q).one(v: 4)
115
- if expected.nil?
116
- assert_nil(result, "#{q} -> nil")
117
- else
118
- assert_equal(expected, result, "#{q} -> #{expected}")
149
+ with_factbases(maps) do |badge, fb|
150
+ {
151
+ '(agg (exists foo) (first foo))' => [42],
152
+ '(agg (exists z) (first z))' => nil,
153
+ '(agg (always) (count))' => 2,
154
+ '(agg (eq bar $v) (count))' => 1,
155
+ '(agg (eq z 40) (count))' => 0
156
+ }.each do |q, expected|
157
+ result = fb.query(q).one(fb, v: 4)
158
+ if expected.nil?
159
+ assert_nil(result, "#{q} -> nil in #{badge}")
160
+ else
161
+ assert_equal(expected, result, "#{q} -> #{expected} in #{badge}")
162
+ end
119
163
  end
120
164
  end
121
165
  end
@@ -126,28 +170,30 @@ class TestQuery < Factbase::Test
126
170
  { 'bar' => [4, 5] },
127
171
  { 'bar' => [5] }
128
172
  ]
129
- q = Factbase::Query.new(Factbase.new, maps, Mutex.new, '(never)')
130
- assert_equal(0, q.delete!)
131
- assert_equal(3, maps.size)
173
+ with_factbases(maps) do |badge, fb|
174
+ q = fb.query('(never)')
175
+ assert_equal(0, q.delete!, "#{q} in #{badge}")
176
+ assert_equal(3, maps.size, "#{q} in #{badge}")
177
+ end
132
178
  end
133
179
 
134
180
  def test_to_array
135
181
  maps = []
136
182
  maps << { 'foo' => [42] }
137
- assert_equal(1, Factbase::Query.new(Factbase.new, maps, Mutex.new, '(eq foo 42)').each.to_a.size)
183
+ assert_equal(1, Factbase::Query.new(maps, '(eq foo 42)', Factbase.new).each.to_a.size)
138
184
  end
139
185
 
140
186
  def test_returns_int
141
187
  maps = []
142
188
  maps << { 'foo' => [1] }
143
- q = Factbase::Query.new(Factbase.new, maps, Mutex.new, '(eq foo 1)')
189
+ q = Factbase::Query.new(maps, '(eq foo 1)', Factbase.new)
144
190
  assert_equal(1, q.each(&:to_s))
145
191
  end
146
192
 
147
193
  def test_with_aliases
148
194
  maps = []
149
195
  maps << { 'foo' => [42] }
150
- assert_equal(45, Factbase::Query.new(Factbase.new, maps, Mutex.new, '(as bar (plus foo 3))').each.to_a[0].bar)
196
+ assert_equal(45, Factbase::Query.new(maps, '(as bar (plus foo 3))', Factbase.new).each.to_a[0].bar)
151
197
  assert_equal(1, maps[0].size)
152
198
  end
153
199
 
@@ -157,22 +203,38 @@ class TestQuery < Factbase::Test
157
203
  { 'foo' => [17] }
158
204
  ]
159
205
  found = 0
160
- Factbase::Query.new(Factbase.new, maps, Mutex.new, '(eq foo $bar)').each(bar: [42]) do
206
+ Factbase::Query.new(maps, '(eq foo $bar)', Factbase.new).each(Factbase.new, bar: [42]) do
161
207
  found += 1
162
208
  end
163
209
  assert_equal(1, found)
164
- assert_equal(1, Factbase::Query.new(Factbase.new, maps, Mutex.new, '(eq foo $bar)').each(bar: 42).to_a.size)
165
- assert_equal(0, Factbase::Query.new(Factbase.new, maps, Mutex.new, '(eq foo $bar)').each(bar: 555).to_a.size)
210
+ assert_equal(1, Factbase::Query.new(maps, '(eq foo $bar)', Factbase.new).each(Factbase.new, bar: 42).to_a.size)
211
+ assert_equal(0, Factbase::Query.new(maps, '(eq foo $bar)', Factbase.new).each(Factbase.new, bar: 555).to_a.size)
166
212
  end
167
213
 
168
214
  def test_with_nil_alias
169
215
  maps = [{ 'foo' => [42] }]
170
- assert_nil(Factbase::Query.new(Factbase.new, maps, Mutex.new, '(as bar (plus xxx 3))').each.to_a[0]['bar'])
216
+ assert_nil(Factbase::Query.new(maps, '(as bar (plus xxx 3))', Factbase.new).each.to_a[0]['bar'])
171
217
  end
172
218
 
173
219
  def test_get_all_properties
174
220
  maps = [{ 'foo' => [42] }]
175
- f = Factbase::Query.new(Factbase.new, maps, Mutex.new, '(always)').each.to_a[0]
221
+ f = Factbase::Query.new(maps, '(always)', Factbase.new).each.to_a[0]
176
222
  assert_includes(f.all_properties, 'foo')
177
223
  end
224
+
225
+ private
226
+
227
+ def with_factbases(maps = [], &)
228
+ {
229
+ 'plain' => Factbase.new(maps),
230
+ 'sync+plain' => Factbase::SyncFactbase.new(Factbase.new(maps)),
231
+ 'indexed+plain' => Factbase::IndexedFactbase.new(Factbase.new(maps)),
232
+ 'cached+plain' => Factbase::CachedFactbase.new(Factbase.new(maps)),
233
+ 'indexed+cached+plain' => Factbase::IndexedFactbase.new(Factbase::CachedFactbase.new(Factbase.new(maps))),
234
+ 'cached+indexed+plain' => Factbase::CachedFactbase.new(Factbase::IndexedFactbase.new(Factbase.new(maps))),
235
+ 'sync+cached+indexed+plain' => Factbase::SyncFactbase.new(
236
+ Factbase::CachedFactbase.new(Factbase::IndexedFactbase.new(Factbase.new(maps)))
237
+ )
238
+ }.each(&)
239
+ end
178
240
  end
@@ -51,7 +51,7 @@ class TestRules < Factbase::Test
51
51
  fb = Factbase::Rules.new(Factbase.new, '(always)')
52
52
  f = fb.insert
53
53
  f.foo = 42
54
- assert_equal(1, fb.query('(agg (eq foo $v) (count))').one(v: 42))
54
+ assert_equal(1, fb.query('(agg (eq foo $v) (count))').one(Factbase.new, v: 42))
55
55
  end
56
56
 
57
57
  def test_check_only_when_txn_is_closed