factbase 0.16.8 → 0.17.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 (84) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -1
  3. data/Gemfile.lock +23 -18
  4. data/README.md +28 -27
  5. data/Rakefile +14 -6
  6. data/lib/factbase/cached/cached_query.rb +2 -0
  7. data/lib/factbase/indexed/indexed_absent.rb +25 -0
  8. data/lib/factbase/indexed/indexed_and.rb +79 -0
  9. data/lib/factbase/indexed/indexed_eq.rb +46 -0
  10. data/lib/factbase/indexed/indexed_exists.rb +25 -0
  11. data/lib/factbase/indexed/indexed_factbase.rb +46 -0
  12. data/lib/factbase/indexed/indexed_gt.rb +41 -0
  13. data/lib/factbase/indexed/indexed_lt.rb +41 -0
  14. data/lib/factbase/indexed/indexed_not.rb +32 -0
  15. data/lib/factbase/indexed/indexed_one.rb +25 -0
  16. data/lib/factbase/indexed/indexed_or.rb +28 -0
  17. data/lib/factbase/indexed/indexed_query.rb +2 -0
  18. data/lib/factbase/indexed/indexed_term.rb +29 -185
  19. data/lib/factbase/indexed/indexed_unique.rb +25 -0
  20. data/lib/factbase/query.rb +3 -1
  21. data/lib/factbase/sync/sync_factbase.rb +11 -11
  22. data/lib/factbase/sync/sync_query.rb +7 -8
  23. data/lib/factbase/term.rb +110 -91
  24. data/lib/factbase/terms/absent.rb +26 -0
  25. data/lib/factbase/terms/aggregates.rb +0 -13
  26. data/lib/factbase/terms/always.rb +27 -0
  27. data/lib/factbase/terms/and.rb +28 -0
  28. data/lib/factbase/terms/arithmetic.rb +55 -0
  29. data/lib/factbase/terms/as.rb +31 -0
  30. data/lib/factbase/terms/{debug.rb → assert.rb} +17 -15
  31. data/lib/factbase/terms/base.rb +17 -0
  32. data/lib/factbase/terms/boolean.rb +28 -0
  33. data/lib/factbase/terms/compare.rb +38 -0
  34. data/lib/factbase/terms/concat.rb +26 -0
  35. data/lib/factbase/terms/count.rb +25 -0
  36. data/lib/factbase/terms/defn.rb +16 -15
  37. data/lib/factbase/terms/div.rb +25 -0
  38. data/lib/factbase/terms/either.rb +31 -0
  39. data/lib/factbase/terms/env.rb +28 -0
  40. data/lib/factbase/terms/eq.rb +28 -0
  41. data/lib/factbase/terms/exists.rb +27 -0
  42. data/lib/factbase/terms/first.rb +30 -0
  43. data/lib/factbase/terms/gt.rb +28 -0
  44. data/lib/factbase/terms/gte.rb +27 -0
  45. data/lib/factbase/terms/head.rb +37 -0
  46. data/lib/factbase/terms/inverted.rb +34 -0
  47. data/lib/factbase/terms/{aliases.rb → join.rb} +15 -15
  48. data/lib/factbase/terms/logical.rb +0 -83
  49. data/lib/factbase/terms/lt.rb +28 -0
  50. data/lib/factbase/terms/lte.rb +28 -0
  51. data/lib/factbase/terms/many.rb +29 -0
  52. data/lib/factbase/terms/{strings.rb → matches.rb} +12 -12
  53. data/lib/factbase/terms/minus.rb +25 -0
  54. data/lib/factbase/terms/never.rb +26 -0
  55. data/lib/factbase/terms/nil.rb +26 -0
  56. data/lib/factbase/terms/not.rb +27 -0
  57. data/lib/factbase/terms/one.rb +30 -0
  58. data/lib/factbase/terms/or.rb +28 -0
  59. data/lib/factbase/terms/plus.rb +27 -0
  60. data/lib/factbase/terms/prev.rb +29 -0
  61. data/lib/factbase/terms/shared.rb +69 -0
  62. data/lib/factbase/terms/size.rb +30 -0
  63. data/lib/factbase/terms/sorted.rb +38 -0
  64. data/lib/factbase/terms/sprintf.rb +29 -0
  65. data/lib/factbase/terms/times.rb +25 -0
  66. data/lib/factbase/terms/to_float.rb +28 -0
  67. data/lib/factbase/terms/to_integer.rb +28 -0
  68. data/lib/factbase/terms/to_string.rb +28 -0
  69. data/lib/factbase/terms/to_time.rb +28 -0
  70. data/lib/factbase/terms/traced.rb +33 -0
  71. data/lib/factbase/terms/type.rb +31 -0
  72. data/lib/factbase/terms/undef.rb +33 -0
  73. data/lib/factbase/terms/unique.rb +34 -0
  74. data/lib/factbase/terms/when.rb +29 -0
  75. data/lib/factbase/terms/zero.rb +28 -0
  76. data/lib/factbase/version.rb +1 -1
  77. data/lib/factbase.rb +10 -1
  78. metadata +60 -10
  79. data/lib/factbase/terms/casting.rb +0 -41
  80. data/lib/factbase/terms/lists.rb +0 -57
  81. data/lib/factbase/terms/math.rb +0 -103
  82. data/lib/factbase/terms/meta.rb +0 -58
  83. data/lib/factbase/terms/ordering.rb +0 -34
  84. data/lib/factbase/terms/system.rb +0 -19
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
4
+ # SPDX-License-Identifier: MIT
5
+
6
+ require_relative 'base'
7
+ # Evaluates whether a value is inverted within a given factbase context.
8
+ class Factbase::Inverted < Factbase::TermBase
9
+ # Constructor.
10
+ # @param [Array] operands Operands
11
+ def initialize(operands)
12
+ super()
13
+ @operands = operands
14
+ @op = 'inverted'
15
+ end
16
+
17
+ # Evaluate term on a fact.
18
+ # @param [Factbase::Fact] _fact The fact
19
+ # @param [Array<Factbase::Fact>] _maps All maps available
20
+ # @param [Factbase] _fb Factbase to use for sub-queries
21
+ # @return [Boolean] Whether the value is inverted
22
+ def evaluate(_fact, _maps, _fb)
23
+ true
24
+ end
25
+
26
+ def predict(maps, fb, params)
27
+ assert_args(1)
28
+ term = @operands[0]
29
+ raise "A term is expected, but '#{term}' provided" unless term.is_a?(Factbase::Term)
30
+ fb.query(term, maps).each(fb, params).to_a
31
+ .reverse
32
+ .map { |m| m.all_properties.to_h { |k| [k, m[k]] } }
33
+ end
34
+ end
@@ -3,24 +3,24 @@
3
3
  # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
4
4
  # SPDX-License-Identifier: MIT
5
5
 
6
- require_relative '../../factbase'
6
+ require_relative 'base'
7
7
 
8
- # Aliases terms.
9
- #
10
- # Author:: Yegor Bugayenko (yegor256@gmail.com)
11
- # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
12
- # License:: MIT
13
- module Factbase::Aliases
14
- def as(fact, maps, fb)
15
- assert_args(2)
16
- a = @operands[0]
17
- raise "A symbol is expected as first argument of 'as'" unless a.is_a?(Symbol)
18
- vv = _values(1, fact, maps, fb)
19
- vv&.each { |v| fact.send(:"#{a}=", v) }
20
- true
8
+ # The Factbase::Join class is a specialized term that performs
9
+ # join operations between facts based on specified attribute mappings.
10
+ class Factbase::Join < Factbase::TermBase
11
+ # Constructor.
12
+ # @param [Array] operands Operands
13
+ def initialize(operands)
14
+ super()
15
+ @operands = operands
21
16
  end
22
17
 
23
- def join(fact, maps, fb)
18
+ # Evaluate term on a fact.
19
+ # @param [Factbase::Fact] fact The fact
20
+ # @param [Array<Factbase::Fact>] maps All maps available
21
+ # @param [Factbase] fb Factbase to use for sub-queries
22
+ # @return [Boolean] True if succeeded
23
+ def evaluate(fact, maps, fb)
24
24
  assert_args(2)
25
25
  jumps = @operands[0]
26
26
  raise "A string is expected as first argument of 'join'" unless jumps.is_a?(String)
@@ -11,77 +11,6 @@ require_relative '../../factbase'
11
11
  # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
12
12
  # License:: MIT
13
13
  module Factbase::Logical
14
- # Always returns true, regardless of the fact
15
- # @param [Factbase::Fact] _fact The fact (unused)
16
- # @param [Array<Factbase::Fact>] _maps All maps available (unused)
17
- # @return [Boolean] Always returns true
18
- def always(_fact, _maps, _fb)
19
- assert_args(0)
20
- true
21
- end
22
-
23
- # Always returns false, regardless of the fact
24
- # @param [Factbase::Fact] _fact The fact (unused)
25
- # @param [Array<Factbase::Fact>] _maps All maps available (unused)
26
- # @return [Boolean] Always returns false
27
- def never(_fact, _maps, _fb)
28
- assert_args(0)
29
- false
30
- end
31
-
32
- # Logical negation (NOT) of an operand
33
- # @param [Factbase::Fact] fact The fact
34
- # @param [Array<Factbase::Fact>] maps All maps available
35
- # @return [Boolean] Negated boolean result of the operand
36
- def not(fact, maps, fb)
37
- assert_args(1)
38
- !_only_bool(_values(0, fact, maps, fb), 0)
39
- end
40
-
41
- # Logical OR of multiple operands
42
- # @param [Factbase::Fact] fact The fact
43
- # @param [Array<Factbase::Fact>] maps All maps available
44
- # @return [Boolean] True if any operand evaluates to true, false otherwise
45
- def or(fact, maps, fb)
46
- (0..(@operands.size - 1)).each do |i|
47
- return true if _only_bool(_values(i, fact, maps, fb), i)
48
- end
49
- false
50
- end
51
-
52
- # Logical AND of multiple operands
53
- # @param [Factbase::Fact] fact The fact
54
- # @param [Array<Factbase::Fact>] maps All maps available
55
- # @return [Boolean] True if all operands evaluate to true, false otherwise
56
- def and(fact, maps, fb)
57
- (0..(@operands.size - 1)).each do |i|
58
- return false unless _only_bool(_values(i, fact, maps, fb), i)
59
- end
60
- true
61
- end
62
-
63
- # Logical implication (IF...THEN)
64
- # @param [Factbase::Fact] fact The fact
65
- # @param [Array<Factbase::Fact>] maps All maps available
66
- # @return [Boolean] True if first operand is false OR both are true
67
- def when(fact, maps, fb)
68
- assert_args(2)
69
- a = @operands[0]
70
- b = @operands[1]
71
- !a.evaluate(fact, maps, fb) || (a.evaluate(fact, maps, fb) && b.evaluate(fact, maps, fb))
72
- end
73
-
74
- # Returns the first non-nil value or the second value
75
- # @param [Factbase::Fact] fact The fact
76
- # @param [Array<Factbase::Fact>] maps All maps available
77
- # @return [Object] First operand if not nil, otherwise second operand
78
- def either(fact, maps, fb)
79
- assert_args(2)
80
- v = _values(0, fact, maps, fb)
81
- return v unless v.nil?
82
- _values(1, fact, maps, fb)
83
- end
84
-
85
14
  # Simplifies AND or OR expressions by removing duplicates
86
15
  # @return [Factbase::Term] Simplified term
87
16
  def and_or_simplify
@@ -109,16 +38,4 @@ module Factbase::Logical
109
38
  def or_simplify
110
39
  and_or_simplify
111
40
  end
112
-
113
- # Helper method to ensure a value is boolean
114
- # @param [Object] val The value to check
115
- # @param [Integer] pos The position of the operand
116
- # @return [Boolean] The boolean value
117
- # @raise [RuntimeError] If value is not a boolean
118
- def _only_bool(val, pos)
119
- val = val[0] if val.respond_to?(:each)
120
- return false if val.nil?
121
- return val if val.is_a?(TrueClass) || val.is_a?(FalseClass)
122
- raise "Boolean is expected, while #{val.class} received from #{@operands[pos]}"
123
- end
124
41
  end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
4
+ # SPDX-License-Identifier: MIT
5
+
6
+ require_relative 'base'
7
+ require_relative 'compare'
8
+
9
+ # Represents a less-than term in the Factbase system.
10
+ # This class is used to evaluate whether a given fact satisfies
11
+ # a less-than comparison with the specified operands.
12
+ class Factbase::Lt < Factbase::TermBase
13
+ # Constructor.
14
+ # @param [Array] operands Operands
15
+ def initialize(operands)
16
+ super()
17
+ @op = Factbase::Compare.new(:<, operands)
18
+ end
19
+
20
+ # Evaluate term on a fact.
21
+ # @param [Factbase::Fact] fact The fact
22
+ # @param [Array<Factbase::Fact>] maps All maps available
23
+ # @param [Factbase] fb Factbase to use for sub-queries
24
+ # @return [Boolean] The result of the less-than comparison
25
+ def evaluate(fact, maps, fb)
26
+ @op.evaluate(fact, maps, fb)
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
4
+ # SPDX-License-Identifier: MIT
5
+
6
+ require_relative 'base'
7
+ require_relative 'compare'
8
+
9
+ # Represents a less-than-equal term in the Factbase.
10
+ # This class is used to evaluate whether a given fact satisfies
11
+ # a less-than-equal comparison with the specified operands.
12
+ class Factbase::Lte < Factbase::TermBase
13
+ # Constructor.
14
+ # @param [Array] operands Operands
15
+ def initialize(operands)
16
+ super()
17
+ @op = Factbase::Compare.new(:<=, operands)
18
+ end
19
+
20
+ # Evaluate term on a fact.
21
+ # @param [Factbase::Fact] fact The fact
22
+ # @param [Array<Factbase::Fact>] maps All maps available
23
+ # @param [Factbase] fb Factbase to use for sub-queries
24
+ # @return [Boolean] The result of the less-than-equal comparison
25
+ def evaluate(fact, maps, fb)
26
+ @op.evaluate(fact, maps, fb)
27
+ end
28
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
4
+ # SPDX-License-Identifier: MIT
5
+
6
+ require_relative 'base'
7
+
8
+ # Represents a 'many' term in the Factbase that evaluates to true if a property
9
+ # has multiple values. This class is used to check if there are more
10
+ # than one values associated with a specific property in a fact.
11
+ class Factbase::Many < Factbase::TermBase
12
+ # Constructor.
13
+ # @param [Array] operands Operands
14
+ def initialize(operands)
15
+ super()
16
+ @operands = operands
17
+ end
18
+
19
+ # Evaluate term on a fact.
20
+ # @param [Factbase::Fact] fact The fact
21
+ # @param [Array<Factbase::Fact>] maps All maps available
22
+ # @param [Factbase] fb Factbase to use for sub-queries
23
+ # @return [Boolean] True if many
24
+ def evaluate(fact, maps, fb)
25
+ assert_args(1)
26
+ v = _values(0, fact, maps, fb)
27
+ !v.nil? && v.size > 1
28
+ end
29
+ end
@@ -3,25 +3,25 @@
3
3
  # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
4
4
  # SPDX-License-Identifier: MIT
5
5
 
6
- require_relative '../../factbase'
6
+ require_relative 'base'
7
7
 
8
- # String terms.
8
+ # Matches term.
9
9
  #
10
10
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
11
11
  # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
12
12
  # License:: MIT
13
- module Factbase::Strings
14
- def concat(fact, maps, fb)
15
- (0..(@operands.length - 1)).map { |i| _values(i, fact, maps, fb)&.first }.join
13
+ class Factbase::Matches < Factbase::TermBase
14
+ def initialize(operands)
15
+ super()
16
+ @operands = operands
16
17
  end
17
18
 
18
- def sprintf(fact, maps, fb)
19
- fmt = _values(0, fact, maps, fb)[0]
20
- ops = (1..(@operands.length - 1)).map { |i| _values(i, fact, maps, fb)&.first }
21
- format(*([fmt] + ops))
22
- end
23
-
24
- def matches(fact, maps, fb)
19
+ # Evaluate term on a fact.
20
+ # @param [Factbase::Fact] fact The fact
21
+ # @param [Array<Factbase::Fact>] maps All maps available
22
+ # @param [Factbase] fb Factbase to use for sub-queries
23
+ # @return [Boolean] True if the string matches the regexp, false otherwise
24
+ def evaluate(fact, maps, fb)
25
25
  assert_args(2)
26
26
  str = _values(0, fact, maps, fb)
27
27
  return false if str.nil?
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
4
+ # SPDX-License-Identifier: MIT
5
+
6
+ require_relative 'base'
7
+ # This class represents a 'minus' term within the Factbase.
8
+ # It performs a subtraction operation over the given operands.
9
+ class Factbase::Minus < Factbase::TermBase
10
+ # Constructor.
11
+ # @param [Array] operands Operands
12
+ def initialize(operands)
13
+ super()
14
+ @minus = Factbase::Arithmetic.new(:-, operands)
15
+ end
16
+
17
+ # Evaluate term on a fact.
18
+ # @param [Factbase::Fact] fact The fact
19
+ # @param [Array<Factbase::Fact>] maps All maps available
20
+ # @param [Factbase] fb Factbase to use for sub-queries
21
+ # @return [Object] Result of the subtraction
22
+ def evaluate(fact, maps, fb)
23
+ @minus.evaluate(fact, maps, fb)
24
+ end
25
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
4
+ # SPDX-License-Identifier: MIT
5
+
6
+ require_relative 'base'
7
+ # The term 'never' that never evaluates to true.
8
+ class Factbase::Never < Factbase::TermBase
9
+ # Constructor.
10
+ # @param [Array] operands Operands
11
+ def initialize(operands = [])
12
+ super()
13
+ @operands = operands
14
+ @op = :never
15
+ end
16
+
17
+ # Evaluate term on a fact.
18
+ # @param [Factbase::Fact] _fact The fact
19
+ # @param [Array<Factbase::Fact>] _maps All maps available
20
+ # @param [Factbase] _fb Factbase to use for sub-queries
21
+ # @return [Boolean] Always returns false
22
+ def evaluate(_fact, _maps, _fb)
23
+ assert_args(0)
24
+ false
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
4
+ # SPDX-License-Identifier: MIT
5
+
6
+ require_relative 'base'
7
+ # A class representing a "nil" term in the Factbase system.
8
+ # It evaluates to true if the value is nil.
9
+ class Factbase::Nil < Factbase::TermBase
10
+ # Constructor.
11
+ # @param [Array] operands Operands
12
+ def initialize(operands)
13
+ super()
14
+ @operands = operands
15
+ end
16
+
17
+ # Evaluate term on a fact.
18
+ # @param [Factbase::Fact] fact The fact
19
+ # @param [Array<Factbase::Fact>] maps All maps available
20
+ # @param [Factbase] fb Factbase to use for sub-queries
21
+ # @return [Boolean] True if nil
22
+ def evaluate(fact, maps, fb)
23
+ assert_args(1)
24
+ _values(0, fact, maps, fb).nil?
25
+ end
26
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
4
+ # SPDX-License-Identifier: MIT
5
+
6
+ require_relative 'base'
7
+ require_relative 'boolean'
8
+ # The 'not' term that negates a boolean operand.
9
+ # Logical negation (NOT) of an operand.
10
+ class Factbase::Not < Factbase::TermBase
11
+ # Constructor.
12
+ # @param [Array] operands Operands
13
+ def initialize(operands)
14
+ super()
15
+ @operands = operands
16
+ @op = :not
17
+ end
18
+
19
+ # Evaluate term on a fact.
20
+ # @param [Factbase::Fact] fact The fact
21
+ # @param [Array<Factbase::Fact>] maps All maps available
22
+ # @return [Boolean] Negated boolean result of the operand
23
+ def evaluate(fact, maps, fb)
24
+ assert_args(1)
25
+ !Factbase::Boolean.new(_values(0, fact, maps, fb), @operands[0]).bool?
26
+ end
27
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
4
+ # SPDX-License-Identifier: MIT
5
+
6
+ require_relative 'base'
7
+
8
+ # The term which property has exactly one value.
9
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
10
+ # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
11
+ # License:: MIT
12
+ class Factbase::One < Factbase::TermBase
13
+ # Constructor.
14
+ # @param [Array] operands Operands
15
+ def initialize(operands)
16
+ super()
17
+ @operands = operands
18
+ end
19
+
20
+ # Evaluate term on a fact.
21
+ # @param [Factbase::Fact] fact The fact
22
+ # @param [Array<Factbase::Fact>] maps All maps available
23
+ # @param [Factbase] fb Factbase to use for sub-queries
24
+ # @return [Boolean] True if exactly one value
25
+ def evaluate(fact, maps, fb)
26
+ assert_args(1)
27
+ v = _values(0, fact, maps, fb)
28
+ !v.nil? && v.size == 1
29
+ end
30
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
4
+ # SPDX-License-Identifier: MIT
5
+
6
+ require_relative 'base'
7
+ require_relative 'boolean'
8
+ # The 'or' term that represents a logical OR operation between multiple operands.
9
+ class Factbase::Or < Factbase::TermBase
10
+ # Constructor.
11
+ # @param [Array] operands Operands
12
+ def initialize(operands)
13
+ super()
14
+ @operands = operands
15
+ @op = :or
16
+ end
17
+
18
+ # Evaluate term on a fact.
19
+ # @param [Factbase::Fact] fact The fact
20
+ # @param [Array<Factbase::Fact>] maps All maps available
21
+ # @return [Boolean] True if any operand evaluates to true, false otherwise
22
+ def evaluate(fact, maps, fb)
23
+ (0..(@operands.size - 1)).each do |i|
24
+ return true if Factbase::Boolean.new(_values(i, fact, maps, fb), @operands[i]).bool?
25
+ end
26
+ false
27
+ end
28
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
4
+ # SPDX-License-Identifier: MIT
5
+
6
+ require_relative 'base'
7
+ require_relative 'arithmetic'
8
+
9
+ # Represents a Plus term in the Factbase system.
10
+ # This class is used to perform addition operations on operands.
11
+ class Factbase::Plus < Factbase::TermBase
12
+ # Constructor.
13
+ # @param [Array] operands Operands
14
+ def initialize(operands)
15
+ super()
16
+ @plus = Factbase::Arithmetic.new(:+, operands)
17
+ end
18
+
19
+ # Evaluate term on a fact.
20
+ # @param [Factbase::Fact] fact The fact
21
+ # @param [Array<Factbase::Fact>] maps All maps available
22
+ # @param [Factbase] fb Factbase to use for sub-queries
23
+ # @return [Object] Result of the addition
24
+ def evaluate(fact, maps, fb)
25
+ @plus.evaluate(fact, maps, fb)
26
+ end
27
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
4
+ # SPDX-License-Identifier: MIT
5
+
6
+ require_relative 'base'
7
+ # The Factbase::Unique class provides functionality for evaluating the uniqueness
8
+ # of terms based on provided operands and facts.
9
+ class Factbase::Prev < Factbase::TermBase
10
+ # Constructor.
11
+ # @param [Array] operands Operands
12
+ def initialize(operands)
13
+ super()
14
+ @operands = operands
15
+ end
16
+
17
+ # Evaluate term on a fact.
18
+ # @param [Factbase::Fact] fact The fact
19
+ # @param [Array<Factbase::Fact>] maps All maps available
20
+ # @param [Factbase] fb Factbase to use for sub-queries
21
+ # @return [Object] The previous value
22
+ def evaluate(fact, maps, fb)
23
+ assert_args(1)
24
+ before = @prev
25
+ v = _values(0, fact, maps, fb)
26
+ @prev = v
27
+ before
28
+ end
29
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
4
+ # SPDX-License-Identifier: MIT
5
+
6
+ # This module provides shared methods for Factbase terms, including argument validation,
7
+ # symbol-based lookups, and handling of operand values.
8
+ # @todo #302:30min Remove this module and move its methods to Factbase::TermBase.
9
+ # Currently, we use it because we are required to inject all thesse methods into Factbase::Term.
10
+ # When all the terms will inherit from Factbase::TermBase, we can remove this module.
11
+ module Factbase::TermShared
12
+ # Turns it into a string.
13
+ # @return [String] The string of it
14
+ def to_s
15
+ @to_s ||=
16
+ begin
17
+ items = []
18
+ items << @op
19
+ items +=
20
+ @operands.map do |o|
21
+ if o.is_a?(String)
22
+ "'#{o.gsub("'", "\\\\'").gsub('"', '\\\\"')}'"
23
+ elsif o.is_a?(Time)
24
+ o.utc.iso8601
25
+ else
26
+ o.to_s
27
+ end
28
+ end
29
+ "(#{items.join(' ')})"
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def assert_args(num)
36
+ c = @operands.size
37
+ raise "Too many (#{c}) operands for '#{@op}' (#{num} expected)" if c > num
38
+ raise "Too few (#{c}) operands for '#{@op}' (#{num} expected)" if c < num
39
+ end
40
+
41
+ def _by_symbol(pos, fact)
42
+ o = @operands[pos]
43
+ raise "A symbol expected at ##{pos}, but '#{o}' (#{o.class}) provided" unless o.is_a?(Symbol)
44
+ k = o.to_s
45
+ fact[k]
46
+ end
47
+
48
+ # @return [Array|nil] Either array of values or NIL
49
+ def _values(pos, fact, maps, fb)
50
+ v = @operands[pos]
51
+ v = v.evaluate(fact, maps, fb) if v.is_a?(Factbase::Term)
52
+ v = v.evaluate(fact, maps, fb) if v.is_a?(Factbase::TermBase)
53
+ v = fact[v.to_s] if v.is_a?(Symbol)
54
+ return v if v.nil?
55
+ unless v.is_a?(Array)
56
+ v =
57
+ if v.respond_to?(:each)
58
+ v.to_a
59
+ else
60
+ [v]
61
+ end
62
+ end
63
+ raise 'Why not array?' unless v.is_a?(Array)
64
+ unless v.all? { |i| [Float, Integer, String, Time, TrueClass, FalseClass].any? { |t| i.is_a?(t) } }
65
+ raise 'Wrong type inside'
66
+ end
67
+ v
68
+ end
69
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
4
+ # SPDX-License-Identifier: MIT
5
+
6
+ require_relative 'base'
7
+
8
+ # Factbase::Size is a term that calculates the size of an operand
9
+ # when evaluated on a given fact.
10
+ class Factbase::Size < Factbase::TermBase
11
+ # Constructor.
12
+ # @param [Array] operands Operands
13
+ def initialize(operands)
14
+ super()
15
+ @operands = operands
16
+ end
17
+
18
+ # Evaluate term on a fact.
19
+ # @param [Factbase::Fact] fact The fact
20
+ # @param [Array<Factbase::Fact>] _maps All maps available
21
+ # @param [Factbase] _fb Factbase to use for sub-queries
22
+ # @return [Integer] Size of the operand
23
+ def evaluate(fact, _maps, _fb)
24
+ assert_args(1)
25
+ v = _by_symbol(0, fact)
26
+ return 0 if v.nil?
27
+ return 1 unless v.respond_to?(:to_a)
28
+ v.size
29
+ end
30
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
4
+ # SPDX-License-Identifier: MIT
5
+
6
+ require_relative 'base'
7
+ # This class represents a 'sorted' term in the Factbase.
8
+ # It evaluates whether the given facts satisfy the sorted condition.
9
+ class Factbase::Sorted < Factbase::TermBase
10
+ # Constructor.
11
+ # @param [Array] operands Operands
12
+ def initialize(operands)
13
+ super()
14
+ @operands = operands
15
+ @op = 'sorted'
16
+ end
17
+
18
+ # Evaluate term on a fact.
19
+ # @param [Factbase::Fact] _fact The fact
20
+ # @param [Array<Factbase::Fact>] _maps All maps available
21
+ # @param [Factbase] _fb Factbase to use for sub-queries
22
+ # @return [Boolean] Whether the value is sorted
23
+ def evaluate(_fact, _maps, _fb)
24
+ true
25
+ end
26
+
27
+ def predict(maps, fb, params)
28
+ assert_args(2)
29
+ prop = @operands[0]
30
+ raise "A symbol is expected as first argument of 'sorted'" unless prop.is_a?(Symbol)
31
+ term = @operands[1]
32
+ raise "A term is expected, but '#{term}' provided" unless term.is_a?(Factbase::Term)
33
+ fb.query(term, maps).each(fb, params).to_a
34
+ .reject { |m| m[prop].nil? }
35
+ .sort_by { |m| m[prop].first }
36
+ .map { |m| m.all_properties.to_h { |k| [k, m[k]] } }
37
+ end
38
+ end