factbase 0.19.11 → 0.19.12

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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +5 -4
  3. data/Gemfile.lock +16 -12
  4. data/README.md +49 -18
  5. data/Rakefile +2 -7
  6. data/factbase.gemspec +11 -11
  7. data/lib/factbase/accum.rb +1 -1
  8. data/lib/factbase/cached/cached_fact.rb +1 -2
  9. data/lib/factbase/cached/cached_factbase.rb +3 -3
  10. data/lib/factbase/cached/cached_query.rb +4 -6
  11. data/lib/factbase/cached/cached_term.rb +1 -2
  12. data/lib/factbase/churn.rb +4 -8
  13. data/lib/factbase/fact.rb +12 -9
  14. data/lib/factbase/flatten.rb +2 -2
  15. data/lib/factbase/impatient.rb +14 -13
  16. data/lib/factbase/indexed/indexed_and.rb +14 -20
  17. data/lib/factbase/indexed/indexed_eq.rb +5 -1
  18. data/lib/factbase/indexed/indexed_fact.rb +1 -4
  19. data/lib/factbase/indexed/indexed_factbase.rb +4 -4
  20. data/lib/factbase/indexed/indexed_gt.rb +3 -1
  21. data/lib/factbase/indexed/indexed_gte.rb +51 -0
  22. data/lib/factbase/indexed/indexed_lt.rb +3 -1
  23. data/lib/factbase/indexed/indexed_lte.rb +51 -0
  24. data/lib/factbase/indexed/indexed_not.rb +1 -1
  25. data/lib/factbase/indexed/indexed_or.rb +2 -2
  26. data/lib/factbase/indexed/indexed_query.rb +6 -7
  27. data/lib/factbase/indexed/indexed_term.rb +10 -6
  28. data/lib/factbase/indexed/indexed_unique.rb +4 -2
  29. data/lib/factbase/inv.rb +3 -3
  30. data/lib/factbase/lazy_taped.rb +10 -13
  31. data/lib/factbase/lazy_taped_hash.rb +2 -1
  32. data/lib/factbase/light.rb +1 -1
  33. data/lib/factbase/logged.rb +37 -34
  34. data/lib/factbase/pre.rb +3 -3
  35. data/lib/factbase/query.rb +4 -5
  36. data/lib/factbase/rules.rb +8 -8
  37. data/lib/factbase/sync/sync_factbase.rb +2 -2
  38. data/lib/factbase/syntax.rb +18 -19
  39. data/lib/factbase/tallied.rb +6 -7
  40. data/lib/factbase/taped.rb +5 -11
  41. data/lib/factbase/tee.rb +2 -2
  42. data/lib/factbase/term.rb +53 -60
  43. data/lib/factbase/terms/agg.rb +3 -4
  44. data/lib/factbase/terms/arithmetic.rb +7 -7
  45. data/lib/factbase/terms/as.rb +2 -2
  46. data/lib/factbase/terms/assert.rb +5 -13
  47. data/lib/factbase/terms/base.rb +6 -7
  48. data/lib/factbase/terms/best.rb +1 -1
  49. data/lib/factbase/terms/boolean.rb +1 -1
  50. data/lib/factbase/terms/compare.rb +2 -1
  51. data/lib/factbase/terms/defn.rb +8 -6
  52. data/lib/factbase/terms/empty.rb +1 -1
  53. data/lib/factbase/terms/first.rb +2 -2
  54. data/lib/factbase/terms/head.rb +3 -3
  55. data/lib/factbase/terms/inverted.rb +2 -2
  56. data/lib/factbase/terms/join.rb +8 -7
  57. data/lib/factbase/terms/matches.rb +4 -4
  58. data/lib/factbase/terms/max.rb +1 -1
  59. data/lib/factbase/terms/min.rb +1 -1
  60. data/lib/factbase/terms/nth.rb +3 -3
  61. data/lib/factbase/terms/plus.rb +1 -1
  62. data/lib/factbase/terms/prev.rb +3 -6
  63. data/lib/factbase/terms/sorted.rb +2 -2
  64. data/lib/factbase/terms/sprintf.rb +5 -4
  65. data/lib/factbase/terms/sum.rb +1 -1
  66. data/lib/factbase/terms/to_float.rb +2 -2
  67. data/lib/factbase/terms/to_integer.rb +2 -2
  68. data/lib/factbase/terms/to_string.rb +1 -1
  69. data/lib/factbase/terms/to_time.rb +2 -2
  70. data/lib/factbase/terms/traced.rb +2 -2
  71. data/lib/factbase/terms/undef.rb +2 -2
  72. data/lib/factbase/terms/unique.rb +3 -7
  73. data/lib/factbase/to_json.rb +1 -1
  74. data/lib/factbase/to_xml.rb +5 -9
  75. data/lib/factbase/to_yaml.rb +1 -1
  76. data/lib/factbase/version.rb +1 -2
  77. data/lib/factbase.rb +27 -10
  78. data/lib/fuzz.rb +3 -3
  79. metadata +3 -1
@@ -43,7 +43,7 @@ class Factbase::Syntax
43
43
  rescue StandardError => e
44
44
  err = "#{e.message} (#{Backtrace.new(e)}) in \"#{@query}\""
45
45
  err = "#{err}, tokens: #{@tokens}" unless @tokens.nil?
46
- raise Broken, err
46
+ raise(Broken, err)
47
47
  end
48
48
 
49
49
  private
@@ -52,12 +52,12 @@ class Factbase::Syntax
52
52
  # @return [Term] The term detected
53
53
  def build
54
54
  @tokens ||= to_tokens
55
- raise 'No tokens' if @tokens.empty?
55
+ raise(StandardError, 'No tokens') if @tokens.empty?
56
56
  @ast ||= to_ast(@tokens, 0)
57
- raise "Too many terms (#{@ast[1]} != #{@tokens.size})" if @ast[1] != @tokens.size
57
+ raise(ArgumentError, "Too many terms (#{@ast[1]} != #{@tokens.size})") if @ast[1] != @tokens.size
58
58
  t = @ast[0]
59
- raise 'No terms found in the AST' if t.nil?
60
- raise "#{t.class.name} is not an instance of Term" unless t.is_a?(Factbase::Term)
59
+ raise(StandardError, 'No terms found in the AST') if t.nil?
60
+ raise(ArgumentError, "#{t.class.name} is not an instance of Term") unless t.is_a?(Factbase::Term)
61
61
  t
62
62
  end
63
63
 
@@ -73,25 +73,24 @@ class Factbase::Syntax
73
73
  # @param [Integer] at Position to start parsing from
74
74
  # @return [Array<Factbase::Term,Integer>] The term detected and ending position
75
75
  def to_ast(tokens, at)
76
- raise "Closing too soon at ##{at}" if tokens[at] == :close
76
+ raise(StandardError, "Closing too soon at ##{at}") if tokens[at] == :close
77
77
  return [tokens[at], at + 1] unless tokens[at] == :open
78
78
  at += 1
79
79
  op = tokens[at]
80
- raise 'No token found' if op == :close
80
+ raise(StandardError, 'No token found') if op == :close
81
81
  operands = []
82
82
  at += 1
83
83
  loop do
84
- raise "End of token stream at ##{at}" if tokens[at].nil?
84
+ raise(StandardError, "End of token stream at ##{at}") if tokens[at].nil?
85
85
  break if tokens[at] == :close
86
86
  (operand, at1) = to_ast(tokens, at)
87
- raise "Stuck at position ##{at}" if at == at1
88
- raise "Jump back at position ##{at}" if at1 < at
87
+ raise(StandardError, "Stuck at position ##{at}") if at == at1
88
+ raise(StandardError, "Jump back at position ##{at}") if at1 < at
89
89
  at = at1
90
90
  operands << operand
91
91
  break if tokens[at] == :close
92
92
  end
93
- t = Factbase::Term.new(op, operands)
94
- [t, at + 1]
93
+ [Factbase::Term.new(op, operands), at + 1]
95
94
  end
96
95
 
97
96
  # Turns a query into an array of tokens.
@@ -100,7 +99,7 @@ class Factbase::Syntax
100
99
  list = []
101
100
  acc = ''
102
101
  quotes = ['\'', '"']
103
- spaces = [' ', ')']
102
+ spaces = [' ', ')', "\n", "\t", "\r"]
104
103
  string = false
105
104
  comment = false
106
105
  @query.to_s.chars.each do |c|
@@ -128,26 +127,26 @@ class Factbase::Syntax
128
127
  when ')'
129
128
  list << :close
130
129
  when ' ', "\n", "\t", "\r"
131
- # ignore it
130
+ next
132
131
  else
133
132
  acc += c
134
133
  end
135
134
  end
136
- raise 'String not closed' if string
135
+ raise(StandardError, 'String not closed') if string
137
136
  list.map do |t|
138
137
  if t.is_a?(Symbol)
139
138
  t
140
139
  elsif t.start_with?('\'', '"')
141
- raise 'String literal can\'t be empty' if t.length <= 2
140
+ raise(ArgumentError, 'String literal can\'t be empty') if t.length <= 2
142
141
  t[1..-2]
143
142
  elsif t.match?(/^(\+|-)?[0-9]+$/)
144
- t.to_i
143
+ Integer(t, 10)
145
144
  elsif t.match?(/^(\+|-)?[0-9]+\.[0-9]+(e(\+|-)[0-9]+)?$/)
146
- t.to_f
145
+ Float(t)
147
146
  elsif t.match?(/^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$/)
148
147
  Time.parse(t)
149
148
  else
150
- raise "Wrong symbol format (#{t})" unless t.match?(/^([_a-z][a-zA-Z0-9_]*|\$[_a-z]+)$/)
149
+ raise(ArgumentError, "Wrong symbol format (#{t})") unless t.match?(/^([_a-z][a-zA-Z0-9_]*|\$[_a-z]+)$/)
151
150
  t.to_sym
152
151
  end
153
152
  end
@@ -18,7 +18,7 @@ class Factbase::Tallied
18
18
  attr_reader :churn
19
19
 
20
20
  def initialize(fb, churn = Factbase::Churn.new)
21
- raise 'The "fb" is nil' if fb.nil?
21
+ raise(ArgumentError, 'The "fb" is nil') if fb.nil?
22
22
  @fb = fb
23
23
  @churn = churn
24
24
  end
@@ -26,9 +26,8 @@ class Factbase::Tallied
26
26
  decoor(:fb)
27
27
 
28
28
  def insert
29
- f = Fact.new(@fb.insert, @churn)
30
29
  @churn.append(1, 0, 0)
31
- f
30
+ Fact.new(@fb.insert, @churn)
32
31
  end
33
32
 
34
33
  def query(query, maps = nil)
@@ -39,13 +38,13 @@ class Factbase::Tallied
39
38
  before = @churn.dup
40
39
  commit = false
41
40
  @fb.txn do |fbt|
42
- catch :rollback do
43
- yield Factbase::Tallied.new(fbt, @churn)
41
+ catch(:rollback) do
42
+ yield(Factbase::Tallied.new(fbt, @churn))
44
43
  commit = true
45
44
  end
46
45
  rescue Factbase::Rollback => e
47
46
  @churn = before
48
- raise e
47
+ raise(e)
49
48
  ensure
50
49
  @churn = before unless commit
51
50
  end
@@ -96,7 +95,7 @@ class Factbase::Tallied
96
95
  def each(fb = @fb, params = {}, &)
97
96
  return to_enum(__method__, fb, params) unless block_given?
98
97
  @query.each(fb, params) do |f|
99
- yield Fact.new(f, @churn)
98
+ yield(Fact.new(f, @churn))
100
99
  end
101
100
  end
102
101
 
@@ -52,13 +52,13 @@ class Factbase::Taped
52
52
  def each
53
53
  return to_enum(__method__) unless block_given?
54
54
  @origin.each do |m|
55
- yield TapedHash.new(m, @added)
55
+ yield(TapedHash.new(m, @added))
56
56
  end
57
57
  end
58
58
 
59
59
  def delete_if
60
60
  @origin.delete_if do |m|
61
- r = yield m
61
+ r = yield(m)
62
62
  @deleted.append(m.object_id) if r
63
63
  r
64
64
  end
@@ -153,14 +153,8 @@ class Factbase::Taped
153
153
  private
154
154
 
155
155
  def join(other)
156
- n = yield @origin.to_a, other.to_a
157
- raise 'Cannot join with another Taped' if other.respond_to?(:inserted)
158
- raise 'Can only join with array' unless other.is_a?(Array)
159
- Factbase::Taped.new(
160
- n,
161
- inserted: @inserted,
162
- deleted: @deleted,
163
- added: @added
164
- )
156
+ raise(ArgumentError, 'Cannot join with another Taped') if other.respond_to?(:inserted)
157
+ raise(ArgumentError, 'Can only join with array') unless other.is_a?(Array)
158
+ Factbase::Taped.new(yield(@origin.to_a, other.to_a), inserted: @inserted, deleted: @deleted, added: @added)
165
159
  end
166
160
  end
data/lib/factbase/tee.rb CHANGED
@@ -16,9 +16,9 @@ class Factbase::Tee
16
16
  # @param [Factbase::Fact] fact Primary fact to use for reading
17
17
  # @param [Factbase::Fact] upper Fact to access with a "$" prefix
18
18
  def initialize(fact, upper)
19
- raise 'Fact is nil' if fact.nil?
19
+ raise(ArgumentError, 'Fact is nil') if fact.nil?
20
20
  @fact = fact
21
- raise 'Upper is nil' if upper.nil?
21
+ raise(ArgumentError, 'Upper is nil') if upper.nil?
22
22
  @upper = upper
23
23
  end
24
24
 
data/lib/factbase/term.rb CHANGED
@@ -7,60 +7,60 @@ require 'backtrace'
7
7
  require_relative '../factbase'
8
8
  require_relative 'fact'
9
9
  require_relative 'tee'
10
- require_relative 'terms/unique'
11
- require_relative 'terms/prev'
10
+ require_relative 'terms/absent'
11
+ require_relative 'terms/agg'
12
+ require_relative 'terms/always'
13
+ require_relative 'terms/and'
14
+ require_relative 'terms/as'
15
+ require_relative 'terms/assert'
12
16
  require_relative 'terms/concat'
13
- require_relative 'terms/sprintf'
14
- require_relative 'terms/matches'
15
17
  require_relative 'terms/contains'
16
- require_relative 'terms/starts_with'
18
+ require_relative 'terms/count'
19
+ require_relative 'terms/defn'
20
+ require_relative 'terms/div'
21
+ require_relative 'terms/either'
22
+ require_relative 'terms/empty'
17
23
  require_relative 'terms/ends_with'
18
- require_relative 'terms/traced'
19
- require_relative 'terms/assert'
20
24
  require_relative 'terms/env'
21
- require_relative 'terms/defn'
22
- require_relative 'terms/undef'
23
- require_relative 'terms/as'
24
- require_relative 'terms/join'
25
+ require_relative 'terms/eq'
25
26
  require_relative 'terms/exists'
26
- require_relative 'terms/absent'
27
- require_relative 'terms/size'
28
- require_relative 'terms/type'
29
- require_relative 'terms/nil'
30
- require_relative 'terms/many'
31
- require_relative 'terms/one'
32
- require_relative 'terms/to_string'
33
- require_relative 'terms/to_integer'
34
- require_relative 'terms/to_float'
35
- require_relative 'terms/to_time'
36
- require_relative 'terms/sorted'
37
- require_relative 'terms/inverted'
27
+ require_relative 'terms/first'
28
+ require_relative 'terms/gt'
29
+ require_relative 'terms/gte'
38
30
  require_relative 'terms/head'
39
- require_relative 'terms/plus'
40
- require_relative 'terms/minus'
41
- require_relative 'terms/times'
42
- require_relative 'terms/div'
43
- require_relative 'terms/zero'
44
- require_relative 'terms/eq'
31
+ require_relative 'terms/inverted'
32
+ require_relative 'terms/join'
45
33
  require_relative 'terms/lt'
46
34
  require_relative 'terms/lte'
47
- require_relative 'terms/gt'
48
- require_relative 'terms/gte'
49
- require_relative 'terms/always'
35
+ require_relative 'terms/many'
36
+ require_relative 'terms/matches'
37
+ require_relative 'terms/max'
38
+ require_relative 'terms/min'
39
+ require_relative 'terms/minus'
50
40
  require_relative 'terms/never'
41
+ require_relative 'terms/nil'
51
42
  require_relative 'terms/not'
52
- require_relative 'terms/or'
53
- require_relative 'terms/and'
54
- require_relative 'terms/when'
55
- require_relative 'terms/either'
56
- require_relative 'terms/count'
57
- require_relative 'terms/first'
58
43
  require_relative 'terms/nth'
44
+ require_relative 'terms/one'
45
+ require_relative 'terms/or'
46
+ require_relative 'terms/plus'
47
+ require_relative 'terms/prev'
48
+ require_relative 'terms/size'
49
+ require_relative 'terms/sorted'
50
+ require_relative 'terms/sprintf'
51
+ require_relative 'terms/starts_with'
59
52
  require_relative 'terms/sum'
60
- require_relative 'terms/agg'
61
- require_relative 'terms/empty'
62
- require_relative 'terms/min'
63
- require_relative 'terms/max'
53
+ require_relative 'terms/times'
54
+ require_relative 'terms/to_float'
55
+ require_relative 'terms/to_integer'
56
+ require_relative 'terms/to_string'
57
+ require_relative 'terms/to_time'
58
+ require_relative 'terms/traced'
59
+ require_relative 'terms/type'
60
+ require_relative 'terms/undef'
61
+ require_relative 'terms/unique'
62
+ require_relative 'terms/when'
63
+ require_relative 'terms/zero'
64
64
 
65
65
  # Term.
66
66
  #
@@ -90,13 +90,7 @@ require_relative 'terms/max'
90
90
  # Copyright:: Copyright (c) 2024-2026 Yegor Bugayenko
91
91
  # License:: MIT
92
92
  class Factbase::Term < Factbase::TermBase
93
- # The operator of this term
94
- # @return [Symbol] The operator
95
- attr_reader :op
96
-
97
- # The operands of this term
98
- # @return [Array] The operands
99
- attr_reader :operands
93
+ attr_reader :op, :operands
100
94
 
101
95
  # Ctor.
102
96
  # @param [Symbol] operator Operator
@@ -167,9 +161,8 @@ class Factbase::Term < Factbase::TermBase
167
161
  # @param [Module] type The type to extend with
168
162
  # @param [Hash] args Attributes to set
169
163
  def redress!(type, **args)
170
- extend type
171
-
172
- args.each { |k, v| send(:instance_variable_set, :"@#{k}", v) }
164
+ extend(type)
165
+ args.each { |k, v| __send__(:instance_variable_set, :"@#{k}", v) }
173
166
  @operands.map do |op|
174
167
  if op.is_a?(Factbase::Term)
175
168
  op.redress!(type, **args)
@@ -195,7 +188,7 @@ class Factbase::Term < Factbase::TermBase
195
188
  maps
196
189
  end
197
190
  elsif respond_to?(m)
198
- send(m, maps, fb, params)
191
+ __send__(m, maps, fb, params)
199
192
  else
200
193
  maps
201
194
  end
@@ -210,12 +203,12 @@ class Factbase::Term < Factbase::TermBase
210
203
  if @terms.key?(@op)
211
204
  @terms[@op].evaluate(fact, maps, fb)
212
205
  else
213
- send(@op, fact, maps, fb)
206
+ __send__(@op, fact, maps, fb)
214
207
  end
215
208
  rescue NoMethodError => e
216
- raise "Probably the term '#{@op}' is not defined at #{self}: #{e.message}"
209
+ raise(RuntimeError, "Probably the term '#{@op}' is not defined at #{self}: #{e.message}")
217
210
  rescue StandardError => e
218
- raise "#{e.message.inspect} at #{self} at #{e.backtrace[0]}"
211
+ raise(RuntimeError, "#{e.message.inspect} at #{self} at #{e.backtrace[0]}")
219
212
  end
220
213
 
221
214
  # Simplify it if possible.
@@ -226,7 +219,7 @@ class Factbase::Term < Factbase::TermBase
226
219
  else
227
220
  m = "#{@op}_simplify"
228
221
  if respond_to?(m, true)
229
- send(m)
222
+ __send__(m)
230
223
  else
231
224
  self
232
225
  end
@@ -262,11 +255,11 @@ class Factbase::Term < Factbase::TermBase
262
255
  def at(fact, maps, fb)
263
256
  assert_args(2)
264
257
  i = _values(0, fact, maps, fb)
265
- raise "Too many values (#{i.size}) at first position, one expected" unless i.size == 1
258
+ raise(RuntimeError, "Too many values (#{i.size}) at first position, one expected") unless i.size == 1
266
259
  i = i[0]
267
- return nil if i.nil?
260
+ return if i.nil?
268
261
  v = _values(1, fact, maps, fb)
269
- return nil if v.nil?
262
+ return if v.nil?
270
263
  v[i]
271
264
  end
272
265
  end
@@ -23,13 +23,12 @@ class Factbase::Agg < Factbase::TermBase
23
23
  assert_args(2)
24
24
  selector = @operands[0]
25
25
  unless selector.is_a?(Factbase::Term) || selector.is_a?(Factbase::TermBase)
26
- raise "A term is expected, but '#{selector}' provided"
26
+ raise(ArgumentError, "A term is expected, but '#{selector}' provided")
27
27
  end
28
28
  term = @operands[1]
29
29
  unless term.is_a?(Factbase::Term) || term.is_a?(Factbase::TermBase)
30
- raise "A term is expected, but '#{term}' provided"
30
+ raise(ArgumentError, "A term is expected, but '#{term}' provided")
31
31
  end
32
- subset = fb.query(selector, maps).each(fb, fact).to_a
33
- term.evaluate(nil, subset, fb)
32
+ term.evaluate(nil, fb.query(selector, maps).each(fb, fact).to_a, fb)
34
33
  end
35
34
  end
@@ -24,16 +24,16 @@ class Factbase::Arithmetic < Factbase::TermBase
24
24
  def evaluate(fact, maps, fb)
25
25
  assert_args(2)
26
26
  lefts = _values(0, fact, maps, fb)
27
- return nil if lefts.nil?
28
- raise 'Too many values at first position, one expected' unless lefts.size == 1
27
+ return if lefts.nil?
28
+ raise(ArgumentError, 'Too many values at first position, one expected') unless lefts.size == 1
29
29
  rights = _values(1, fact, maps, fb)
30
- return nil if rights.nil?
31
- raise 'Too many values at second position, one expected' unless rights.size == 1
30
+ return if rights.nil?
31
+ raise(ArgumentError, 'Too many values at second position, one expected') unless rights.size == 1
32
32
  v = lefts[0]
33
33
  r = rights[0]
34
34
  if v.is_a?(Time) && r.is_a?(String)
35
35
  (num, units) = r.split
36
- num = num.to_i
36
+ num = Integer(num, 10)
37
37
  r =
38
38
  case units
39
39
  when 'seconds', 'second'
@@ -47,9 +47,9 @@ class Factbase::Arithmetic < Factbase::TermBase
47
47
  when 'weeks', 'week'
48
48
  num * 60 * 60 * 24 * 7
49
49
  else
50
- raise "Unknown time unit '#{units}' in '#{r}"
50
+ raise(ArgumentError, "Unknown time unit '#{units}' in '#{r}")
51
51
  end
52
52
  end
53
- v.send(@op, r)
53
+ v.__send__(@op, r)
54
54
  end
55
55
  end
@@ -23,9 +23,9 @@ class Factbase::As < Factbase::TermBase
23
23
  def evaluate(fact, maps, fb)
24
24
  assert_args(2)
25
25
  a = @operands[0]
26
- raise "A symbol is expected as first argument of 'as'" unless a.is_a?(Symbol)
26
+ raise(ArgumentError, "A symbol is expected as first argument of 'as'") unless a.is_a?(Symbol)
27
27
  vv = _values(1, fact, maps, fb)
28
- vv&.each { |v| fact.send(:"#{a}=", v) }
28
+ vv&.each { |v| fact.__send__(:"#{a}=", v) }
29
29
  true
30
30
  end
31
31
  end
@@ -26,24 +26,16 @@ class Factbase::Assert < Factbase::TermBase
26
26
  assert_args(2)
27
27
  message = @operands[0]
28
28
  unless message.is_a?(String)
29
- raise ArgumentError,
30
- "A string is expected as first argument of 'assert', but '#{message}' provided"
29
+ raise(ArgumentError, "A string is expected as first argument of 'assert', but '#{message}' provided")
31
30
  end
32
31
  t = @operands[1]
33
32
  unless t.is_a?(Factbase::Term)
34
- raise ArgumentError,
35
- "A term is expected as second argument of 'assert', but '#{t}' provided"
33
+ raise(ArgumentError, "A term is expected as second argument of 'assert', but '#{t}' provided")
36
34
  end
37
35
  result = t.evaluate(fact, maps, fb)
38
- # Convert result to boolean-like evaluation
39
- # Arrays are truthy if they contain at least one truthy element
40
- truthy =
41
- if result.is_a?(Array)
42
- result.any? { |v| v && v != 0 }
43
- else
44
- result && result != 0
45
- end
46
- raise message unless truthy
36
+ unless result.is_a?(Array) ? result.any? { |v| v && v != 0 } : (result && result != 0)
37
+ raise(StandardError, message)
38
+ end
47
39
  true
48
40
  end
49
41
  end
@@ -33,15 +33,14 @@ class Factbase::TermBase
33
33
 
34
34
  def assert_args(num)
35
35
  c = @operands.size
36
- raise "Too many (#{c}) operands for '#{@op}' (#{num} expected)" if c > num
37
- raise "Too few (#{c}) operands for '#{@op}' (#{num} expected)" if c < num
36
+ raise(ArgumentError, "Too many (#{c}) operands for '#{@op}' (#{num} expected)") if c > num
37
+ raise(ArgumentError, "Too few (#{c}) operands for '#{@op}' (#{num} expected)") if c < num
38
38
  end
39
39
 
40
40
  def _by_symbol(pos, fact)
41
41
  o = @operands[pos]
42
- raise "A symbol expected at ##{pos}, but '#{o}' (#{o.class}) provided" unless o.is_a?(Symbol)
43
- k = o.to_s
44
- fact[k]
42
+ raise(ArgumentError, "A symbol expected at ##{pos}, but '#{o}' (#{o.class}) provided") unless o.is_a?(Symbol)
43
+ fact[o.to_s]
45
44
  end
46
45
 
47
46
  # @return [Array|nil] Either array of values or NIL
@@ -59,9 +58,9 @@ class Factbase::TermBase
59
58
  [v]
60
59
  end
61
60
  end
62
- raise 'Why not array?' unless v.is_a?(Array)
61
+ raise(ArgumentError, 'Why not array?') unless v.is_a?(Array)
63
62
  unless v.all? { |i| [Float, Integer, String, Time, TrueClass, FalseClass].any? { |t| i.is_a?(t) } }
64
- raise 'Wrong type inside'
63
+ raise(ArgumentError, 'Wrong type inside')
65
64
  end
66
65
  v
67
66
  end
@@ -10,7 +10,7 @@ class Factbase::Best
10
10
  end
11
11
 
12
12
  def evaluate(key, maps)
13
- raise "A symbol is expected, but #{key} provided" unless key.is_a?(Symbol)
13
+ raise(ArgumentError, "A symbol is expected, but #{key} provided") unless key.is_a?(Symbol)
14
14
  best = nil
15
15
  maps.each do |m|
16
16
  vv = m[key.to_s]
@@ -23,6 +23,6 @@ class Factbase::Boolean
23
23
  val = val[0] if val.respond_to?(:each)
24
24
  return false if val.nil?
25
25
  return val if val.is_a?(TrueClass) || val.is_a?(FalseClass)
26
- raise "Boolean is expected, while #{val.class} received from #{@from}"
26
+ raise(ArgumentError, "Boolean is expected, while #{val.class} received from #{@from}")
27
27
  end
28
28
  end
@@ -43,9 +43,10 @@ class Factbase::Compare < Factbase::TermBase
43
43
  # @param [Object] right Right value
44
44
  # @return [Boolean] The result of the comparison
45
45
  def _compare(left, right)
46
- left.send(@op, right)
46
+ left.__send__(@op, right)
47
47
  rescue ArgumentError => e
48
48
  raise(
49
+ RuntimeError,
49
50
  "Cannot compare #{left.inspect} (#{left.class}) " \
50
51
  "with #{right.inspect} (#{right.class}) using (compare #{@op}): #{e.message}"
51
52
  )
@@ -25,13 +25,15 @@ class Factbase::Defn < Factbase::TermBase
25
25
  def evaluate(_fact, _maps, _fb)
26
26
  assert_args(2)
27
27
  fn = @operands[0]
28
- raise "A symbol expected as first argument of 'defn'" unless fn.is_a?(Symbol)
29
- raise "Can't use '#{fn}' name as a term" if Factbase::Term.method_defined?(fn)
30
- raise "Term '#{fn}' is already defined" if Factbase::Term.private_method_defined?(fn, false)
31
- raise "The '#{fn}' is a bad name for a term" unless fn.match?(/^[a-z_]+$/)
32
- e = "class Factbase::Term\nprivate\ndef #{fn}(fact, maps, fb)\n#{@operands[1]}\nend\nend"
28
+ raise(ArgumentError, "A symbol expected as first argument of 'defn'") unless fn.is_a?(Symbol)
29
+ raise(ArgumentError, "Can't use '#{fn}' name as a term") if Factbase::Term.method_defined?(fn)
30
+ raise(ArgumentError, "Term '#{fn}' is already defined") if Factbase::Term.private_method_defined?(fn, false)
31
+ raise(ArgumentError, "The '#{fn}' is a bad name for a term") unless fn.match?(/^[a-z_]+$/)
33
32
  # rubocop:disable Security/Eval
34
- eval(e)
33
+ eval(
34
+ "class Factbase::Term\nprivate\ndef #{fn}(fact, maps, fb)\n#{@operands[1]}\nend\nend",
35
+ binding, __FILE__, __LINE__ - 1
36
+ )
35
37
  # rubocop:enable Security/Eval
36
38
  true
37
39
  end
@@ -24,7 +24,7 @@ class Factbase::Empty < Factbase::TermBase
24
24
  assert_args(1)
25
25
  term = @operands[0]
26
26
  unless term.is_a?(Factbase::Term) || term.is_a?(Factbase::TermBase)
27
- raise "A term is expected, but '#{term}' provided"
27
+ raise(ArgumentError, "A term is expected, but '#{term}' provided")
28
28
  end
29
29
  # rubocop:disable Lint/UnreachableLoop
30
30
  fb.query(term, maps).each(fb, fact) do
@@ -22,9 +22,9 @@ class Factbase::First < Factbase::TermBase
22
22
  def evaluate(_fact, maps, _fb)
23
23
  assert_args(1)
24
24
  k = @operands[0]
25
- raise "A symbol is expected, but #{k} provided" unless k.is_a?(Symbol)
25
+ raise(ArgumentError, "A symbol is expected, but #{k} provided") unless k.is_a?(Symbol)
26
26
  first = maps[0]
27
- return nil if first.nil?
27
+ return if first.nil?
28
28
  first[k.to_s]
29
29
  end
30
30
  end
@@ -27,11 +27,11 @@ class Factbase::Head < Factbase::TermBase
27
27
  def predict(maps, fb, params)
28
28
  assert_args(2)
29
29
  max = @operands[0]
30
- raise "An integer is expected as first argument of '#{@op}'" unless max.is_a?(Integer)
30
+ raise(ArgumentError, "An integer is expected as first argument of '#{@op}'") unless max.is_a?(Integer)
31
31
  term = @operands[1]
32
- raise "A term is expected, but '#{term}' provided" unless term.is_a?(Factbase::Term)
32
+ raise(ArgumentError, "A term is expected, but '#{term}' provided") unless term.is_a?(Factbase::Term)
33
33
  fb.query(term, maps).each(fb, params).to_a
34
34
  .take(max)
35
- .map { |m| m.all_properties.to_h { |k| [k, m[k]] } }
35
+ .map! { |m| m.all_properties.to_h { |k| [k, m[k]] } }
36
36
  end
37
37
  end
@@ -26,9 +26,9 @@ class Factbase::Inverted < Factbase::TermBase
26
26
  def predict(maps, fb, params)
27
27
  assert_args(1)
28
28
  term = @operands[0]
29
- raise "A term is expected, but '#{term}' provided" unless term.is_a?(Factbase::Term)
29
+ raise(ArgumentError, "A term is expected, but '#{term}' provided") unless term.is_a?(Factbase::Term)
30
30
  fb.query(term, maps).each(fb, params).to_a
31
31
  .reverse
32
- .map { |m| m.all_properties.to_h { |k| [k, m[k]] } }
32
+ .map! { |m| m.all_properties.to_h { |k| [k, m[k]] } }
33
33
  end
34
34
  end
@@ -23,18 +23,19 @@ class Factbase::Join < Factbase::TermBase
23
23
  def evaluate(fact, maps, fb)
24
24
  assert_args(2)
25
25
  jumps = @operands[0]
26
- raise "A string is expected as first argument of 'join'" unless jumps.is_a?(String)
27
- jumps = jumps.split(',')
28
- .map(&:strip)
29
- .map { |j| j.split('<=').map(&:strip) }
30
- .map { |j| j.size == 1 ? [j[0], j[0]] : j }
26
+ raise(ArgumentError, "A string is expected as first argument of 'join'") unless jumps.is_a?(String)
27
+ jumps =
28
+ jumps.split(',')
29
+ .map(&:strip)
30
+ .map! { |j| j.split('<=').map(&:strip) }
31
+ .map! { |j| j.size == 1 ? [j[0], j[0]] : j }
31
32
  term = @operands[1]
32
- raise "A term is expected, but '#{term}' provided" unless term.is_a?(Factbase::Term)
33
+ raise(ArgumentError, "A term is expected, but '#{term}' provided") unless term.is_a?(Factbase::Term)
33
34
  subset = fb.query(term, maps).each(fb, fact).to_a
34
35
  subset.each do |s|
35
36
  jumps.each do |to, from|
36
37
  s[from]&.each do |v|
37
- fact.send(:"#{to}=", v)
38
+ fact.__send__(:"#{to}=", v)
38
39
  end
39
40
  end
40
41
  end
@@ -26,10 +26,10 @@ class Factbase::Matches < Factbase::TermBase
26
26
  assert_args(2)
27
27
  str = _values(0, fact, maps, fb)
28
28
  return false if str.nil?
29
- raise 'Exactly one string is expected' unless str.size == 1
29
+ raise(RuntimeError, 'Exactly one string is expected') unless str.size == 1
30
30
  re = _values(1, fact, maps, fb)
31
- raise 'Regexp is nil' if re.nil?
32
- raise 'Exactly one regexp is expected' unless re.size == 1
31
+ raise(RuntimeError, 'Regexp is nil') if re.nil?
32
+ raise(RuntimeError, 'Exactly one regexp is expected') unless re.size == 1
33
33
  str[0].to_s.match?(regexp(re[0]))
34
34
  end
35
35
 
@@ -39,6 +39,6 @@ class Factbase::Matches < Factbase::TermBase
39
39
  key = pattern.to_s
40
40
  @regexps[key] ||= Regexp.new(key)
41
41
  rescue RegexpError => e
42
- raise "Invalid regexp '#{key}': #{e.message}"
42
+ raise(RuntimeError, "Invalid regexp '#{key}': #{e.message}")
43
43
  end
44
44
  end