ruleby 0.9.b4 → 0.9.b7
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.
- data/.gitignore +5 -0
- data/GPL.txt +341 -0
- data/LICENSE.txt +52 -0
- data/README.markdown +22 -0
- data/benchmarks/basic_rules.rb +61 -0
- data/benchmarks/joined_rules.rb +66 -0
- data/benchmarks/miss_manners/data.rb +146 -0
- data/benchmarks/miss_manners/miss_manners.rb +33 -0
- data/benchmarks/miss_manners/model.rb +193 -0
- data/benchmarks/miss_manners/rules.rb +105 -0
- data/benchmarks/model.rb +36 -0
- data/examples/diagnosis.rb +129 -0
- data/examples/fibonacci_example1.rb +44 -0
- data/examples/fibonacci_example2.rb +40 -0
- data/examples/fibonacci_example3.rb +78 -0
- data/examples/fibonacci_rulebook.rb +84 -0
- data/examples/hello.rb +45 -0
- data/examples/ticket.rb +113 -0
- data/examples/wordgame.rb +107 -0
- data/lib/core/engine.rb +26 -24
- data/lib/core/nodes.rb +77 -35
- data/lib/ruleby.rb +0 -37
- data/ruleby.gemspec +17 -0
- data/spec/and_or_spec.rb +252 -0
- data/spec/coercion_spec.rb +5 -0
- data/spec/collect_spec.rb +1021 -0
- data/spec/errors_spec.rb +148 -0
- data/spec/ferrari_spec.rb +39 -0
- data/spec/function_spec.rb +199 -0
- data/spec/hello_spec.rb +34 -0
- data/spec/node_sharing_spec.rb +53 -0
- data/spec/property_spec.rb +69 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +9 -0
- data/tasks/documentation.rake +32 -0
- data/tasks/rspec.rake +21 -0
- data/tasks/test.rake +9 -0
- data/tests/assert_facts.rb +130 -0
- data/tests/common.rb +29 -0
- data/tests/duck_type.rb +79 -0
- data/tests/gets.rb +48 -0
- data/tests/join_nodes.rb +63 -0
- data/tests/nil.rb +72 -0
- data/tests/not_patterns.rb +91 -0
- data/tests/or_patterns.rb +154 -0
- data/tests/regex.rb +47 -0
- data/tests/self_reference.rb +54 -0
- metadata +81 -49
data/examples/ticket.rb
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# This file is part of the Ruleby project (http://ruleby.org)
|
|
2
|
+
#
|
|
3
|
+
# This application is free software; you can redistribute it and/or
|
|
4
|
+
# modify it under the terms of the Ruby license defined in the
|
|
5
|
+
# LICENSE.txt file.
|
|
6
|
+
#
|
|
7
|
+
# Copyright (c) 2007 Joe Kutner and Matt Smith. All rights reserved.
|
|
8
|
+
#
|
|
9
|
+
# * Authors: Joe Kutner, Matt Smith
|
|
10
|
+
#
|
|
11
|
+
|
|
12
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__), '../lib/')
|
|
13
|
+
require 'ruleby'
|
|
14
|
+
|
|
15
|
+
include Ruleby
|
|
16
|
+
|
|
17
|
+
class Customer
|
|
18
|
+
def initialize(name,subscription)
|
|
19
|
+
@name = name
|
|
20
|
+
@subscription = subscription
|
|
21
|
+
end
|
|
22
|
+
attr_reader :name,:subscription
|
|
23
|
+
def to_s
|
|
24
|
+
return '[Customer ' + @name.to_s + ' : ' + @subscription.to_s + ']';
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class Ticket
|
|
29
|
+
def initialize(customer)
|
|
30
|
+
@customer = customer
|
|
31
|
+
@status = :New
|
|
32
|
+
end
|
|
33
|
+
attr :status, true
|
|
34
|
+
attr_reader :customer
|
|
35
|
+
def to_s
|
|
36
|
+
return '[Ticket ' + @customer.to_s + ' : ' + @status.to_s + ']';
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# This example is used in JBoss-Rules to demonstrate durations and the use of
|
|
41
|
+
# custom DSL. We are simply using it here to demonstrate another example.
|
|
42
|
+
class TroubleTicketRulebook < Rulebook
|
|
43
|
+
def rules
|
|
44
|
+
|
|
45
|
+
# This is uses the letigre syntax... but we can mix and match syntaxes in
|
|
46
|
+
# the same rule set.
|
|
47
|
+
rule :New_Ticket, {:priority => 10}, # :duration => 10},
|
|
48
|
+
[Customer, :c],
|
|
49
|
+
[Ticket, :ticket, {m.customer => :c}, m.status == :New] do |vars|
|
|
50
|
+
puts 'New : ' + vars[:ticket].to_s
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Now we are using the ferrari syntax. The rule method can detect which
|
|
54
|
+
# syntax we are using, and compile accordingly.
|
|
55
|
+
rule :Silver_Priority, #{:duration => 3000},
|
|
56
|
+
[Customer, :customer, m.subscription == 'Silver'],
|
|
57
|
+
[Ticket,:ticket, m.customer == b(:customer), m.status == :New] do |vars|
|
|
58
|
+
vars[:ticket].status = :Escalate
|
|
59
|
+
modify vars[:ticket]
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
rule :Gold_Priority, #{:duration => 1000},
|
|
63
|
+
[Customer, :customer, m.subscription == 'Gold'],
|
|
64
|
+
[Ticket,:ticket, m.customer == b(:customer), m.status == :New] do |vars|
|
|
65
|
+
vars[:ticket].status = :Escalate
|
|
66
|
+
modify vars[:ticket]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
rule :Platinum_Priority,
|
|
70
|
+
[Customer, :customer, m.subscription == 'Platinum'],
|
|
71
|
+
[Ticket,:ticket, m.customer == b(:customer), m.status == :New] do |vars|
|
|
72
|
+
vars[:ticket].status = :Escalate
|
|
73
|
+
modify vars[:ticket]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
rule :Escalate,
|
|
77
|
+
[Customer, :c],
|
|
78
|
+
[Ticket, :ticket, {m.customer => :c}, m.status == :Escalate] do |vars|
|
|
79
|
+
puts 'Email : ' + vars[:ticket].to_s
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
rule :Done,
|
|
83
|
+
[Customer, :c],
|
|
84
|
+
[Ticket, :ticket, {m.customer => :c}, m.status == :Done] do |vars|
|
|
85
|
+
puts 'Done : ' + vars[:ticket].to_s
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# FACTS
|
|
91
|
+
|
|
92
|
+
a = Customer.new('A', 'Gold')
|
|
93
|
+
b = Customer.new('B', 'Platinum')
|
|
94
|
+
c = Customer.new('C', 'Silver')
|
|
95
|
+
d = Customer.new('D', 'Silver')
|
|
96
|
+
|
|
97
|
+
t1 = Ticket.new(a)
|
|
98
|
+
t2 = Ticket.new(b)
|
|
99
|
+
t3 = Ticket.new(c)
|
|
100
|
+
t4 = Ticket.new(d)
|
|
101
|
+
|
|
102
|
+
engine :engine do |e|
|
|
103
|
+
TroubleTicketRulebook.new(e).rules
|
|
104
|
+
e.assert a
|
|
105
|
+
e.assert b
|
|
106
|
+
e.assert c
|
|
107
|
+
e.assert d
|
|
108
|
+
e.assert t1
|
|
109
|
+
e.assert t2
|
|
110
|
+
e.assert t3
|
|
111
|
+
e.assert t4
|
|
112
|
+
e.match
|
|
113
|
+
end
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# This file is part of the Ruleby project (http://ruleby.org)
|
|
2
|
+
#
|
|
3
|
+
# This application is free software; you can redistribute it and/or
|
|
4
|
+
# modify it under the terms of the Ruby license defined in the
|
|
5
|
+
# LICENSE.txt file.
|
|
6
|
+
#
|
|
7
|
+
# Copyright (c) 2007 Joe Kutner and Matt Smith. All rights reserved.
|
|
8
|
+
#
|
|
9
|
+
# * Authors: Joe Kutner
|
|
10
|
+
#
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# This example solves the number puzzle problem where
|
|
14
|
+
# GERALD
|
|
15
|
+
# + DONALD
|
|
16
|
+
# ------
|
|
17
|
+
# = ROBERT
|
|
18
|
+
|
|
19
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__), '../lib/')
|
|
20
|
+
require 'ruleby'
|
|
21
|
+
|
|
22
|
+
include Ruleby
|
|
23
|
+
|
|
24
|
+
class Combination
|
|
25
|
+
attr:a
|
|
26
|
+
attr:x
|
|
27
|
+
def initialize(a,x)
|
|
28
|
+
@a = a
|
|
29
|
+
@x = x
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
class WordGameRulebook < Ruleby::Rulebook
|
|
34
|
+
def rules
|
|
35
|
+
rule :generate_combos, [String, :a], [Fixnum, :x] do |v|
|
|
36
|
+
assert Combination.new(v[:a], v[:x])
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
c1 = c{|t,d| ((d+d) % 10) == t }
|
|
40
|
+
c2 = c{|r,d,t,l| ((d+d+(10*l)+(10*l)) % 100) == ((10 * r) + t) }
|
|
41
|
+
c3 = c{|e,d,l,a,r,t| ((d+d+(10*l)+(10*l)+(100*a)+(100*a)) % 1000) == ((100*e)+(10*r)+t) }
|
|
42
|
+
c4 = c{|b,d,l,a,r,n,e,t| ((d+d+(10*l)+(10*l)+(100*a)+(100*a)+(1000*r)+(1000*n)) % 10000) == ((1000*b)+(100*e)+(10*r)+t) }
|
|
43
|
+
c5 = c{|g,d,l,a,r,n,e,o,b,t| (d+d+(10*l)+(10*l)+(100*a)+(100*a)+(1000*r)+(1000*n)+(10000*e)+(10000*o)+(100000*g)+(100000*d)) == ((100000*r)+(10000*o)+(1000*b)+(100*e)+(10*r)+t) }
|
|
44
|
+
|
|
45
|
+
rule :find_solution,
|
|
46
|
+
[Combination, m.a=='D', {m.x => :d}],
|
|
47
|
+
[Combination, m.a=='T', {m.x.not==b(:d)=>:t}, m.x(:d, &c1)],
|
|
48
|
+
[Combination, m.a=='L', {m.x.not==b(:d)=>:l}, m.x.not==b(:t)],
|
|
49
|
+
[Combination, m.a=='R', {m.x.not==b(:d)=>:r}, m.x.not==b(:t), m.x.not==b(:l),
|
|
50
|
+
m.x(:d,:t,:l, &c2)],
|
|
51
|
+
[Combination, m.a=='A', {m.x.not==b(:d)=>:a}, m.x.not==b(:t), m.x.not==b(:l), m.x.not==b(:r)],
|
|
52
|
+
[Combination, m.a=='E', {m.x.not==b(:d)=>:e}, m.x.not==b(:t), m.x.not==b(:l), m.x.not==b(:r),
|
|
53
|
+
m.x.not==b(:a), m.x(:d,:l,:a,:r,:t, &c3)],
|
|
54
|
+
[Combination, m.a=='N', {m.x.not==b(:d)=>:n}, m.x.not==b(:t), m.x.not==b(:l), m.x.not==b(:r),
|
|
55
|
+
m.x.not==b(:a), m.x.not==b(:e)],
|
|
56
|
+
[Combination, m.a=='B', {m.x.not==b(:d)=>:b}, m.x.not==b(:t), m.x.not==b(:l), m.x.not==b(:r),
|
|
57
|
+
m.x.not==b(:a), m.x.not==b(:e), m.x.not==b(:n), m.x(:d,:l,:a,:r,:n,:e,:t, &c4)],
|
|
58
|
+
[Combination, m.a=='O', {m.x.not==b(:d)=>:o}, m.x.not==b(:t), m.x.not==b(:l), m.x.not==b(:r),
|
|
59
|
+
m.x.not==b(:a), m.x.not==b(:e), m.x.not==b(:n), m.x.not==b(:b)],
|
|
60
|
+
[Combination, m.a=='G', {m.x.not==b(:d)=>:g}, m.x.not==b(:t), m.x.not==b(:l), m.x.not==b(:r),
|
|
61
|
+
m.x.not==b(:a), m.x.not==b(:e), m.x.not==b(:n), m.x.not==b(:b), m.x.not==b(:o),
|
|
62
|
+
m.x(:d,:l,:a,:r,:n,:e,:o,:b,:t, &c5)] do |v|
|
|
63
|
+
puts "One Solution is:"
|
|
64
|
+
puts " G = #{v[:g]}"
|
|
65
|
+
puts " E = #{v[:e]}"
|
|
66
|
+
puts " R = #{v[:r]}"
|
|
67
|
+
puts " A = #{v[:a]}"
|
|
68
|
+
puts " L = #{v[:l]}"
|
|
69
|
+
puts " D = #{v[:d]}"
|
|
70
|
+
puts " O = #{v[:o]}"
|
|
71
|
+
puts " N = #{v[:n]}"
|
|
72
|
+
puts " B = #{v[:b]}"
|
|
73
|
+
puts " T = #{v[:t]}"
|
|
74
|
+
puts ""
|
|
75
|
+
puts " #{v[:g]} #{v[:e]} #{v[:r]} #{v[:a]} #{v[:l]} #{v[:d]}"
|
|
76
|
+
puts " + #{v[:d]} #{v[:o]} #{v[:n]} #{v[:a]} #{v[:l]} #{v[:d]}"
|
|
77
|
+
puts " ------"
|
|
78
|
+
puts " = #{v[:r]} #{v[:o]} #{v[:b]} #{v[:e]} #{v[:r]} #{v[:t]}"
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
e = engine :e do |e|
|
|
84
|
+
WordGameRulebook.new(e).rules
|
|
85
|
+
e.assert 0
|
|
86
|
+
e.assert 1
|
|
87
|
+
e.assert 2
|
|
88
|
+
e.assert 3
|
|
89
|
+
e.assert 4
|
|
90
|
+
e.assert 5
|
|
91
|
+
e.assert 6
|
|
92
|
+
e.assert 7
|
|
93
|
+
e.assert 8
|
|
94
|
+
e.assert 9
|
|
95
|
+
e.assert 'G'
|
|
96
|
+
e.assert 'E'
|
|
97
|
+
e.assert 'R'
|
|
98
|
+
e.assert 'A'
|
|
99
|
+
e.assert 'L'
|
|
100
|
+
e.assert 'D'
|
|
101
|
+
e.assert 'O'
|
|
102
|
+
e.assert 'N'
|
|
103
|
+
e.assert 'B'
|
|
104
|
+
e.assert 'T'
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
e.match
|
data/lib/core/engine.rb
CHANGED
|
@@ -73,11 +73,11 @@ module Ruleby
|
|
|
73
73
|
i = 0; while @match.recency[i] == a2.match.recency[i] && i < @match.recency.size-1 && i < a2.match.recency.size-1
|
|
74
74
|
i += 1
|
|
75
75
|
end
|
|
76
|
-
|
|
76
|
+
@match.recency[i] <=> a2.match.recency[i]
|
|
77
77
|
end
|
|
78
78
|
|
|
79
79
|
def ==(a2)
|
|
80
|
-
|
|
80
|
+
a2 != nil && @action == a2.action && @match == a2.match
|
|
81
81
|
end
|
|
82
82
|
|
|
83
83
|
def modify(match)
|
|
@@ -110,12 +110,10 @@ module Ruleby
|
|
|
110
110
|
# A fact is an object that is stored in working memory. The rules in the
|
|
111
111
|
# system will either look for the existence or absence of particular facts.
|
|
112
112
|
class Fact
|
|
113
|
-
attr :
|
|
114
|
-
attr :recency, true
|
|
113
|
+
attr :recency, true
|
|
115
114
|
attr_reader :object
|
|
116
115
|
|
|
117
|
-
def initialize(object
|
|
118
|
-
@token = token
|
|
116
|
+
def initialize(object)
|
|
119
117
|
@object = object
|
|
120
118
|
end
|
|
121
119
|
|
|
@@ -163,20 +161,19 @@ module Ruleby
|
|
|
163
161
|
end
|
|
164
162
|
|
|
165
163
|
def assert_fact(fact)
|
|
166
|
-
raise 'The fact asserted cannot be nil!'
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
end
|
|
164
|
+
raise 'The fact asserted cannot be nil!' if fact.object.nil?
|
|
165
|
+
fact.recency = @recency
|
|
166
|
+
@recency += 1
|
|
167
|
+
@facts.push fact
|
|
168
|
+
return fact
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def retract_fact(fact)
|
|
172
|
+
i = @facts.index(fact)
|
|
173
|
+
raise 'The fact to remove does not exist!' unless i
|
|
174
|
+
existing_fact = @facts[i]
|
|
175
|
+
@facts.delete_at(i)
|
|
176
|
+
return existing_fact
|
|
180
177
|
end
|
|
181
178
|
|
|
182
179
|
def print
|
|
@@ -281,17 +278,22 @@ module Ruleby
|
|
|
281
278
|
end
|
|
282
279
|
|
|
283
280
|
private
|
|
284
|
-
def fact_helper(object, sign=:plus
|
|
285
|
-
f = Core::Fact.new object
|
|
281
|
+
def fact_helper(object, sign=:plus)
|
|
282
|
+
f = Core::Fact.new object
|
|
286
283
|
yield f if block_given?
|
|
287
|
-
assert_fact f
|
|
284
|
+
sign==:plus ? assert_fact(f) : retract_fact(f)
|
|
288
285
|
f
|
|
289
286
|
end
|
|
290
287
|
|
|
291
288
|
def assert_fact(fact)
|
|
292
289
|
wm_fact = @working_memory.assert_fact fact
|
|
293
290
|
@root.assert_fact wm_fact if @root != nil
|
|
294
|
-
end
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
def retract_fact(fact)
|
|
294
|
+
wm_fact = @working_memory.retract_fact fact
|
|
295
|
+
@root.retract_fact wm_fact if @root != nil
|
|
296
|
+
end
|
|
295
297
|
end
|
|
296
298
|
end
|
|
297
299
|
end
|
data/lib/core/nodes.rb
CHANGED
|
@@ -34,14 +34,16 @@ module Ruleby
|
|
|
34
34
|
@terminal_nodes.push terminal_node
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
# When a new fact is added to working memory
|
|
38
|
-
#
|
|
39
|
-
# them accordingly.
|
|
37
|
+
# When a new fact is added to working memory this method is called. It finds any nodes that depend on it, and
|
|
38
|
+
# updates them accordingly.
|
|
40
39
|
def assert_fact(fact)
|
|
41
|
-
@type_node
|
|
42
|
-
@inherit_nodes.each
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
@type_node.assert(fact) if @type_node
|
|
41
|
+
@inherit_nodes.each {|node| node.assert(fact) }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def retract_fact(fact)
|
|
45
|
+
@type_node.retract(fact) if @type_node
|
|
46
|
+
@inherit_nodes.each {|node| node.retract(fact) }
|
|
45
47
|
end
|
|
46
48
|
|
|
47
49
|
# Increments the activation counter. This is just a pass-thru to the static
|
|
@@ -459,34 +461,67 @@ module Ruleby
|
|
|
459
461
|
end
|
|
460
462
|
end
|
|
461
463
|
end
|
|
464
|
+
|
|
465
|
+
class AlphaMemoryNode < AtomNode
|
|
466
|
+
def initialize(bucket, atom)
|
|
467
|
+
super(bucket, atom)
|
|
468
|
+
@memory = Hash.new(DEFAULT)
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
def retract(assertable)
|
|
472
|
+
forget(assertable)
|
|
473
|
+
super
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
def remember(assertable)
|
|
477
|
+
@memory[assertable.fact.object_id]
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
def memorized?(assertable)
|
|
481
|
+
@memory[assertable.fact.object_id] != DEFAULT
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
def memorize(assertable, value)
|
|
485
|
+
@memory[assertable.fact.object_id] = value
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
def forget(assertable)
|
|
489
|
+
@memory.delete(assertable.fact.object_id)
|
|
490
|
+
end
|
|
491
|
+
end
|
|
462
492
|
|
|
463
493
|
# This node class is used for matching properties of a fact.
|
|
464
|
-
class PropertyNode <
|
|
494
|
+
class PropertyNode < AlphaMemoryNode
|
|
465
495
|
def assert(assertable)
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
496
|
+
unless memorized?(assertable)
|
|
497
|
+
begin
|
|
498
|
+
v = assertable.fact.object.send(@atom.slot)
|
|
499
|
+
assertable.add_tag(@atom.tag, v)
|
|
500
|
+
begin
|
|
501
|
+
if @atom.proc.arity == 1
|
|
502
|
+
r = @atom.proc.call(v)
|
|
503
|
+
else
|
|
504
|
+
r = @atom.proc.call(v, @atom.value)
|
|
505
|
+
end
|
|
506
|
+
memorize(assertable, r)
|
|
507
|
+
super if r
|
|
508
|
+
rescue Exception => e
|
|
509
|
+
@bucket.add_error Error.new(:proc_call, :error, {
|
|
510
|
+
:object => assertable.fact.object.to_s,
|
|
511
|
+
:method => @atom.slot,
|
|
512
|
+
:value => v.to_s,
|
|
513
|
+
:message => e.message
|
|
514
|
+
})
|
|
515
|
+
end
|
|
516
|
+
rescue NoMethodError => e
|
|
517
|
+
@bucket.add_error Error.new(:no_method, :warn, {
|
|
518
|
+
:object => assertable.fact.object.to_s,
|
|
519
|
+
:method => e.name,
|
|
520
|
+
:message => e.message
|
|
521
|
+
})
|
|
482
522
|
end
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
:object => assertable.fact.object.to_s,
|
|
486
|
-
:method => @atom.slot,
|
|
487
|
-
:value => v.to_s,
|
|
488
|
-
:message => e.message
|
|
489
|
-
})
|
|
523
|
+
else
|
|
524
|
+
super if remember(assertable)
|
|
490
525
|
end
|
|
491
526
|
end
|
|
492
527
|
end
|
|
@@ -501,10 +536,13 @@ module Ruleby
|
|
|
501
536
|
end
|
|
502
537
|
|
|
503
538
|
# This node class is used conditions that are simply a function, which returns true or false.
|
|
504
|
-
class FunctionNode <
|
|
539
|
+
class FunctionNode < AlphaMemoryNode
|
|
505
540
|
def assert(assertable)
|
|
506
541
|
begin
|
|
507
|
-
|
|
542
|
+
unless memorized?(assertable)
|
|
543
|
+
memorize(assertable, @atom.proc.call(assertable.fact.object, *@atom.arguments))
|
|
544
|
+
end
|
|
545
|
+
super if remember(assertable)
|
|
508
546
|
rescue Exception => e
|
|
509
547
|
@bucket.add_error Error.new(:proc_call, :error, {
|
|
510
548
|
:object => fact.object.to_s,
|
|
@@ -546,7 +584,7 @@ module Ruleby
|
|
|
546
584
|
:message => e.message
|
|
547
585
|
})
|
|
548
586
|
end
|
|
549
|
-
|
|
587
|
+
MatchResult.new
|
|
550
588
|
end
|
|
551
589
|
end
|
|
552
590
|
|
|
@@ -611,7 +649,7 @@ module Ruleby
|
|
|
611
649
|
class CollectNode < BaseBridgeNode
|
|
612
650
|
def initialize(bucket, pattern)
|
|
613
651
|
super
|
|
614
|
-
@collection_memory = Fact.new([]
|
|
652
|
+
@collection_memory = Fact.new([])
|
|
615
653
|
@should_modify = false
|
|
616
654
|
# not really sure what to do about this. might just need to handle nil's
|
|
617
655
|
# using a puts a limit on how many facts can be asserted before this feature breaks
|
|
@@ -1001,5 +1039,9 @@ module Ruleby
|
|
|
1001
1039
|
end
|
|
1002
1040
|
end
|
|
1003
1041
|
|
|
1042
|
+
class MemoryDefault
|
|
1043
|
+
end
|
|
1044
|
+
DEFAULT = MemoryDefault.new
|
|
1045
|
+
|
|
1004
1046
|
end
|
|
1005
1047
|
end
|