Class: RuleEngine::Base

Inherits:
Object
  • Object
show all
Defined in:
lib/cirrocumulus/rules/engine.rb

Overview

Core class for Cirrocumulus Rule Engine. All the magic about asserting/retracting facts and pattern-matching is performed here.

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeBase

Returns a new instance of Base.



56
57
58
59
60
61
# File 'lib/cirrocumulus/rules/engine.rb', line 56

def initialize
  @queue = RunQueue.new(self)
  @facts = []
  @fact_times = {}
  @mutex = Mutex.new
end

Class Method Details

.rule(name, facts, options = {}, &block) ⇒ Object

DSL-method for defining rules.



52
53
54
# File 'lib/cirrocumulus/rules/engine.rb', line 52

def self.rule(name, facts, options = {}, &block)
  current_ruleset << RuleDescription.new(name, facts, options, block)
end

Instance Method Details

#assert(fact, options = {}, silent = false) ⇒ Object

Asserts new fact. If ‘silent’ is set to true, does not perform any associated rules

  • Returns :

    • nothing



90
91
92
93
94
95
96
97
# File 'lib/cirrocumulus/rules/engine.rb', line 90

def assert(fact, options = {}, silent = false)
  silent = options unless options.is_a?(Hash)
  options = {} unless options.is_a?(Hash)

  @mutex.synchronize do
    assert_nonblocking(fact, options, silent)
  end
end

#dump_kbObject



63
64
65
66
67
68
69
70
71
# File 'lib/cirrocumulus/rules/engine.rb', line 63

def dump_kb()
  log "Dumping current knowledge base:\n"

  @facts.each_with_index do |fact,i|
    log "%d) %s (at %s)" % [i, fact.inspect, @fact_times[fact]]
  end

  log "Empty" if @facts.empty?
end

#dump_rulesetObject



73
74
75
76
77
78
79
80
# File 'lib/cirrocumulus/rules/engine.rb', line 73

def dump_ruleset()
  log "Dumping current ruleset:\n"
  self.class.current_ruleset.each_with_index do |rule,i|
    log "%d) %s" % [i, rule[:name]]
  end

  log "Empty ruleset" if self.class.current_ruleset.empty?
end

#executeObject

Executes all associated with current KB rules. Normally, this shouldn’t be called by the programmer.



195
196
197
# File 'lib/cirrocumulus/rules/engine.rb', line 195

def execute()
  process()
end

#match(pattern) ⇒ Object



159
160
161
162
163
164
165
166
# File 'lib/cirrocumulus/rules/engine.rb', line 159

def match(pattern)
  res = []
  find_matches_for_condition(pattern).each do |fact|
    res << bind_parameters(pattern, fact.data, {})
  end

  res
end

#query(fact) ⇒ Object



155
156
157
# File 'lib/cirrocumulus/rules/engine.rb', line 155

def query(fact)
  find_matches_for_condition(fact).map {|data| data.data}
end

#replace(pattern, values, options = {}) ⇒ Object

Replaces fact value. If fact was not found - asserts it

  • Returns :

    • nothing



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/cirrocumulus/rules/engine.rb', line 113

def replace(pattern, values, options = {})
  @mutex.synchronize do
    data = match(pattern)

    if data.empty?
      new_fact = pattern.clone

      pattern.each_with_index do |item,i|
        if item.is_a?(Symbol) && item.to_s.upcase == item.to_s
          new_fact[i] = values.is_a?(Hash) ? values[item] : values
        end
      end

      assert_nonblocking(new_fact, options, false)
    else
      data.each do |match_data|
        old_fact = pattern.clone
        new_fact = pattern.clone
        pattern.each_with_index do |item,i|
          if match_data.include? item
            old_fact[i] = match_data[item]
            new_fact[i] = values.is_a?(Hash) ? values[item] : values
          end
        end

        facts_are_same = true
        old_fact.each_with_index do |item, idx|
          new_item = new_fact[idx]
          facts_are_same = false if new_item != item
        end

        unless facts_are_same
          debug "replace #{pattern.inspect} for #{values.inspect}"

          retract_nonblocking(old_fact, true)
          assert_nonblocking(new_fact, {}, false)
        end
      end
    end
  end
end

#retract(fact, silent = false) ⇒ Object

Retracts an existing fact. If ‘silent’ is set to true, does not perform any associated rules

  • Returns :

    • nothing



103
104
105
106
107
# File 'lib/cirrocumulus/rules/engine.rb', line 103

def retract(fact, silent = false)
  @mutex.synchronize do
    retract_nonblocking(fact, silent)
  end
end

#startObject

Starts this rule engine instance.



169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/cirrocumulus/rules/engine.rb', line 169

def start()
  @worker_thread = Thread.new do
    engine = nil
    while true do
      engine = Thread.current[:engine] if engine.nil?
      engine.tick() if engine
      @queue.run_queued_rules()
      sleep 0.1
    end
  end

  @worker_thread[:engine] = self
end

#tickObject



183
184
185
186
187
188
189
190
191
192
# File 'lib/cirrocumulus/rules/engine.rb', line 183

def tick()
  @mutex.synchronize do
    to_retract = []
    @facts.each {|fact| to_retract << fact if fact.timed_out? }
    to_retract.each {|fact| retract_nonblocking(fact.data, true) }
    process() unless to_retract.empty?
  end
rescue Exception => ex
  p ex
end

#transactionObject



82
83
84
# File 'lib/cirrocumulus/rules/engine.rb', line 82

def transaction

end