factbase 0.15.7 → 0.16.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.
- checksums.yaml +4 -4
- data/Gemfile.lock +2 -2
- data/README.md +6 -0
- data/lib/factbase/cached/cached_term.rb +1 -0
- data/lib/factbase/indexed/indexed_term.rb +4 -4
- data/lib/factbase/logged.rb +6 -5
- data/lib/factbase/query.rb +1 -1
- data/lib/factbase/tee.rb +2 -1
- data/lib/factbase/term.rb +10 -2
- data/lib/factbase/terms/lists.rb +42 -0
- data/lib/factbase/terms/ordering.rb +8 -0
- data/lib/factbase/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 96133055dc92095960ab4b258f4a144105393d2a86e11f96c3199ff24d35653a
|
4
|
+
data.tar.gz: f93590d550bf7b54aaa235e43557173d57af37abdc9e0a37491cf68c654e2c1e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2a532d7b59b0fa32d5662c420fb1d0de409d1c9cdf52fd6cf42c3020fd20a2008679019cf853a49e7ac1b0c9207d87134ffe1f23693f652a99188c8c6bd518f2
|
7
|
+
data.tar.gz: 67df97f44bdfbbcdba5cb5b7cec6a2f62cf9b4049f0a3d643382d797d9ec2997160ac231e871a18642c8fbd903387412e6c741d367f4e4362019d3a45a4cf6e3
|
data/Gemfile.lock
CHANGED
@@ -72,7 +72,7 @@ GEM
|
|
72
72
|
psych (>= 4.0.0)
|
73
73
|
regexp_parser (2.11.2)
|
74
74
|
rexml (3.4.2)
|
75
|
-
rubocop (1.80.
|
75
|
+
rubocop (1.80.2)
|
76
76
|
json (~> 2.3)
|
77
77
|
language_server-protocol (~> 3.17.0.2)
|
78
78
|
lint_roller (~> 1.1.0)
|
@@ -86,7 +86,7 @@ GEM
|
|
86
86
|
rubocop-ast (1.46.0)
|
87
87
|
parser (>= 3.3.7.2)
|
88
88
|
prism (~> 1.4)
|
89
|
-
rubocop-minitest (0.38.
|
89
|
+
rubocop-minitest (0.38.2)
|
90
90
|
lint_roller (~> 1.1)
|
91
91
|
rubocop (>= 1.75.0, < 2.0)
|
92
92
|
rubocop-ast (>= 1.38.0, < 2.0)
|
data/README.md
CHANGED
@@ -176,6 +176,12 @@ It's also possible to use a sub-query in a shorter form than with the `agg`:
|
|
176
176
|
|
177
177
|
* `(empty q)` is true if the subquery `q` is empty
|
178
178
|
|
179
|
+
It's possible to post-process a list of facts, for `agg` and `join`:
|
180
|
+
|
181
|
+
* `(sorted p expr)` sorts them by the value of `p` property
|
182
|
+
* `(inverted expr)` reverses them
|
183
|
+
* `(head n expr)` takes only `n` facts from the head of the list
|
184
|
+
|
179
185
|
There are some system-level terms:
|
180
186
|
|
181
187
|
* `(env v1 v2)` returns the value of environment variable `v1` or the string
|
@@ -17,6 +17,7 @@ module Factbase::CachedTerm
|
|
17
17
|
# @return [bool] TRUE if matches
|
18
18
|
def evaluate(fact, maps, fb)
|
19
19
|
return super unless static? && !abstract?
|
20
|
+
return super if %i[head unique].include?(@op)
|
20
21
|
key = [maps.object_id, to_s]
|
21
22
|
before = @cache[key]
|
22
23
|
@cache[key] = super if before.nil?
|
@@ -19,7 +19,7 @@ module Factbase::IndexedTerm
|
|
19
19
|
# @param [Array<Hash>] maps Array of facts
|
20
20
|
# @param [Hash] params Key/value params to use
|
21
21
|
# @return [Array<Hash>|nil] Returns a new array, or NIL if the original array must be used
|
22
|
-
def predict(maps, params)
|
22
|
+
def predict(maps, fb, params)
|
23
23
|
key = [maps.object_id, @operands.first, @op]
|
24
24
|
case @op
|
25
25
|
when :one
|
@@ -144,7 +144,7 @@ module Factbase::IndexedTerm
|
|
144
144
|
r = (maps & []) | j
|
145
145
|
else
|
146
146
|
@operands.each do |o|
|
147
|
-
n = o.predict(maps, params)
|
147
|
+
n = o.predict(maps, fb, params)
|
148
148
|
break if n.nil?
|
149
149
|
if r.nil?
|
150
150
|
r = n
|
@@ -159,7 +159,7 @@ module Factbase::IndexedTerm
|
|
159
159
|
when :or
|
160
160
|
r = nil
|
161
161
|
@operands.each do |o|
|
162
|
-
n = o.predict(maps, params)
|
162
|
+
n = o.predict(maps, fb, params)
|
163
163
|
if n.nil?
|
164
164
|
r = nil
|
165
165
|
break
|
@@ -171,7 +171,7 @@ module Factbase::IndexedTerm
|
|
171
171
|
r
|
172
172
|
when :not
|
173
173
|
if @idx[key].nil?
|
174
|
-
yes = @operands.first.predict(maps, params)
|
174
|
+
yes = @operands.first.predict(maps, fb, params)
|
175
175
|
if yes.nil?
|
176
176
|
@idx[key] = { r: nil }
|
177
177
|
else
|
data/lib/factbase/logged.rb
CHANGED
@@ -146,10 +146,11 @@ class Factbase::Logged
|
|
146
146
|
end
|
147
147
|
raise ".query(#{@term.to_s.inspect}).each() of #{qry.class} returned #{r.class}" unless r.is_a?(Integer)
|
148
148
|
q = Factbase::Syntax.new(@term).to_term.to_s
|
149
|
+
q = "#{q} with {#{params.map { |k, v| "#{k}=#{v}" }.join(', ')}}" if params.is_a?(Hash) && !params.empty?
|
149
150
|
if r.zero?
|
150
|
-
@tube.say(start, "Zero/#{@fb.size} facts found by
|
151
|
+
@tube.say(start, "Zero/#{@fb.size} facts found by #{q} #{tail}")
|
151
152
|
else
|
152
|
-
@tube.say(start, "Found #{r}/#{@fb.size} fact(s) by
|
153
|
+
@tube.say(start, "Found #{r}/#{@fb.size} fact(s) by #{q} #{tail}")
|
153
154
|
end
|
154
155
|
r
|
155
156
|
end
|
@@ -180,11 +181,11 @@ class Factbase::Logged
|
|
180
181
|
end
|
181
182
|
raise ".delete! of #{@term.class} returned #{r.class}" unless r.is_a?(Integer)
|
182
183
|
if before.zero?
|
183
|
-
@tube.say(start, "There were no facts, nothing deleted by
|
184
|
+
@tube.say(start, "There were no facts, nothing deleted by #{@term} #{tail}")
|
184
185
|
elsif r.zero?
|
185
|
-
@tube.say(start, "No facts out of #{before} deleted by
|
186
|
+
@tube.say(start, "No facts out of #{before} deleted by #{@term} #{tail}")
|
186
187
|
else
|
187
|
-
@tube.say(start, "Deleted #{r} fact(s) out of #{before} by
|
188
|
+
@tube.say(start, "Deleted #{r} fact(s) out of #{before} by #{@term} #{tail}")
|
188
189
|
end
|
189
190
|
r
|
190
191
|
end
|
data/lib/factbase/query.rb
CHANGED
@@ -44,7 +44,7 @@ class Factbase::Query
|
|
44
44
|
return to_enum(__method__, fb, params) unless block_given?
|
45
45
|
yielded = 0
|
46
46
|
params = params.transform_keys(&:to_s) if params.is_a?(Hash)
|
47
|
-
maybe = @term.predict(@maps, Factbase::Tee.new({}, params))
|
47
|
+
maybe = @term.predict(@maps, fb, Factbase::Tee.new({}, params))
|
48
48
|
maybe ||= @maps unless maybe.equal?(@maps)
|
49
49
|
maybe.each do |m|
|
50
50
|
extras = {}
|
data/lib/factbase/tee.rb
CHANGED
@@ -27,7 +27,8 @@ class Factbase::Tee
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def all_properties
|
30
|
-
@fact.
|
30
|
+
(@fact.is_a?(Hash) ? @fact.keys : @fact.all_properties) +
|
31
|
+
(@upper.is_a?(Hash) ? @upper.keys : @upper.all_properties)
|
31
32
|
end
|
32
33
|
|
33
34
|
others do |*args|
|
data/lib/factbase/term.rb
CHANGED
@@ -53,6 +53,9 @@ class Factbase::Term
|
|
53
53
|
require_relative 'terms/aggregates'
|
54
54
|
include Factbase::Aggregates
|
55
55
|
|
56
|
+
require_relative 'terms/lists'
|
57
|
+
include Factbase::Lists
|
58
|
+
|
56
59
|
require_relative 'terms/strings'
|
57
60
|
include Factbase::Strings
|
58
61
|
|
@@ -107,8 +110,13 @@ class Factbase::Term
|
|
107
110
|
# @param [Array<Hash>] maps Records to iterate, maybe
|
108
111
|
# @param [Hash] _params Params to use (keys must be strings, not symbols, with values as arrays)
|
109
112
|
# @return [Array<Hash>] Records to iterate
|
110
|
-
def predict(maps,
|
111
|
-
|
113
|
+
def predict(maps, fb, params)
|
114
|
+
m = :"#{@op}_predict"
|
115
|
+
if respond_to?(m)
|
116
|
+
send(m, maps, fb, params)
|
117
|
+
else
|
118
|
+
maps
|
119
|
+
end
|
112
120
|
end
|
113
121
|
|
114
122
|
# Does it match the fact?
|
@@ -0,0 +1,42 @@
|
|
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
|
+
# Lists management.
|
9
|
+
#
|
10
|
+
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
11
|
+
# Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
|
12
|
+
# License:: MIT
|
13
|
+
module Factbase::Lists
|
14
|
+
def sorted(_fact, _maps, _fb)
|
15
|
+
true
|
16
|
+
end
|
17
|
+
|
18
|
+
def sorted_predict(maps, fb, params)
|
19
|
+
assert_args(2)
|
20
|
+
prop = @operands[0]
|
21
|
+
raise "A symbol is expected as first argument of 'sorted'" unless prop.is_a?(Symbol)
|
22
|
+
term = @operands[1]
|
23
|
+
raise "A term is expected, but '#{term}' provided" unless term.is_a?(Factbase::Term)
|
24
|
+
fb.query(term, maps).each(fb, params).to_a
|
25
|
+
.reject { |m| m[prop].nil? }
|
26
|
+
.sort_by { |m| m[prop].first }
|
27
|
+
.map { |m| m.all_properties.to_h { |k| [k, m[k]] } }
|
28
|
+
end
|
29
|
+
|
30
|
+
def inverted(_fact, _maps, _fb)
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
34
|
+
def inverted_predict(maps, fb, params)
|
35
|
+
assert_args(1)
|
36
|
+
term = @operands[0]
|
37
|
+
raise "A term is expected, but '#{term}' provided" unless term.is_a?(Factbase::Term)
|
38
|
+
fb.query(term, maps).each(fb, params).to_a
|
39
|
+
.reverse
|
40
|
+
.map { |m| m.all_properties.to_h { |k| [k, m[k]] } }
|
41
|
+
end
|
42
|
+
end
|
@@ -31,4 +31,12 @@ module Factbase::Ordering
|
|
31
31
|
end
|
32
32
|
pass
|
33
33
|
end
|
34
|
+
|
35
|
+
def head(*)
|
36
|
+
max = @operands[0]
|
37
|
+
raise "An integer is expected as first argument of 'head'" unless max.is_a?(Integer)
|
38
|
+
@passed = 0 if @passed.nil?
|
39
|
+
@passed += 1
|
40
|
+
@passed <= max
|
41
|
+
end
|
34
42
|
end
|
data/lib/factbase/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: factbase
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.16.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yegor Bugayenko
|
@@ -202,6 +202,7 @@ files:
|
|
202
202
|
- lib/factbase/terms/casting.rb
|
203
203
|
- lib/factbase/terms/debug.rb
|
204
204
|
- lib/factbase/terms/defn.rb
|
205
|
+
- lib/factbase/terms/lists.rb
|
205
206
|
- lib/factbase/terms/logical.rb
|
206
207
|
- lib/factbase/terms/math.rb
|
207
208
|
- lib/factbase/terms/meta.rb
|