Class: Noteikumi::Rule
- Inherits:
-
Object
- Object
- Noteikumi::Rule
- Defined in:
- lib/noteikumi/rule.rb
Overview
A class that represents an individual rule used by the engine
Rules are generally stored in files named something_rule.rb in a rule directory, there are several samples of these in the examples dir and in docs on the wiki at GitHub
Instance Attribute Summary collapse
-
#concurrent ⇒ :safe, :unsafe
readonly
private
The concurrency safe level.
-
#conditions ⇒ Hash
readonly
private
Named conditions for this rule.
-
#file ⇒ String
private
The file the rule was found in.
-
#logger ⇒ Logger
private
The logger used by this rule.
-
#name ⇒ String, Symbol
private
The rule name.
-
#needs ⇒ Array
readonly
private
Items the rule expect on the state.
-
#priority ⇒ Fixnum
readonly
The priority for this fule.
-
#run_condition ⇒ Proc
readonly
private
The run conditions for this rule.
-
#run_count ⇒ Fixnum
readonly
private
How many times this rule have been run.
-
#run_logic ⇒ Proc
readonly
private
The logic to run.
-
#state ⇒ State?
readonly
private
The state this rule is being evaluated against.
Instance Method Summary collapse
-
#assign_state(state) ⇒ void
Assign the provided state to the rule.
-
#concurrency=(level) ⇒ :safe, :unsafe
Sets the concurrency safe level.
-
#concurrent_safe? ⇒ Boolean
Determines if the concurrency level is :safe.
-
#condition(name, &blk) ⇒ void
Creates a named condition.
-
#has_condition?(condition) ⇒ Boolean
Checks if a condition matching the name has been created on the rule.
-
#initialize(name) ⇒ Rule
constructor
Creates a new rule.
-
#new_result ⇒ Result
Construct a result object for this rule.
-
#process(state) ⇒ Result?
Process a rule after first checking all the requirements are met.
-
#requirement(*args) ⇒ void
Sets a requirement that the state should meet.
-
#reset_counter ⇒ void
Resets the run count for the rule to 0.
-
#reset_state ⇒ void
Resets the state to nil state.
-
#rule_priority(priority) ⇒ Fixnum
Sets the rule priority.
-
#run(&blk) ⇒ void
Creates the logic that will be run when all the conditions are met.
-
#run_rule_logic ⇒ Result
Runs the rule logic.
-
#run_when(&blk) ⇒ void
Logic to execute once state has met to determine if the rule should be run.
-
#satisfies_run_condition? ⇒ Boolean
Determines if the run_when block is satisfied.
-
#state_meets_requirements? ⇒ Boolean
Checks every requirement against the state.
-
#to_s ⇒ String
:nodoc:.
-
#with_state(state) ⇒ Object
Assigns the state, yields to the block and resets it.
Constructor Details
#initialize(name) ⇒ Rule
Creates a new rule
71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/noteikumi/rule.rb', line 71 def initialize(name) @name = name @priority = 500 @concurrent = :unsafe @needs = [] @conditions = {} @state = nil @file = "unknown file" @run_count = 0 run_when { true } run { raise("No execution logic provided for rule") } end |
Instance Attribute Details
#concurrent ⇒ :safe, :unsafe (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
The concurrency safe level
33 34 35 |
# File 'lib/noteikumi/rule.rb', line 33 def concurrent @concurrent end |
#conditions ⇒ Hash (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Named conditions for this rule
21 22 23 |
# File 'lib/noteikumi/rule.rb', line 21 def conditions @conditions end |
#file ⇒ String
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
The file the rule was found in
55 56 57 |
# File 'lib/noteikumi/rule.rb', line 55 def file @file end |
#logger ⇒ Logger
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
The logger used by this rule
65 66 67 |
# File 'lib/noteikumi/rule.rb', line 65 def logger @logger end |
#name ⇒ String, Symbol
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
The rule name
60 61 62 |
# File 'lib/noteikumi/rule.rb', line 60 def name @name end |
#needs ⇒ Array (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Items the rule expect on the state
27 28 29 |
# File 'lib/noteikumi/rule.rb', line 27 def needs @needs end |
#priority ⇒ Fixnum (readonly)
The priority for this fule
10 11 12 |
# File 'lib/noteikumi/rule.rb', line 10 def priority @priority end |
#run_condition ⇒ Proc (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
The run conditions for this rule
39 40 41 |
# File 'lib/noteikumi/rule.rb', line 39 def run_condition @run_condition end |
#run_count ⇒ Fixnum (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
How many times this rule have been run
50 51 52 |
# File 'lib/noteikumi/rule.rb', line 50 def run_count @run_count end |
#run_logic ⇒ Proc (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
The logic to run
45 46 47 |
# File 'lib/noteikumi/rule.rb', line 45 def run_logic @run_logic end |
#state ⇒ State? (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
The state this rule is being evaluated against
15 16 17 |
# File 'lib/noteikumi/rule.rb', line 15 def state @state end |
Instance Method Details
#assign_state(state) ⇒ void
This method returns an undefined value.
Assign the provided state to the rule
98 99 100 |
# File 'lib/noteikumi/rule.rb', line 98 def assign_state(state) @state = state end |
#concurrency=(level) ⇒ :safe, :unsafe
Sets the concurrency safe level
This is mainly not used now but will result in the state becoming immutable when the level is :safe. This is with an eye on supporting parallel or threaded execution of rules in the long term
250 251 252 253 254 |
# File 'lib/noteikumi/rule.rb', line 250 def concurrency=(level) raise("Concurrency has to be one of :safe, :unsafe") unless [:safe, :unsafe].include?(level) @concurrent = level end |
#concurrent_safe? ⇒ Boolean
Determines if the concurrency level is :safe
259 260 261 |
# File 'lib/noteikumi/rule.rb', line 259 def concurrent_safe? @concurrent == :safe end |
#condition(name, &blk) ⇒ void
these blocks must always return boolean and will be coerced to that
This method returns an undefined value.
Creates a named condition
276 277 278 279 280 281 282 283 |
# File 'lib/noteikumi/rule.rb', line 276 def condition(name, &blk) raise("Duplicate condition name %s" % name) if @conditions[name] raise("A block is required for condition %s" % name) unless block_given? @conditions[name] = blk nil end |
#has_condition?(condition) ⇒ Boolean
Checks if a condition matching the name has been created on the rule
90 91 92 |
# File 'lib/noteikumi/rule.rb', line 90 def has_condition?(condition) @conditions.include?(condition) end |
#new_result ⇒ Result
Construct a result object for this rule
155 156 157 |
# File 'lib/noteikumi/rule.rb', line 155 def new_result Result.new(self) end |
#process(state) ⇒ Result?
Process a rule after first checking all the requirements are met
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
# File 'lib/noteikumi/rule.rb', line 163 def process(state) result = nil with_state(state) do if state_meets_requirements? if satisfies_run_condition? logger.debug("Processing rule %s" % self) result = run_rule_logic else logger.debug("Skipping processing rule due to run_when block returning false on %s" % self) end else logger.debug("Skipping processing rule %s due to state check failing" % self) end end result end |
#requirement(*args) ⇒ void
This method returns an undefined value.
Sets a requirement that the state should meet
297 298 299 300 301 302 303 304 305 306 |
# File 'lib/noteikumi/rule.rb', line 297 def requirement(*args) case args.size when 1 @needs << [nil, args[0]] when 2 @needs << args else raise("Unsupported requirement input %s" % args.inspect) end end |
#reset_counter ⇒ void
This method returns an undefined value.
Resets the run count for the rule to 0
112 113 114 |
# File 'lib/noteikumi/rule.rb', line 112 def reset_counter @run_count = 0 end |
#reset_state ⇒ void
This method returns an undefined value.
Resets the state to nil state
105 106 107 |
# File 'lib/noteikumi/rule.rb', line 105 def reset_state @state = nil end |
#rule_priority(priority) ⇒ Fixnum
Sets the rule priority
238 239 240 |
# File 'lib/noteikumi/rule.rb', line 238 def rule_priority(priority) @priority = Integer(priority) end |
#run(&blk) ⇒ void
This method returns an undefined value.
Creates the logic that will be run when all the conditions are met
229 230 231 232 |
# File 'lib/noteikumi/rule.rb', line 229 def run(&blk) raise("A block is needed to run") unless block_given? @run_logic = blk end |
#run_rule_logic ⇒ Result
Runs the rule logic
Rules are run within an instance of Noteikumi::RuleExecutionScope
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
# File 'lib/noteikumi/rule.rb', line 133 def run_rule_logic @run_count += 1 result = new_result begin result.start_processing result.output = RuleExecutionScope.new(self).run rescue => e logger.error("Error during processing of rule: %s: %s: %s" % [self, e.class, e.to_s]) logger.debug(e.backtrace.join("\n\t")) result.exception = e ensure result.stop_processing end result end |
#run_when(&blk) ⇒ void
This method returns an undefined value.
Logic to execute once state has met to determine if the rule should be run
217 218 219 220 |
# File 'lib/noteikumi/rule.rb', line 217 def run_when(&blk) raise("A block is needed to evaluate for run_when") unless block_given? @run_condition = blk end |
#satisfies_run_condition? ⇒ Boolean
Determines if the run_when block is satisfied
185 186 187 188 |
# File 'lib/noteikumi/rule.rb', line 185 def satisfies_run_condition? validator = RuleConditionValidator.new(self) validator.__should_run? end |
#state_meets_requirements? ⇒ Boolean
Checks every requirement against the state
193 194 195 196 197 198 199 200 201 202 203 204 |
# File 'lib/noteikumi/rule.rb', line 193 def state_meets_requirements? @needs.each do |requirement| valid, reason = state.meets_requirement?(requirement) unless valid logger.debug("State does not meet the requirements %s: %s" % [self, reason]) return false end end true end |
#to_s ⇒ String
:nodoc:
208 209 210 |
# File 'lib/noteikumi/rule.rb', line 208 def to_s "#<%s:%s run_count: %d priority: %d name: %s @ %s>" % [self.class, object_id, run_count, priority, name, file] end |
#with_state(state) ⇒ Object
Assigns the state, yields to the block and resets it
120 121 122 123 124 125 126 |
# File 'lib/noteikumi/rule.rb', line 120 def with_state(state) assign_state(state) yield ensure reset_state end |