philiprehberger-rule_engine 0.2.1 → 0.3.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +26 -0
- data/lib/philiprehberger/rule_engine/engine.rb +38 -0
- data/lib/philiprehberger/rule_engine/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b150f5bc67b381f5f5a1c88032c5241c4bc83f952c565ceaf5d66baa852d4f65
|
|
4
|
+
data.tar.gz: 0fc2ae7a0e84089dcfddee3b80d917201924522c249c320bc80dd6bbc317e9f2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 99ab211d1ba3beefd989c2c32444fcc40386d72fdaa90963cbb2ae845021db06ebe4b882843ff8eea76f9773879610f47f71fec26044065b165530ae8d387767
|
|
7
|
+
data.tar.gz: 0b3ab9ec81247acaab0f07b7c44add184aa0437cf650f66c760d99666f3797d3049d4730c788e9a280ef4352a7333546878a0105289fa64933d5b76787c83af1
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.3.0] - 2026-04-01
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `#dry_run(facts)` for evaluating rules without executing actions
|
|
14
|
+
- `#detect_conflicts` for finding potentially overlapping rule pairs
|
|
15
|
+
- `#validate_rules` for checking rule completeness
|
|
16
|
+
|
|
10
17
|
## [0.2.1] - 2026-03-31
|
|
11
18
|
|
|
12
19
|
### Changed
|
data/README.md
CHANGED
|
@@ -158,6 +158,29 @@ engine.stats
|
|
|
158
158
|
engine.reset_stats!
|
|
159
159
|
```
|
|
160
160
|
|
|
161
|
+
### Dry Run
|
|
162
|
+
|
|
163
|
+
Evaluate without executing actions:
|
|
164
|
+
|
|
165
|
+
```ruby
|
|
166
|
+
matched = engine.dry_run(facts)
|
|
167
|
+
# => [{ name: "discount", priority: 10 }]
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Conflict Detection
|
|
171
|
+
|
|
172
|
+
```ruby
|
|
173
|
+
engine.detect_conflicts
|
|
174
|
+
# => [{ rules: ["rule_a", "rule_b"], priorities: [10, 5] }]
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Rule Validation
|
|
178
|
+
|
|
179
|
+
```ruby
|
|
180
|
+
engine.validate_rules
|
|
181
|
+
# => { valid: false, issues: ["Rule 'x' has no action"] }
|
|
182
|
+
```
|
|
183
|
+
|
|
161
184
|
### Serialization
|
|
162
185
|
|
|
163
186
|
```ruby
|
|
@@ -198,6 +221,9 @@ end
|
|
|
198
221
|
| `#chain(*rule_names)` | Execute rules sequentially as a pipeline |
|
|
199
222
|
| `#stats` | Per-rule execution statistics |
|
|
200
223
|
| `#reset_stats!` | Clear all execution statistics |
|
|
224
|
+
| `#dry_run(facts)` | Evaluate rules without executing actions |
|
|
225
|
+
| `#detect_conflicts` | Find rule pairs that could both match |
|
|
226
|
+
| `#validate_rules` | Check all rules have conditions and actions |
|
|
201
227
|
|
|
202
228
|
### Rule DSL
|
|
203
229
|
|
|
@@ -135,6 +135,40 @@ module Philiprehberger
|
|
|
135
135
|
@stats.each_key { |k| @stats[k] = new_stat_entry }
|
|
136
136
|
end
|
|
137
137
|
|
|
138
|
+
# Evaluate rules without executing actions, return matched rule info
|
|
139
|
+
#
|
|
140
|
+
# @param facts [Hash] the facts to evaluate
|
|
141
|
+
# @return [Array<Hash>] matched rules with name and priority
|
|
142
|
+
def dry_run(facts)
|
|
143
|
+
matched = enabled_rules_sorted.select { |rule| rule.matches?(facts) }
|
|
144
|
+
matched = [matched.first].compact if @mode == :first
|
|
145
|
+
matched.map { |rule| { name: rule.name, priority: rule.priority } }
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Find rules with potentially overlapping conditions
|
|
149
|
+
#
|
|
150
|
+
# @return [Array<Hash>] pairs of rule names that could both match
|
|
151
|
+
def detect_conflicts
|
|
152
|
+
pairs = []
|
|
153
|
+
sorted = enabled_rules_sorted
|
|
154
|
+
sorted.combination(2) do |a, b|
|
|
155
|
+
pairs << { rules: [a.name, b.name], priorities: [a.priority, b.priority] }
|
|
156
|
+
end
|
|
157
|
+
pairs
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Validate all rules have conditions and actions defined
|
|
161
|
+
#
|
|
162
|
+
# @return [Hash] { valid: Boolean, issues: Array<String> }
|
|
163
|
+
def validate_rules
|
|
164
|
+
issues = []
|
|
165
|
+
@rules.each do |rule|
|
|
166
|
+
issues << "Rule '#{rule.name}' has no condition" unless rule.instance_variable_get(:@condition)
|
|
167
|
+
issues << "Rule '#{rule.name}' has no action" unless rule.instance_variable_get(:@action)
|
|
168
|
+
end
|
|
169
|
+
{ valid: issues.empty?, issues: issues }
|
|
170
|
+
end
|
|
171
|
+
|
|
138
172
|
# Serialize the engine configuration to a hash.
|
|
139
173
|
#
|
|
140
174
|
# @return [Hash] engine metadata including mode and rules
|
|
@@ -169,6 +203,10 @@ module Philiprehberger
|
|
|
169
203
|
|
|
170
204
|
private
|
|
171
205
|
|
|
206
|
+
def enabled_rules_sorted
|
|
207
|
+
@rules.select(&:enabled).sort_by(&:priority)
|
|
208
|
+
end
|
|
209
|
+
|
|
172
210
|
def new_stat_entry
|
|
173
211
|
{
|
|
174
212
|
evaluations: 0,
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: philiprehberger-rule_engine
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Philip Rehberger
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-04-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: A lightweight rule engine with a declarative DSL for defining conditions
|
|
14
14
|
and actions. Supports priority-based ordering, first-match and all-match evaluation
|
|
@@ -27,11 +27,11 @@ files:
|
|
|
27
27
|
- lib/philiprehberger/rule_engine/helpers.rb
|
|
28
28
|
- lib/philiprehberger/rule_engine/rule.rb
|
|
29
29
|
- lib/philiprehberger/rule_engine/version.rb
|
|
30
|
-
homepage: https://
|
|
30
|
+
homepage: https://philiprehberger.com/open-source-packages/ruby/philiprehberger-rule_engine
|
|
31
31
|
licenses:
|
|
32
32
|
- MIT
|
|
33
33
|
metadata:
|
|
34
|
-
homepage_uri: https://
|
|
34
|
+
homepage_uri: https://philiprehberger.com/open-source-packages/ruby/philiprehberger-rule_engine
|
|
35
35
|
source_code_uri: https://github.com/philiprehberger/rb-rule-engine
|
|
36
36
|
changelog_uri: https://github.com/philiprehberger/rb-rule-engine/blob/main/CHANGELOG.md
|
|
37
37
|
bug_tracker_uri: https://github.com/philiprehberger/rb-rule-engine/issues
|