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
@@ -18,7 +18,7 @@ class TestSyntax < Factbase::Test
18
18
  "(foo 'one two three ')",
19
19
  "(foo 'one two three ' 'tail tail')"
20
20
  ].each do |q|
21
- assert_equal(q, Factbase::Syntax.new(Factbase.new, q).to_term.to_s, q)
21
+ assert_equal(q, Factbase::Syntax.new(q).to_term.to_s, q)
22
22
  end
23
23
  end
24
24
 
@@ -28,7 +28,7 @@ class TestSyntax < Factbase::Test
28
28
  '(foo (bar (a (b c (f $bar)))))' => true,
29
29
  '(foo (bar (a (b c (f bar)))))' => false
30
30
  }.each do |q, a|
31
- assert_equal(a, Factbase::Syntax.new(Factbase.new, q).to_term.abstract?)
31
+ assert_equal(a, Factbase::Syntax.new(q).to_term.abstract?)
32
32
  end
33
33
  end
34
34
 
@@ -38,7 +38,7 @@ class TestSyntax < Factbase::Test
38
38
  '(foo "bar")' => true,
39
39
  '(agg (always) (max id))' => true
40
40
  }.each do |q, a|
41
- assert_equal(a, Factbase::Syntax.new(Factbase.new, q).to_term.static?)
41
+ assert_equal(a, Factbase::Syntax.new(q).to_term.static?)
42
42
  end
43
43
  end
44
44
 
@@ -53,7 +53,7 @@ class TestSyntax < Factbase::Test
53
53
  "(foo 'Hello,\n\nworld!\r\t\n')\n",
54
54
  "(or ( a 4) (b 5) (always) (and (always) (c 5) \t\t(r 7 w8s w8is 'Foo')))"
55
55
  ].each do |q|
56
- refute_nil(Factbase::Syntax.new(Factbase.new, q).to_term)
56
+ refute_nil(Factbase::Syntax.new(q).to_term)
57
57
  end
58
58
  end
59
59
 
@@ -73,7 +73,7 @@ class TestSyntax < Factbase::Test
73
73
  '(eq t 3.0e+21)',
74
74
  "(foo (x (f (t (y 42 'Hey you'))) (never) (r 3)) y z)"
75
75
  ].each do |q|
76
- assert_equal(q, Factbase::Syntax.new(Factbase.new, q).to_term.to_s, q)
76
+ assert_equal(q, Factbase::Syntax.new(q).to_term.to_s, q)
77
77
  end
78
78
  end
79
79
 
@@ -88,7 +88,7 @@ class TestSyntax < Factbase::Test
88
88
  '(or (eq bar 888) (eq z 1))' => true,
89
89
  "(or (gt bar 100) (eq foo 'Hello, world!'))" => true
90
90
  }.each do |k, v|
91
- assert_equal(v, Factbase::Syntax.new(Factbase.new, k).to_term.evaluate(m, []), k)
91
+ assert_equal(v, Factbase::Syntax.new(k).to_term.evaluate(m, [], Factbase.new), k)
92
92
  end
93
93
  end
94
94
 
@@ -113,33 +113,46 @@ class TestSyntax < Factbase::Test
113
113
  '"'
114
114
  ].each do |q|
115
115
  msg = assert_raises(q) do
116
- Factbase::Syntax.new(Factbase.new, q).to_term
116
+ Factbase::Syntax.new(q).to_term
117
117
  end.message
118
118
  assert_includes(msg, q, msg)
119
119
  end
120
120
  end
121
121
 
122
+ def test_raises_on_broken_syntax
123
+ 100.times do
124
+ q = [
125
+ '(', ')', '#test', '$foo', '%what',
126
+ '"hello"', '42', '+', '?', '!', '\"',
127
+ '\'', 'привет'
128
+ ].shuffle.join(' . ')
129
+ assert_raises(Factbase::Syntax::Broken, q) do
130
+ Factbase::Syntax.new(q).to_term
131
+ end
132
+ end
133
+ end
134
+
122
135
  def test_simplification
123
136
  {
124
137
  '(foo)' => '(foo)',
125
138
  '(and (foo) (foo))' => '(foo)',
126
139
  '(and (foo) (or (and (eq a 1))) (eq a 1) (foo))' => '(and (foo) (eq a 1))'
127
140
  }.each do |s, t|
128
- assert_equal(t, Factbase::Syntax.new(Factbase.new, s).to_term.to_s)
141
+ assert_equal(t, Factbase::Syntax.new(s).to_term.to_s)
129
142
  end
130
143
  end
131
144
 
132
145
  def test_fails_when_term_is_not_a_class
133
- assert_raises(StandardError) { Factbase::Syntax.new(Factbase.new, '(foo 1)', term: 'hello') }
146
+ assert_raises(StandardError) { Factbase::Syntax.new('(foo 1)', term: 'hello') }
134
147
  end
135
148
 
136
149
  def test_fails_when_term_is_wrong_class
137
- assert_raises(StandardError) { Factbase::Syntax.new(Factbase.new, '(bar 1)', term: String).to_term }
150
+ assert_raises(StandardError) { Factbase::Syntax.new('(bar 1)', term: String).to_term }
138
151
  end
139
152
 
140
153
  def test_fails_when_term_is_incorrectly_defined_class
141
154
  assert_includes(
142
- assert_raises(StandardError) { Factbase::Syntax.new(Factbase.new, '(bar 1)', term: FakeTerm).to_term }.message,
155
+ assert_raises(StandardError) { Factbase::Syntax.new('(bar 1)', term: FakeTerm).to_term }.message,
143
156
  'wrong number of arguments'
144
157
  )
145
158
  end
@@ -14,9 +14,9 @@ require_relative '../../lib/factbase/fact'
14
14
  # License:: MIT
15
15
  class TestTee < Factbase::Test
16
16
  def test_two_facts
17
- prim = Factbase::Fact.new(Factbase.new, Mutex.new, {})
17
+ prim = Factbase::Fact.new({})
18
18
  prim.foo = 42
19
- upper = Factbase::Fact.new(Factbase.new, Mutex.new, {})
19
+ upper = Factbase::Fact.new({})
20
20
  upper.bar = 13
21
21
  t = Factbase::Tee.new(prim, upper)
22
22
  assert_equal(42, t.foo)
@@ -24,9 +24,9 @@ class TestTee < Factbase::Test
24
24
  end
25
25
 
26
26
  def test_all_properties
27
- prim = Factbase::Fact.new(Factbase.new, Mutex.new, {})
27
+ prim = Factbase::Fact.new({})
28
28
  prim.foo = 42
29
- upper = Factbase::Fact.new(Factbase.new, Mutex.new, {})
29
+ upper = Factbase::Fact.new({})
30
30
  upper.bar = 13
31
31
  t = Factbase::Tee.new(prim, upper)
32
32
  assert_includes(t.all_properties, 'foo')
@@ -35,18 +35,18 @@ class TestTee < Factbase::Test
35
35
 
36
36
  def test_recursively
37
37
  map = {}
38
- prim = Factbase::Fact.new(Factbase.new, Mutex.new, map)
38
+ prim = Factbase::Fact.new(map)
39
39
  prim.foo = 42
40
- t = Factbase::Tee.new(nil, { 'bar' => 7 })
40
+ t = Factbase::Tee.new(Factbase::Fact.new({}), { 'bar' => 7 })
41
41
  assert_equal(7, t['$bar'])
42
42
  t = Factbase::Tee.new(prim, t)
43
43
  assert_equal(7, t['$bar'])
44
44
  end
45
45
 
46
46
  def test_prints_to_string
47
- prim = Factbase::Fact.new(Factbase.new, Mutex.new, {})
47
+ prim = Factbase::Fact.new({})
48
48
  prim.foo = 42
49
- upper = Factbase::Fact.new(Factbase.new, Mutex.new, {})
49
+ upper = Factbase::Fact.new({})
50
50
  upper.bar = 13
51
51
  t = Factbase::Tee.new(prim, upper)
52
52
  assert_equal('[ foo: [42] ]', t.to_s)
@@ -4,6 +4,7 @@
4
4
  # SPDX-License-Identifier: MIT
5
5
 
6
6
  require_relative '../test__helper'
7
+ require_relative '../../lib/factbase'
7
8
  require_relative '../../lib/factbase/term'
8
9
 
9
10
  # Term test.
@@ -12,65 +13,73 @@ require_relative '../../lib/factbase/term'
12
13
  # License:: MIT
13
14
  class TestTerm < Factbase::Test
14
15
  def test_false_matching
15
- t = Factbase::Term.new(Factbase.new, :never, [])
16
- refute(t.evaluate(fact('foo' => [100]), []))
16
+ t = Factbase::Term.new(:never, [])
17
+ refute(t.evaluate(fact('foo' => [100]), [], Factbase.new))
17
18
  end
18
19
 
19
20
  def test_size_matching
20
- t = Factbase::Term.new(Factbase.new, :size, [:foo])
21
- assert_equal(3, t.evaluate(fact('foo' => [42, 12, -90]), []))
22
- assert_equal(0, t.evaluate(fact('bar' => 100), []))
21
+ t = Factbase::Term.new(:size, [:foo])
22
+ assert_equal(3, t.evaluate(fact('foo' => [42, 12, -90]), [], Factbase.new))
23
+ assert_equal(0, t.evaluate(fact('bar' => 100), [], Factbase.new))
23
24
  end
24
25
 
25
26
  def test_exists_matching
26
- t = Factbase::Term.new(Factbase.new, :exists, [:foo])
27
- assert(t.evaluate(fact('foo' => [42, 12, -90]), []))
28
- refute(t.evaluate(fact('bar' => 100), []))
27
+ t = Factbase::Term.new(:exists, [:foo])
28
+ assert(t.evaluate(fact('foo' => [42, 12, -90]), [], Factbase.new))
29
+ refute(t.evaluate(fact('bar' => 100), [], Factbase.new))
29
30
  end
30
31
 
31
32
  def test_absent_matching
32
- t = Factbase::Term.new(Factbase.new, :absent, [:foo])
33
- assert(t.evaluate(fact('z' => [42, 12, -90]), []))
34
- refute(t.evaluate(fact('foo' => 100), []))
33
+ t = Factbase::Term.new(:absent, [:foo])
34
+ assert(t.evaluate(fact('z' => [42, 12, -90]), [], Factbase.new))
35
+ refute(t.evaluate(fact('foo' => 100), [], Factbase.new))
35
36
  end
36
37
 
37
38
  def test_type_matching
38
- t = Factbase::Term.new(Factbase.new, :type, [:foo])
39
- assert_equal('Integer', t.evaluate(fact('foo' => 42), []))
40
- assert_equal('Integer', t.evaluate(fact('foo' => [42]), []))
41
- assert_equal('Array', t.evaluate(fact('foo' => [1, 2, 3]), []))
42
- assert_equal('String', t.evaluate(fact('foo' => 'Hello, world!'), []))
43
- assert_equal('Float', t.evaluate(fact('foo' => 3.14), []))
44
- assert_equal('Time', t.evaluate(fact('foo' => Time.now), []))
45
- assert_equal('Integer', t.evaluate(fact('foo' => 1_000_000_000_000_000), []))
46
- assert_equal('nil', t.evaluate(fact, []))
39
+ t = Factbase::Term.new(:type, [:foo])
40
+ assert_equal('Integer', t.evaluate(fact('foo' => 42), [], Factbase.new))
41
+ assert_equal('Integer', t.evaluate(fact('foo' => [42]), [], Factbase.new))
42
+ assert_equal('Array', t.evaluate(fact('foo' => [1, 2, 3]), [], Factbase.new))
43
+ assert_equal('String', t.evaluate(fact('foo' => 'Hello, world!'), [], Factbase.new))
44
+ assert_equal('Float', t.evaluate(fact('foo' => 3.14), [], Factbase.new))
45
+ assert_equal('Time', t.evaluate(fact('foo' => Time.now), [], Factbase.new))
46
+ assert_equal('Integer', t.evaluate(fact('foo' => 1_000_000_000_000_000), [], Factbase.new))
47
+ assert_equal('nil', t.evaluate(fact, [], Factbase.new))
47
48
  end
48
49
 
49
50
  def test_past
50
- t = Factbase::Term.new(Factbase.new, :prev, [:foo])
51
- assert_nil(t.evaluate(fact('foo' => 4), []))
52
- assert_equal([4], t.evaluate(fact('foo' => 5), []))
51
+ t = Factbase::Term.new(:prev, [:foo])
52
+ assert_nil(t.evaluate(fact('foo' => 4), [], Factbase.new))
53
+ assert_equal([4], t.evaluate(fact('foo' => 5), [], Factbase.new))
53
54
  end
54
55
 
55
56
  def test_at
56
- t = Factbase::Term.new(Factbase.new, :at, [1, :foo])
57
- assert_nil(t.evaluate(fact('foo' => 4), []))
58
- assert_equal(5, t.evaluate(fact('foo' => [4, 5]), []))
57
+ t = Factbase::Term.new(:at, [1, :foo])
58
+ assert_nil(t.evaluate(fact('foo' => 4), [], Factbase.new))
59
+ assert_equal(5, t.evaluate(fact('foo' => [4, 5]), [], Factbase.new))
59
60
  end
60
61
 
61
62
  def test_report_missing_term
62
- t = Factbase::Term.new(Factbase.new, :something, [])
63
+ t = Factbase::Term.new(:something, [])
63
64
  msg = assert_raises(StandardError) do
64
- t.evaluate(fact, [])
65
+ t.evaluate(fact, [], Factbase.new)
65
66
  end.message
66
67
  assert_includes(msg, 'not defined at (something)', msg)
67
68
  end
68
69
 
69
70
  def test_report_other_error
70
- t = Factbase::Term.new(Factbase.new, :at, [])
71
+ t = Factbase::Term.new(:at, [])
71
72
  msg = assert_raises(StandardError) do
72
- t.evaluate(fact, [])
73
+ t.evaluate(fact, [], Factbase.new)
73
74
  end.message
74
75
  assert_includes(msg, 'at (at)', msg)
75
76
  end
77
+
78
+ def test_redresses_itself
79
+ t = Factbase::Term.new(:something, [])
80
+ require_relative '../../lib/factbase/indexed/indexed_term'
81
+ t.redress!(Factbase::IndexedTerm)
82
+ require_relative '../../lib/factbase/cached/cached_term'
83
+ t.redress!(Factbase::CachedTerm)
84
+ end
76
85
  end
data/test/test__helper.rb CHANGED
@@ -22,7 +22,7 @@ require_relative '../lib/factbase'
22
22
  # Default methods for all tests.
23
23
  class Factbase::Test < Minitest::Test
24
24
  def fact(map = {})
25
- require 'factbase/fact'
26
- Factbase::Fact.new(Factbase.new, Mutex.new, map)
25
+ require_relative '../lib/factbase/fact'
26
+ Factbase::Fact.new(map)
27
27
  end
28
28
  end
@@ -41,6 +41,12 @@ class TestFactbase < Factbase::Test
41
41
  end
42
42
  end
43
43
 
44
+ def test_converts_query_to_term
45
+ fb = Factbase.new
46
+ term = fb.to_term('(eq foo 42)')
47
+ assert_equal('(eq foo 42)', term.to_s)
48
+ end
49
+
44
50
  def test_simple_setting
45
51
  fb = Factbase.new
46
52
  fb.insert
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: factbase
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.5
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-03-03 00:00:00.000000000 Z
10
+ date: 2025-03-12 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: backtrace
@@ -162,24 +162,45 @@ files:
162
162
  - benchmark/bench_query.rb
163
163
  - benchmark/bench_taped.rb
164
164
  - factbase.gemspec
165
+ - fixtures/stories/agg.yml
166
+ - fixtures/stories/always.yml
167
+ - fixtures/stories/as.yml
168
+ - fixtures/stories/count.yml
169
+ - fixtures/stories/eq.yml
170
+ - fixtures/stories/gt.yml
171
+ - fixtures/stories/join.yml
172
+ - fixtures/stories/max.yml
173
+ - fixtures/stories/min.yml
174
+ - fixtures/stories/nth.yml
175
+ - fixtures/stories/or.yml
176
+ - fixtures/stories/sprintf.yml
177
+ - fixtures/stories/sum.yml
165
178
  - lib/factbase.rb
166
179
  - lib/factbase/accum.rb
180
+ - lib/factbase/cached/cached_fact.rb
181
+ - lib/factbase/cached/cached_factbase.rb
182
+ - lib/factbase/cached/cached_query.rb
183
+ - lib/factbase/cached/cached_term.rb
167
184
  - lib/factbase/churn.rb
168
185
  - lib/factbase/fact.rb
169
186
  - lib/factbase/flatten.rb
187
+ - lib/factbase/indexed/indexed_fact.rb
188
+ - lib/factbase/indexed/indexed_factbase.rb
189
+ - lib/factbase/indexed/indexed_query.rb
190
+ - lib/factbase/indexed/indexed_term.rb
170
191
  - lib/factbase/inv.rb
171
192
  - lib/factbase/light.rb
172
193
  - lib/factbase/logged.rb
173
194
  - lib/factbase/pre.rb
174
195
  - lib/factbase/query.rb
175
- - lib/factbase/query_once.rb
176
196
  - lib/factbase/rules.rb
197
+ - lib/factbase/sync/sync_factbase.rb
198
+ - lib/factbase/sync/sync_query.rb
177
199
  - lib/factbase/syntax.rb
178
200
  - lib/factbase/tallied.rb
179
201
  - lib/factbase/taped.rb
180
202
  - lib/factbase/tee.rb
181
203
  - lib/factbase/term.rb
182
- - lib/factbase/term_once.rb
183
204
  - lib/factbase/terms/aggregates.rb
184
205
  - lib/factbase/terms/aliases.rb
185
206
  - lib/factbase/terms/casting.rb
@@ -195,6 +216,10 @@ files:
195
216
  - lib/factbase/to_xml.rb
196
217
  - lib/factbase/to_yaml.rb
197
218
  - renovate.json
219
+ - test/factbase/cached/test_cached_factbase.rb
220
+ - test/factbase/cached/test_cached_query.rb
221
+ - test/factbase/indexed/test_indexed_query.rb
222
+ - test/factbase/sync/test_sync_query.rb
198
223
  - test/factbase/terms/test_aggregates.rb
199
224
  - test/factbase/terms/test_aliases.rb
200
225
  - test/factbase/terms/test_casting.rb
@@ -1,54 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
4
- # SPDX-License-Identifier: MIT
5
-
6
- require_relative '../factbase'
7
-
8
- # Query with a cache, a decorator of another query.
9
- #
10
- # It is NOT thread-safe!
11
- #
12
- # Author:: Yegor Bugayenko (yegor256@gmail.com)
13
- # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
14
- # License:: MIT
15
- class Factbase::QueryOnce
16
- # Constructor.
17
- # @param [Factbase] fb Factbase
18
- # @param [Factbase::Query] query Original query
19
- # @param [Array<Hash>] maps Where to search
20
- def initialize(fb, query, maps)
21
- @fb = fb
22
- @query = query
23
- @maps = maps
24
- end
25
-
26
- # Iterate facts one by one.
27
- # @param [Hash] params Optional params accessible in the query via the "$" symbol
28
- # @yield [Fact] Facts one-by-one
29
- # @return [Integer] Total number of facts yielded
30
- def each(params = {}, &)
31
- unless block_given?
32
- return to_enum(__method__, params) if Factbase::Syntax.new(@fb, @query).to_term.abstract?
33
- key = [@query.to_s, @maps.object_id]
34
- before = @fb.cache[key]
35
- @fb.cache[key] = to_enum(__method__, params).to_a if before.nil?
36
- return @fb.cache[key]
37
- end
38
- @query.each(params, &)
39
- end
40
-
41
- # Read a single value.
42
- # @param [Hash] params Optional params accessible in the query via the "$" symbol
43
- # @return The value evaluated
44
- def one(params = {})
45
- @query.one(params)
46
- end
47
-
48
- # Delete all facts that match the query.
49
- # @return [Integer] Total number of facts deleted
50
- def delete!
51
- @fb.cache.clear
52
- @query.delete!
53
- end
54
- end
@@ -1,67 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
4
- # SPDX-License-Identifier: MIT
5
-
6
- require_relative '../factbase'
7
-
8
- # Term with a cache, a decorator of another term.
9
- #
10
- # It is NOT thread-safe!
11
- #
12
- # Author:: Yegor Bugayenko (yegor256@gmail.com)
13
- # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
14
- # License:: MIT
15
- class Factbase::TermOnce < Factbase::Term
16
- # Constructor.
17
- # @param [Factbase::Term] term Original term
18
- # @param [Hash] cache The cache
19
- def initialize(term, cache)
20
- super(nil, nil, nil) # just to please Rubocop
21
- @term = term
22
- @cache = cache
23
- @text = @term.to_s
24
- @cacheable = @term.static? && !@term.abstract?
25
- end
26
-
27
- # Does it match the fact?
28
- # @param [Factbase::Fact] fact The fact
29
- # @param [Array<Factbase::Fact>] maps All maps available
30
- # @return [bool] TRUE if matches
31
- def evaluate(fact, maps)
32
- return @term.evaluate(fact, maps) unless @cacheable
33
- key = [@text, maps.object_id]
34
- before = @cache[key]
35
- @cache[key] = @term.evaluate(nil, maps) if before.nil?
36
- @cache[key]
37
- end
38
-
39
- # Simplify it if possible.
40
- # @return [Factbase::Term] New term or itself
41
- def simplify
42
- @term.simplify
43
- end
44
-
45
- # Does it have any dependencies on a fact?
46
- #
47
- # If a term is static, it will return the same value for +evaluate+,
48
- # no matter what is the fact given.
49
- #
50
- # @return [Boolean] TRUE if static
51
- def static?
52
- @term.static?
53
- end
54
-
55
- # Does it have any variables (+$foo+, for example) inside?
56
- #
57
- # @return [Boolean] TRUE if abstract
58
- def abstract?
59
- @term.abstract?
60
- end
61
-
62
- # Turns it into a string.
63
- # @return [String] The string of it
64
- def to_s
65
- @term.to_s
66
- end
67
- end