factbase 0.3.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.0pdd.yml +1 -1
- data/.github/workflows/actionlint.yml +1 -1
- data/.github/workflows/benchmark.yml +64 -0
- data/.github/workflows/codecov.yml +4 -2
- data/.github/workflows/copyrights.yml +2 -2
- data/.github/workflows/markdown-lint.yml +1 -1
- data/.github/workflows/pdd.yml +1 -1
- data/.github/workflows/rake.yml +2 -2
- data/.github/workflows/xcop.yml +1 -1
- data/.github/workflows/yamllint.yml +1 -1
- data/.gitignore +1 -1
- data/.rubocop.yml +6 -1
- data/.rultor.yml +2 -2
- data/.simplecov +1 -1
- data/.yamllint.yml +9 -4
- data/Gemfile +9 -7
- data/Gemfile.lock +99 -78
- data/LICENSE.txt +1 -1
- data/README.md +33 -0
- data/Rakefile +6 -1
- data/benchmarks/simple.rb +96 -0
- data/factbase.gemspec +1 -1
- data/lib/factbase/accum.rb +3 -3
- data/lib/factbase/fact.rb +6 -4
- data/lib/factbase/flatten.rb +2 -2
- data/lib/factbase/inv.rb +2 -2
- data/lib/factbase/looged.rb +6 -5
- data/lib/factbase/pre.rb +15 -4
- data/lib/factbase/query.rb +16 -8
- data/lib/factbase/query_once.rb +71 -0
- data/lib/factbase/rules.rb +3 -3
- data/lib/factbase/syntax.rb +13 -8
- data/lib/factbase/tee.rb +2 -2
- data/lib/factbase/term.rb +33 -5
- data/lib/factbase/term_once.rb +84 -0
- data/lib/factbase/terms/aggregates.rb +4 -4
- data/lib/factbase/terms/aliases.rb +5 -5
- data/lib/factbase/terms/casting.rb +2 -2
- data/lib/factbase/terms/debug.rb +2 -2
- data/lib/factbase/terms/defn.rb +2 -2
- data/lib/factbase/terms/logical.rb +3 -3
- data/lib/factbase/terms/math.rb +2 -2
- data/lib/factbase/terms/meta.rb +2 -2
- data/lib/factbase/terms/ordering.rb +2 -2
- data/lib/factbase/terms/strings.rb +2 -2
- data/lib/factbase/terms/system.rb +2 -2
- data/lib/factbase/to_json.rb +2 -2
- data/lib/factbase/to_xml.rb +2 -2
- data/lib/factbase/to_yaml.rb +2 -2
- data/lib/factbase.rb +16 -6
- data/test/factbase/terms/test_aggregates.rb +6 -6
- data/test/factbase/terms/test_aliases.rb +6 -6
- data/test/factbase/terms/test_casting.rb +6 -6
- data/test/factbase/terms/test_debug.rb +53 -0
- data/test/factbase/terms/test_defn.rb +15 -15
- data/test/factbase/terms/test_logical.rb +15 -13
- data/test/factbase/terms/test_math.rb +42 -42
- data/test/factbase/terms/test_meta.rb +87 -0
- data/test/factbase/terms/test_ordering.rb +45 -0
- data/test/factbase/terms/test_strings.rb +8 -8
- data/test/factbase/terms/test_system.rb +6 -6
- data/test/factbase/test_accum.rb +17 -8
- data/test/factbase/test_fact.rb +22 -22
- data/test/factbase/test_flatten.rb +6 -6
- data/test/factbase/test_inv.rb +3 -3
- data/test/factbase/test_looged.rb +13 -13
- data/test/factbase/test_pre.rb +16 -2
- data/test/factbase/test_query.rb +17 -17
- data/test/factbase/test_rules.rb +11 -8
- data/test/factbase/test_syntax.rb +29 -9
- data/test/factbase/test_tee.rb +18 -9
- data/test/factbase/test_term.rb +18 -18
- data/test/factbase/test_to_json.rb +4 -4
- data/test/factbase/test_to_xml.rb +12 -14
- data/test/factbase/test_to_yaml.rb +3 -3
- data/test/test__helper.rb +2 -2
- data/test/test_factbase.rb +198 -18
- 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 =
|
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
|
-
|
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 =
|
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)
|
data/lib/factbase/terms/debug.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
|
@@ -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)
|
data/lib/factbase/terms/defn.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
|
@@ -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
|
data/lib/factbase/terms/math.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
|
@@ -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)
|
data/lib/factbase/terms/meta.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
|
@@ -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)
|
data/lib/factbase/to_json.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
|
@@ -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.
|
data/lib/factbase/to_xml.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
|
@@ -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.
|
data/lib/factbase/to_yaml.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
|
@@ -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.
|
84
|
+
VERSION = '0.5.0'
|
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
|
-
|
147
|
+
# @param [Array<Hash>] maps Custom maps
|
148
|
+
def query(query, maps = @maps)
|
144
149
|
require_relative 'factbase/query'
|
145
|
-
|
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
|
-
|
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
|