Skip to content
This repository was archived by the owner on Feb 16, 2026. It is now read-only.

Commit 677d839

Browse files
committed
Merge branch 'develop'
2 parents 93ed320 + 21d11a3 commit 677d839

18 files changed

Lines changed: 709 additions & 181 deletions

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
/coverage
1+
Gemfile.lock
2+
coverage

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Changelog
2+
All notable changes to this gem will be documented in this file.
3+
4+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6+
7+
## [Unreleased]
8+
9+
## [1.6.0] - 2022-12-13
10+
### Added
11+
- Multiple outcomes: the ability to continue walking a tree, returning all outcomes instead of stopping at the first outcome

Gemfile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
source "https://rubygems.org"
2-
gem 'boolean_dsl', github: 'jobready/boolean_dsl', branch: 'develop'
32
gemspec
3+
4+
gem 'boolean_dsl', github: 'jobready/boolean_dsl', branch: 'develop'
5+
gem 'simplecov', group: :test, require: nil

Gemfile.lock

Lines changed: 0 additions & 91 deletions
This file was deleted.

README.md

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,20 +24,39 @@ Or install it yourself as:
2424

2525
## Usage
2626

27-
Require everything
27+
Single outcome:
2828

2929
```ruby
30-
require 'rules_engine'
31-
@name = options.fetch(:name)
32-
@reference = options.fetch(:reference)
33-
@parameter = options.fetch(:parameter)
34-
35-
true_outcome = RulesEngine::Outcome.new(reference:'some category', name:'true outcome',parameter:'some parameter')
36-
false_outcome = RulesEngine::Outcome.new(reference:'some category', 'name:false outcome', parameter:'another parameter')
37-
root_rule = RulesEngine::Condition.new(name:'condition name', condition:'2 > 1', when_true:true_outcome, when_false:false_outcome)
38-
set = RulesEngine::Set.new(root:root_rule, name:'rule set name')
39-
walker = RulesEngine::Walker.new(set:set, object:some_object)
30+
true_outcome = RulesEngine::Outcome.new(name: "Outcome 1", values: "Outcome 1 Values")
31+
false_outcome = RulesEngine::Outcome.new(name: "Outcome 2", values: "Outcome 2 Values")
32+
root = RulesEngine::Condition.new(name: "Root", condition: "2 > 1", when_true: true_outcome, when_false: false_outcome)
33+
set = RulesEngine::Set.new(name: "Single Outcome", root: root)
34+
walker = RulesEngine::Walker.new(set: set, event_logger: nil)
4035
walker.walk
36+
37+
# Executing Rule Set Single Outcome
38+
# Evaluated condition 2 > 1, result is true (overridden: false)
39+
# => #<RulesEngine::Outcome:... @name="Outcome 1", @values="Outcome 1 Values">
40+
```
41+
42+
Multiple outcomes:
43+
44+
```ruby
45+
outcome_1 = RulesEngine::Outcome.new(name: "Outcome 1", values: "Outcome 1 Values")
46+
outcome_2 = RulesEngine::Outcome.new(name: "Outcome 2", values: "Outcome 2 Values")
47+
outcome_3 = RulesEngine::Outcome.new(name: "Outcome 3", values: "Outcome 3 Values")
48+
outcome_4 = RulesEngine::Outcome.new(name: "Outcome 4", values: "Outcome 4 Values")
49+
condition_1 = RulesEngine::Condition.new(name: "Condition 1", condition: "2 < 1", when_true_outcome: outcome_3, when_false_outcome: outcome_4)
50+
root = RulesEngine::Condition.new(name: "Root", condition: "2 > 1", when_true_outcome: outcome_1, when_true_condition: condition_1, when_false_outcome: outcome_2, when_false_condition: condition_1)
51+
set = RulesEngine::Set.new(name: "Multiple Outcomes", root: root, multiple_outcomes: true)
52+
walker = RulesEngine::Walker.new(set: set, event_logger: nil)
53+
walker.walk
54+
55+
# Executing Rule Set Multiple Outcomes
56+
# Evaluated condition 2 > 1, result is true (overridden: false)
57+
# Evaluated condition 2 < 1, result is false (overridden: false)
58+
# => [#<RulesEngine::Outcome:... @name="Outcome 1", @values="Outcome 1 Values">,
59+
#<RulesEngine::Outcome:... @name="Outcome 4", @values="Outcome 4 Values">]
4160
```
4261

4362
## Contributing

lib/rules_engine/condition.rb

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
class RulesEngine::Condition
2-
attr_accessor :name, :condition, :when_true, :when_false, :original, :override
2+
attr_accessor :name,
3+
:condition,
4+
:when_true,
5+
:when_false,
6+
:original,
7+
:override,
8+
:when_true_outcome,
9+
:when_false_outcome,
10+
:when_true_condition,
11+
:when_false_condition
312

413
def initialize(options = {})
514
@name = options.fetch(:name)
@@ -8,6 +17,10 @@ def initialize(options = {})
817
@when_false = options.fetch(:when_false, nil)
918
@original = options.fetch(:original, nil)
1019
@override = options.fetch(:override, nil)
20+
@when_true_outcome = options.fetch(:when_true_outcome, nil)
21+
@when_false_outcome = options.fetch(:when_false_outcome, nil)
22+
@when_true_condition = options.fetch(:when_true_condition, nil)
23+
@when_false_condition = options.fetch(:when_false_condition, nil)
1124
end
1225

1326
def override?
@@ -25,5 +38,14 @@ def execute(context)
2538
def outcome(which)
2639
which ? when_true : when_false
2740
end
41+
42+
# For sets with multiple outcomes
43+
def outcomes(which)
44+
if which
45+
[when_true_outcome, when_true_condition]
46+
else
47+
[when_false_outcome, when_false_condition]
48+
end
49+
end
2850
end
2951

lib/rules_engine/outcome.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
class RulesEngine::Outcome
2-
attr_accessor :name, :values, :reference
2+
attr_accessor :name,
3+
:values,
4+
:reference,
5+
:original
36

47
def initialize(options = {})
58
@name = options.fetch(:name)
69
@values = options.fetch(:values)
710
@reference = options.fetch(:reference, nil)
11+
@original = options.fetch(:original, nil)
812
end
913
end

lib/rules_engine/set.rb

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
class RulesEngine::Set
2-
attr_reader :root, :name
2+
attr_reader :name,
3+
:root,
4+
:original,
5+
:multiple_outcomes
36

47
def initialize(options = {})
58
@root = options.fetch(:root)
69
@name = options.fetch(:name)
10+
@original = options.fetch(:original, nil)
11+
@multiple_outcomes = options.fetch(:multiple_outcomes, false)
12+
end
13+
14+
def multiple_outcomes?
15+
multiple_outcomes
716
end
817
end

lib/rules_engine/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module RulesEngine
2-
VERSION = '1.3.0'
2+
VERSION = '1.6.0'
33
end

lib/rules_engine/walker.rb

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,33 @@ def walk_set(set)
2020
logger.info("Executing Rule Set #{set.name}")
2121

2222
node = set.root
23+
outcome = nil
24+
condition = nil
25+
outcomes = []
26+
2327
loop do
24-
unless node.is_a?(RulesEngine::Condition)
25-
add_event(set, node, nil) if node
26-
return node
28+
if set.multiple_outcomes?
29+
if outcome
30+
add_event(set, outcome, nil)
31+
outcomes << outcome
32+
end
33+
return outcomes unless node
34+
else
35+
unless node.is_a?(RulesEngine::Condition)
36+
add_event(set, node, nil) if node
37+
return node
38+
end
2739
end
2840

2941
which = node.execute(context)
3042
logger.info("Evaluated condition #{node.condition}, result is #{which} (overridden: #{node.override?})")
3143
add_event(set, node, which)
32-
node = node.outcome(which)
44+
45+
if set.multiple_outcomes?
46+
outcome, node = node.outcomes(which)
47+
else
48+
node = node.outcome(which)
49+
end
3350
end
3451
end
3552

0 commit comments

Comments
 (0)