factbase 0.4.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/.0pdd.yml +1 -1
  3. data/.github/workflows/actionlint.yml +1 -1
  4. data/.github/workflows/benchmark.yml +64 -0
  5. data/.github/workflows/codecov.yml +4 -2
  6. data/.github/workflows/copyrights.yml +2 -2
  7. data/.github/workflows/markdown-lint.yml +1 -1
  8. data/.github/workflows/pdd.yml +1 -1
  9. data/.github/workflows/rake.yml +2 -2
  10. data/.github/workflows/xcop.yml +1 -1
  11. data/.github/workflows/yamllint.yml +1 -1
  12. data/.gitignore +1 -1
  13. data/.rubocop.yml +6 -1
  14. data/.rultor.yml +2 -2
  15. data/.simplecov +1 -1
  16. data/.yamllint.yml +9 -4
  17. data/Gemfile +9 -7
  18. data/Gemfile.lock +98 -78
  19. data/LICENSE.txt +1 -1
  20. data/README.md +33 -0
  21. data/Rakefile +6 -1
  22. data/benchmarks/simple.rb +96 -0
  23. data/factbase.gemspec +1 -1
  24. data/lib/factbase/accum.rb +2 -2
  25. data/lib/factbase/fact.rb +6 -4
  26. data/lib/factbase/flatten.rb +2 -2
  27. data/lib/factbase/inv.rb +2 -2
  28. data/lib/factbase/looged.rb +6 -5
  29. data/lib/factbase/pre.rb +2 -2
  30. data/lib/factbase/query.rb +16 -8
  31. data/lib/factbase/query_once.rb +71 -0
  32. data/lib/factbase/rules.rb +3 -3
  33. data/lib/factbase/syntax.rb +21 -11
  34. data/lib/factbase/tee.rb +2 -2
  35. data/lib/factbase/term.rb +33 -5
  36. data/lib/factbase/term_once.rb +84 -0
  37. data/lib/factbase/terms/aggregates.rb +4 -4
  38. data/lib/factbase/terms/aliases.rb +5 -5
  39. data/lib/factbase/terms/casting.rb +2 -2
  40. data/lib/factbase/terms/debug.rb +2 -2
  41. data/lib/factbase/terms/defn.rb +2 -2
  42. data/lib/factbase/terms/logical.rb +3 -3
  43. data/lib/factbase/terms/math.rb +2 -2
  44. data/lib/factbase/terms/meta.rb +2 -2
  45. data/lib/factbase/terms/ordering.rb +2 -2
  46. data/lib/factbase/terms/strings.rb +2 -2
  47. data/lib/factbase/terms/system.rb +2 -2
  48. data/lib/factbase/to_json.rb +2 -2
  49. data/lib/factbase/to_xml.rb +2 -2
  50. data/lib/factbase/to_yaml.rb +2 -2
  51. data/lib/factbase.rb +16 -6
  52. data/test/factbase/terms/test_aggregates.rb +6 -6
  53. data/test/factbase/terms/test_aliases.rb +6 -6
  54. data/test/factbase/terms/test_casting.rb +6 -6
  55. data/test/factbase/terms/test_debug.rb +53 -0
  56. data/test/factbase/terms/test_defn.rb +15 -15
  57. data/test/factbase/terms/test_logical.rb +15 -13
  58. data/test/factbase/terms/test_math.rb +42 -42
  59. data/test/factbase/terms/test_meta.rb +87 -0
  60. data/test/factbase/terms/test_ordering.rb +45 -0
  61. data/test/factbase/terms/test_strings.rb +8 -8
  62. data/test/factbase/terms/test_system.rb +6 -6
  63. data/test/factbase/test_accum.rb +9 -9
  64. data/test/factbase/test_fact.rb +22 -22
  65. data/test/factbase/test_flatten.rb +6 -6
  66. data/test/factbase/test_inv.rb +3 -3
  67. data/test/factbase/test_looged.rb +13 -13
  68. data/test/factbase/test_pre.rb +2 -2
  69. data/test/factbase/test_query.rb +17 -17
  70. data/test/factbase/test_rules.rb +8 -8
  71. data/test/factbase/test_syntax.rb +30 -9
  72. data/test/factbase/test_tee.rb +11 -11
  73. data/test/factbase/test_term.rb +18 -18
  74. data/test/factbase/test_to_json.rb +4 -4
  75. data/test/factbase/test_to_xml.rb +12 -14
  76. data/test/factbase/test_to_yaml.rb +3 -3
  77. data/test/test__helper.rb +2 -2
  78. data/test/test_factbase.rb +200 -18
  79. metadata +12 -5
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2024-2025 Yegor Bugayenko
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the 'Software'), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ require_relative '../factbase'
24
+
25
+ # Term with a cache, a decorator of another term.
26
+ #
27
+ # It is NOT thread-safe!
28
+ #
29
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
30
+ # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
31
+ # License:: MIT
32
+ class Factbase::TermOnce < Factbase::Term
33
+ # Constructor.
34
+ # @param [Factbase::Term] term Original term
35
+ # @param [Hash] cache The cache
36
+ def initialize(term, cache)
37
+ super(nil, nil, nil) # just to please Rubocop
38
+ @term = term
39
+ @cache = cache
40
+ @text = @term.to_s
41
+ @cacheable = @term.static? && !@term.abstract?
42
+ end
43
+
44
+ # Does it match the fact?
45
+ # @param [Factbase::Fact] fact The fact
46
+ # @param [Array<Factbase::Fact>] maps All maps available
47
+ # @return [bool] TRUE if matches
48
+ def evaluate(fact, maps)
49
+ return @term.evaluate(fact, maps) unless @cacheable
50
+ key = [@text, maps.object_id]
51
+ before = @cache[key]
52
+ @cache[key] = @term.evaluate(nil, maps) if before.nil?
53
+ @cache[key]
54
+ end
55
+
56
+ # Simplify it if possible.
57
+ # @return [Factbase::Term] New term or itself
58
+ def simplify
59
+ @term.simplify
60
+ end
61
+
62
+ # Does it have any dependencies on a fact?
63
+ #
64
+ # If a term is static, it will return the same value for +evaluate+,
65
+ # no matter what is the fact given.
66
+ #
67
+ # @return [Boolean] TRUE if static
68
+ def static?
69
+ @term.static?
70
+ end
71
+
72
+ # Does it have any variables (+$foo+, for example) inside?
73
+ #
74
+ # @return [Boolean] TRUE if abstract
75
+ def abstract?
76
+ @term.abstract?
77
+ end
78
+
79
+ # Turns it into a string.
80
+ # @return [String] The string of it
81
+ def to_s
82
+ @term.to_s
83
+ end
84
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2024 Yegor Bugayenko
3
+ # Copyright (c) 2024-2025 Yegor Bugayenko
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the 'Software'), to deal
@@ -25,7 +25,7 @@ require_relative '../../factbase'
25
25
  # Aggregating terms.
26
26
  #
27
27
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
28
- # Copyright:: Copyright (c) 2024 Yegor Bugayenko
28
+ # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
29
29
  # License:: MIT
30
30
  module Factbase::Term::Aggregates
31
31
  def min(_fact, maps)
@@ -81,7 +81,7 @@ module Factbase::Term::Aggregates
81
81
  raise "A term expected, but '#{selector}' provided" unless selector.is_a?(Factbase::Term)
82
82
  term = @operands[1]
83
83
  raise "A term expected, but '#{term}' provided" unless term.is_a?(Factbase::Term)
84
- subset = Factbase::Query.new(maps, Mutex.new, selector.to_s).each(fact).to_a
84
+ subset = @fb.query(selector.to_s, maps).each(fact).to_a
85
85
  term.evaluate(nil, subset)
86
86
  end
87
87
 
@@ -89,7 +89,7 @@ module Factbase::Term::Aggregates
89
89
  assert_args(1)
90
90
  term = @operands[0]
91
91
  raise "A term expected, but '#{term}' provided" unless term.is_a?(Factbase::Term)
92
- Factbase::Query.new(maps, Mutex.new, term.to_s).each(fact).to_a.empty?
92
+ @fb.query(term.to_s, maps).each(fact).to_a.empty?
93
93
  end
94
94
 
95
95
  def best(maps)
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2024 Yegor Bugayenko
3
+ # Copyright (c) 2024-2025 Yegor Bugayenko
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the 'Software'), to deal
@@ -25,7 +25,7 @@ require_relative '../../factbase'
25
25
  # Aliases terms.
26
26
  #
27
27
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
28
- # Copyright:: Copyright (c) 2024 Yegor Bugayenko
28
+ # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
29
29
  # License:: MIT
30
30
  module Factbase::Term::Aliases
31
31
  def as(fact, maps)
@@ -33,7 +33,7 @@ module Factbase::Term::Aliases
33
33
  a = @operands[0]
34
34
  raise "A symbol expected as first argument of 'as'" unless a.is_a?(Symbol)
35
35
  vv = the_values(1, fact, maps)
36
- vv&.each { |v| fact.send("#{a}=", v) }
36
+ vv&.each { |v| fact.send(:"#{a}=", v) }
37
37
  true
38
38
  end
39
39
 
@@ -47,11 +47,11 @@ module Factbase::Term::Aliases
47
47
  .map { |j| j.size == 1 ? [j[0], j[0]] : j }
48
48
  term = @operands[1]
49
49
  raise "A term expected, but '#{term}' provided" unless term.is_a?(Factbase::Term)
50
- subset = Factbase::Query.new(maps, Mutex.new, term.to_s).each(fact).to_a
50
+ subset = @fb.query(term.to_s, maps).each(fact).to_a
51
51
  subset.each do |s|
52
52
  jumps.each do |to, from|
53
53
  s[from]&.each do |v|
54
- fact.send("#{to}=", v)
54
+ fact.send(:"#{to}=", v)
55
55
  end
56
56
  end
57
57
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2024 Yegor Bugayenko
3
+ # Copyright (c) 2024-2025 Yegor Bugayenko
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the 'Software'), to deal
@@ -25,7 +25,7 @@ require_relative '../../factbase'
25
25
  # Casting terms.
26
26
  #
27
27
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
28
- # Copyright:: Copyright (c) 2024 Yegor Bugayenko
28
+ # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
29
29
  # License:: MIT
30
30
  module Factbase::Term::Casting
31
31
  def to_string(fact, maps)
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2024 Yegor Bugayenko
3
+ # Copyright (c) 2024-2025 Yegor Bugayenko
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the 'Software'), to deal
@@ -25,7 +25,7 @@ require_relative '../../factbase'
25
25
  # Debug terms.
26
26
  #
27
27
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
28
- # Copyright:: Copyright (c) 2024 Yegor Bugayenko
28
+ # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
29
29
  # License:: MIT
30
30
  module Factbase::Term::Debug
31
31
  def traced(fact, maps)
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2024 Yegor Bugayenko
3
+ # Copyright (c) 2024-2025 Yegor Bugayenko
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the 'Software'), to deal
@@ -25,7 +25,7 @@ require_relative '../../factbase'
25
25
  # Defn terms.
26
26
  #
27
27
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
28
- # Copyright:: Copyright (c) 2024 Yegor Bugayenko
28
+ # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
29
29
  # License:: MIT
30
30
  module Factbase::Term::Defn
31
31
  def defn(_fact, _maps)
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2024 Yegor Bugayenko
3
+ # Copyright (c) 2024-2025 Yegor Bugayenko
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the 'Software'), to deal
@@ -25,7 +25,7 @@ require_relative '../../factbase'
25
25
  # Logical terms.
26
26
  #
27
27
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
28
- # Copyright:: Copyright (c) 2024 Yegor Bugayenko
28
+ # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
29
29
  # License:: MIT
30
30
  module Factbase::Term::Logical
31
31
  def always(_fact, _maps)
@@ -82,7 +82,7 @@ module Factbase::Term::Logical
82
82
  ops << o
83
83
  end
84
84
  return ops[0] if ops.size == 1
85
- Factbase::Term.new(@op, ops)
85
+ Factbase::Term.new(@fb, @op, ops)
86
86
  end
87
87
 
88
88
  def and_simplify
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2024 Yegor Bugayenko
3
+ # Copyright (c) 2024-2025 Yegor Bugayenko
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the 'Software'), to deal
@@ -25,7 +25,7 @@ require_relative '../../factbase'
25
25
  # Math terms.
26
26
  #
27
27
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
28
- # Copyright:: Copyright (c) 2024 Yegor Bugayenko
28
+ # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
29
29
  # License:: MIT
30
30
  module Factbase::Term::Math
31
31
  def plus(fact, maps)
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2024 Yegor Bugayenko
3
+ # Copyright (c) 2024-2025 Yegor Bugayenko
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the 'Software'), to deal
@@ -25,7 +25,7 @@ require_relative '../../factbase'
25
25
  # Meta terms.
26
26
  #
27
27
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
28
- # Copyright:: Copyright (c) 2024 Yegor Bugayenko
28
+ # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
29
29
  # License:: MIT
30
30
  module Factbase::Term::Meta
31
31
  def exists(fact, _maps)
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2024 Yegor Bugayenko
3
+ # Copyright (c) 2024-2025 Yegor Bugayenko
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the 'Software'), to deal
@@ -25,7 +25,7 @@ require_relative '../../factbase'
25
25
  # Ordering terms.
26
26
  #
27
27
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
28
- # Copyright:: Copyright (c) 2024 Yegor Bugayenko
28
+ # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
29
29
  # License:: MIT
30
30
  module Factbase::Term::Ordering
31
31
  def prev(fact, maps)
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2024 Yegor Bugayenko
3
+ # Copyright (c) 2024-2025 Yegor Bugayenko
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the 'Software'), to deal
@@ -25,7 +25,7 @@ require_relative '../../factbase'
25
25
  # String terms.
26
26
  #
27
27
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
28
- # Copyright:: Copyright (c) 2024 Yegor Bugayenko
28
+ # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
29
29
  # License:: MIT
30
30
  module Factbase::Term::Strings
31
31
  def concat(fact, maps)
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2024 Yegor Bugayenko
3
+ # Copyright (c) 2024-2025 Yegor Bugayenko
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the 'Software'), to deal
@@ -25,7 +25,7 @@ require_relative '../../factbase'
25
25
  # System-level terms.
26
26
  #
27
27
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
28
- # Copyright:: Copyright (c) 2024 Yegor Bugayenko
28
+ # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
29
29
  # License:: MIT
30
30
  module Factbase::Term::System
31
31
  def env(fact, maps)
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2024 Yegor Bugayenko
3
+ # Copyright (c) 2024-2025 Yegor Bugayenko
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the 'Software'), to deal
@@ -33,7 +33,7 @@ require_relative '../factbase/flatten'
33
33
  # puts Factbase::ToJSON.new(fb).json
34
34
  #
35
35
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
36
- # Copyright:: Copyright (c) 2024 Yegor Bugayenko
36
+ # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
37
37
  # License:: MIT
38
38
  class Factbase::ToJSON
39
39
  # Constructor.
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2024 Yegor Bugayenko
3
+ # Copyright (c) 2024-2025 Yegor Bugayenko
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the 'Software'), to deal
@@ -34,7 +34,7 @@ require_relative '../factbase/flatten'
34
34
  # puts Factbase::ToXML.new(fb).xml
35
35
  #
36
36
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
37
- # Copyright:: Copyright (c) 2024 Yegor Bugayenko
37
+ # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
38
38
  # License:: MIT
39
39
  class Factbase::ToXML
40
40
  # Constructor.
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2024 Yegor Bugayenko
3
+ # Copyright (c) 2024-2025 Yegor Bugayenko
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the 'Software'), to deal
@@ -33,7 +33,7 @@ require_relative '../factbase/flatten'
33
33
  # puts Factbase::ToYAML.new(fb).yaml
34
34
  #
35
35
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
36
- # Copyright:: Copyright (c) 2024 Yegor Bugayenko
36
+ # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
37
37
  # License:: MIT
38
38
  class Factbase::ToYAML
39
39
  # Constructor.
data/lib/factbase.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2024 Yegor Bugayenko
3
+ # Copyright (c) 2024-2025 Yegor Bugayenko
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the 'Software'), to deal
@@ -77,20 +77,23 @@ require 'yaml'
77
77
  # It is NOT thread-safe!
78
78
  #
79
79
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
80
- # Copyright:: Copyright (c) 2024 Yegor Bugayenko
80
+ # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
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.4.0'
84
+ VERSION = '0.5.1'
85
85
 
86
86
  # An exception that may be thrown in a transaction, to roll it back.
87
87
  class Rollback < StandardError; end
88
88
 
89
+ attr_reader :cache
90
+
89
91
  # Constructor.
90
92
  # @param [Array<Hash>] facts Array of facts to start with
91
93
  def initialize(facts = [])
92
94
  @maps = facts
93
95
  @mutex = Mutex.new
96
+ @cache = {}
94
97
  end
95
98
 
96
99
  # Make a deep duplicate of this factbase.
@@ -118,8 +121,9 @@ class Factbase
118
121
  @mutex.synchronize do
119
122
  @maps << map
120
123
  end
124
+ @cache.clear
121
125
  require_relative 'factbase/fact'
122
- Factbase::Fact.new(@mutex, map)
126
+ Factbase::Fact.new(self, @mutex, map)
123
127
  end
124
128
 
125
129
  # Create a query capable of iterating.
@@ -140,9 +144,15 @@ class Factbase
140
144
  # +README.md+ file of the repository.
141
145
  #
142
146
  # @param [String] query The query to use for selections
143
- def query(query)
147
+ # @param [Array<Hash>] maps Custom maps
148
+ def query(query, maps = @maps)
144
149
  require_relative 'factbase/query'
145
- Factbase::Query.new(@maps, @mutex, query)
150
+ require_relative 'factbase/query_once'
151
+ Factbase::QueryOnce.new(
152
+ self,
153
+ Factbase::Query.new(self, maps, @mutex, query),
154
+ maps
155
+ )
146
156
  end
147
157
 
148
158
  # Run an ACID transaction, which will either modify the factbase
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2024 Yegor Bugayenko
3
+ # Copyright (c) 2024-2025 Yegor Bugayenko
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the 'Software'), to deal
@@ -26,7 +26,7 @@ require_relative '../../../lib/factbase/syntax'
26
26
 
27
27
  # Aggregates test.
28
28
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
29
- # Copyright:: Copyright (c) 2024 Yegor Bugayenko
29
+ # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
30
30
  # License:: MIT
31
31
  class TestAggregates < Minitest::Test
32
32
  def test_aggregation
@@ -47,10 +47,10 @@ class TestAggregates < Minitest::Test
47
47
  '(eq x (agg (eq y 0) (first x)))' => '(eq x 1)',
48
48
  '(agg (eq foo 42) (always))' => '(eq x 1)'
49
49
  }.each do |q, r|
50
- t = Factbase::Syntax.new(q).to_term
50
+ t = Factbase::Syntax.new(Factbase.new, q).to_term
51
51
  f = maps.find { |m| t.evaluate(fact(m), maps) }
52
- assert(!f.nil?, "nothing found by: #{q}")
53
- assert(Factbase::Syntax.new(r).to_term.evaluate(fact(f), []))
52
+ refute_nil(f, "nothing found by: #{q}")
53
+ assert(Factbase::Syntax.new(Factbase.new, r).to_term.evaluate(fact(f), []))
54
54
  end
55
55
  end
56
56
 
@@ -63,7 +63,7 @@ class TestAggregates < Minitest::Test
63
63
  '(empty (eq y 42))' => true,
64
64
  '(empty (eq x 1))' => false
65
65
  }.each do |q, r|
66
- t = Factbase::Syntax.new(q).to_term
66
+ t = Factbase::Syntax.new(Factbase.new, q).to_term
67
67
  assert_equal(r, t.evaluate(nil, maps), q)
68
68
  end
69
69
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2024 Yegor Bugayenko
3
+ # Copyright (c) 2024-2025 Yegor Bugayenko
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the 'Software'), to deal
@@ -27,7 +27,7 @@ require_relative '../../../lib/factbase/accum'
27
27
 
28
28
  # Aliases test.
29
29
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
30
- # Copyright:: Copyright (c) 2024 Yegor Bugayenko
30
+ # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
31
31
  # License:: MIT
32
32
  class TestAliases < Minitest::Test
33
33
  def test_aliases
@@ -41,11 +41,11 @@ class TestAliases < Minitest::Test
41
41
  '(as foo (plus bar 1))' => '(absent foo)',
42
42
  '(as foo (minus t1 t2))' => '(when (exists foo) (eq "Float" (type foo)))'
43
43
  }.each do |q, r|
44
- t = Factbase::Syntax.new(q).to_term
44
+ t = Factbase::Syntax.new(Factbase.new, q).to_term
45
45
  maps.each do |m|
46
46
  f = Factbase::Accum.new(fact(m), {}, false)
47
47
  next unless t.evaluate(f, maps)
48
- assert(Factbase::Syntax.new(r).to_term.evaluate(f, []), "#{q} -> #{f}")
48
+ assert(Factbase::Syntax.new(Factbase.new, r).to_term.evaluate(f, []), "#{q} -> #{f}")
49
49
  end
50
50
  end
51
51
  end
@@ -61,13 +61,13 @@ class TestAliases < Minitest::Test
61
61
  '(join "uuu" (eq x 1))' => '(absent uuu)',
62
62
  '(join "uuu <= fff" (eq fff 1))' => '(absent uuu)'
63
63
  }.each do |q, r|
64
- t = Factbase::Syntax.new(q).to_term
64
+ t = Factbase::Syntax.new(Factbase.new, q).to_term
65
65
  maps.each do |m|
66
66
  f = Factbase::Accum.new(fact(m), {}, false)
67
67
  require_relative '../../../lib/factbase/looged'
68
68
  f = Factbase::Looged::Fact.new(f, Loog::NULL)
69
69
  next unless t.evaluate(f, maps)
70
- assert(Factbase::Syntax.new(r).to_term.evaluate(f, []), "#{q} -> #{f} doesn't match #{r}")
70
+ assert(Factbase::Syntax.new(Factbase.new, r).to_term.evaluate(f, []), "#{q} -> #{f} doesn't match #{r}")
71
71
  end
72
72
  end
73
73
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2024 Yegor Bugayenko
3
+ # Copyright (c) 2024-2025 Yegor Bugayenko
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the 'Software'), to deal
@@ -26,26 +26,26 @@ require_relative '../../../lib/factbase/term'
26
26
 
27
27
  # Math test.
28
28
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
29
- # Copyright:: Copyright (c) 2024 Yegor Bugayenko
29
+ # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
30
30
  # License:: MIT
31
31
  class TestCasting < Minitest::Test
32
32
  def test_to_str
33
- t = Factbase::Term.new(:to_string, [Time.now])
33
+ t = Factbase::Term.new(Factbase.new, :to_string, [Time.now])
34
34
  assert_equal('String', t.evaluate(fact, []).class.to_s)
35
35
  end
36
36
 
37
37
  def test_to_integer
38
- t = Factbase::Term.new(:to_integer, [[42, 'hello']])
38
+ t = Factbase::Term.new(Factbase.new, :to_integer, [[42, 'hello']])
39
39
  assert_equal('Integer', t.evaluate(fact, []).class.to_s)
40
40
  end
41
41
 
42
42
  def test_to_float
43
- t = Factbase::Term.new(:to_float, [[3.14, 'hello']])
43
+ t = Factbase::Term.new(Factbase.new, :to_float, [[3.14, 'hello']])
44
44
  assert_equal('Float', t.evaluate(fact, []).class.to_s)
45
45
  end
46
46
 
47
47
  def test_to_time
48
- t = Factbase::Term.new(:to_time, [%w[2023-01-01 hello]])
48
+ t = Factbase::Term.new(Factbase.new, :to_time, [%w[2023-01-01 hello]])
49
49
  assert_equal('Time', t.evaluate(fact, []).class.to_s)
50
50
  end
51
51
  end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2024-2025 Yegor Bugayenko
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the 'Software'), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ require 'minitest/autorun'
24
+ require_relative '../../../lib/factbase/term'
25
+
26
+ # Debug test.
27
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
28
+ # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
29
+ # License:: MIT
30
+ class TestDebug < Minitest::Test
31
+ def test_traced
32
+ t = Factbase::Term.new(Factbase.new, :traced, [Factbase::Term.new(Factbase.new, :defn, [:test_debug, 'self.to_s'])])
33
+ assert_output("(traced (defn test_debug 'self.to_s')) -> true\n") do
34
+ assert(t.evaluate(fact, []))
35
+ end
36
+ end
37
+
38
+ def test_traced_raises
39
+ e = assert_raises(StandardError) { Factbase::Term.new(Factbase.new, :traced, ['foo']).evaluate(fact, []) }
40
+ assert_match(/A term expected, but 'foo' provided/, e.message)
41
+ end
42
+
43
+ def test_traced_raises_when_too_many_args
44
+ e =
45
+ assert_raises(StandardError) do
46
+ Factbase::Term.new(
47
+ Factbase.new, :traced,
48
+ [Factbase::Term.new(Factbase.new, :defn, [:debug, 'self.to_s']), 'something']
49
+ ).evaluate(fact, [])
50
+ end
51
+ assert_match(/Too many \(\d+\) operands for 'traced' \(\d+ expected\)/, e.message)
52
+ end
53
+ end