Module: Ruler
- Included in:
- TeaDrinker
- Defined in:
- lib/ruler.rb
Instance Method Summary collapse
-
#default_rule(&blk) ⇒ Object
the default_rule is simple a rule that is always true.
-
#dynamic_fact(name, &blk) ⇒ Object
a dynamic_fact is evaulated every time the fact is checked.
-
#fact(name, dval = nil, &blk) ⇒ Object
a fact takes a symbol name and either a value or a block.
-
#multi_ruleset(&blk) ⇒ Object
multi_ruleset is a helper function to define rulesets that allow more than one rule to fire.
-
#notf(name) ⇒ Object
allows for a fact to be NOT another fact.
-
#rule(vlist, docstr = nil, &blk) ⇒ Object
a rule takes a list of fact names and a block.
-
#ruleset(singletary = true, &blk) ⇒ Object
puts result.
Instance Method Details
#default_rule(&blk) ⇒ Object
the default_rule is simple a rule that is always true. This is mostly syntactic-sugar to represent a rule that should fire if no others fire. You cannot have a default rule if you allow more than one match. The BadDefaultRule exception is raised.
160 161 162 163 164 165 166 167 |
# File 'lib/ruler.rb', line 160 def default_rule &blk raise BadDefaultRule.new("Can't have a default rule when multiple matches are allowed") unless Thread.current[@rulesetids.last][:singletary] if Thread.current[@rulesetids.last][:singletary] && !Thread.current[@rulesetids.last][:rulematched] yield else Thread.current[@rulesetids.last][:rulematched] end end |
#dynamic_fact(name, &blk) ⇒ Object
a dynamic_fact is evaulated every time the fact is checked. Unlike a normal fact, which is only evaluated once, dynamic facts are evaluated once for each rule they appear in
110 111 112 |
# File 'lib/ruler.rb', line 110 def dynamic_fact name, &blk Thread.current[@rulesetids.last][:working_memory][name] = {:transient => true, :block => blk } end |
#fact(name, dval = nil, &blk) ⇒ Object
a fact takes a symbol name and either a value or a block. for example:
fact :factname, true
fact :otherfactname do
some_computation_that_evaluates_to_true_or_false
end
Facts may be defined anywhere in the ruleset. due to the evaluation order, facts that appear after the last rule will never be used.
95 96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/ruler.rb', line 95 def fact name, dval = nil, &blk if dval.nil? _res = begin yield rescue raise BadFact.new($!) end Thread.current[@rulesetids.last][:working_memory][name] = {:value => _res } else Thread.current[@rulesetids.last][:working_memory][name] = {:value => dval } end end |
#multi_ruleset(&blk) ⇒ Object
multi_ruleset is a helper function to define rulesets that allow more than one rule to fire
81 82 83 |
# File 'lib/ruler.rb', line 81 def multi_ruleset &blk ruleset false,&blk end |
#notf(name) ⇒ Object
allows for a fact to be NOT another fact. notf cannot be used with a dynamic_fact for example:
fact :one, 10 == 10
fact :notfone, not(:one)
118 119 120 121 122 123 124 |
# File 'lib/ruler.rb', line 118 def notf name if Thread.current[@rulesetids.last][:working_memory][name][:transient] raise BadNotCall.new("Cannot call notf on dynamic fact") else not(Thread.current[@rulesetids.last][:working_memory][name][:value]) end end |
#rule(vlist, docstr = nil, &blk) ⇒ Object
a rule takes a list of fact names and a block. Rules are evaluated in the order they appear in the ruleset and are evaluated at execution time. If all of the facts are true, then that rule is fired. If the ruleset is singletary, only one rule (the first rule to be true) may fire.
rules may also have docstrings. These aren’t really used yet, but they will be and they provide a nice alternative to commenting. For example:
rule [:one_fact, :two_facts], "this is the docstring" do
some_method
end
there is no check to see if fact names are valid, and facts can be (re)defined inside of rules. Fact names are false if they are not defined.
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/ruler.rb', line 138 def rule vlist,docstr = nil,&blk conditional_call = lambda {|n| begin Thread.current[@rulesetids.last][:working_memory][n][:transient].nil? ? Thread.current[@rulesetids.last][:working_memory][n][:value] : Thread.current[@rulesetids.last][:working_memory][n][:block].call() rescue false end } dbg = lambda {|va| puts begin "|=-\t#{va} = #{conditional_call.call(va)}" rescue "|>=- ERROR: #{$!}" end } if @DEBUG puts "---------------------------------------" puts vlist.join(" & ") puts "=======================================" vlist.each {|v| dbg.call(v) } puts "---------------------------------------" end if Thread.current[@rulesetids.last][:singletary] && Thread.current[@rulesetids.last][:rulematched] Thread.current[@rulesetids.last][:rulematched] else Thread.current[@rulesetids.last][:rulematched] = if vlist.inject(true) {|k,v| raise UnknownFact.new("Fact: #{v} is unknown") if Thread.current[@rulesetids.last][:working_memory][v].nil? ;k ? k && conditional_call.call(v) : false } yield end end end |
#ruleset(singletary = true, &blk) ⇒ Object
puts result
65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/ruler.rb', line 65 def ruleset singletary = true,&blk @rulesetids ||= [] @rulesetids << UUID.generate puts "Ruleset: #{@rulesetids.last} of #{@rulesetids.length}" if @DEBUG Thread.current[@rulesetids.last] = {:singletary => singletary, :rulematched => nil, :working_memory => {}} _rval = nil begin _rval = yield ensure @rulesetids.pop end _rval end |