lex-conditioner 0.2.5 → 0.3.1
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/.github/workflows/ci.yml +16 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +36 -20
- data/CHANGELOG.md +27 -0
- data/CLAUDE.md +135 -0
- data/Dockerfile +1 -1
- data/Gemfile +11 -0
- data/README.md +103 -16
- data/Rakefile +2 -0
- data/docker_deploy.rb +1 -0
- data/lex-conditioner.gemspec +19 -18
- data/lib/legion/extensions/conditioner/actors/conditioner.rb +17 -11
- data/lib/legion/extensions/conditioner/client.rb +20 -0
- data/lib/legion/extensions/conditioner/helpers/comparator.rb +68 -7
- data/lib/legion/extensions/conditioner/helpers/condition.rb +59 -24
- data/lib/legion/extensions/conditioner/runners/conditioner.rb +55 -49
- data/lib/legion/extensions/conditioner/transport/exchanges/task.rb +10 -4
- data/lib/legion/extensions/conditioner/transport/messages/conditioner.rb +15 -9
- data/lib/legion/extensions/conditioner/transport/queues/conditioner.rb +12 -6
- data/lib/legion/extensions/conditioner/transport.rb +15 -8
- data/lib/legion/extensions/conditioner/version.rb +3 -1
- data/lib/legion/extensions/conditioner.rb +3 -0
- metadata +55 -40
- data/Gemfile.lock +0 -119
- data/bitbucket-pipelines.yml +0 -19
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a8f0f6e6d047004004fc22bbbc5e59600a19a49c0456cd469e8abfbf2182d12a
|
|
4
|
+
data.tar.gz: 9511f9d436244571f12bc15b587c028c2692c0fff54ed81ea5caaf1a59948676
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5a86d76e674865fa293c3dff60e8a7a7f22f6dbf632d1a336c9f2fdbc88b47a68f727408a1e358d43571b904ac53612c226c6819caaa6d1354d8b9909a61ff4b
|
|
7
|
+
data.tar.gz: bbb2646ca44322974146f11bef9c9012d5363838645f29f8d12d77b7b685d725b91891d97644baeccbdcbbb30f445107dfc46098cad35d2dadb90e052901c1bb
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches: [main]
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
ci:
|
|
9
|
+
uses: LegionIO/.github/.github/workflows/ci.yml@main
|
|
10
|
+
|
|
11
|
+
release:
|
|
12
|
+
needs: ci
|
|
13
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
14
|
+
uses: LegionIO/.github/.github/workflows/release.yml@main
|
|
15
|
+
secrets:
|
|
16
|
+
rubygems-api-key: ${{ secrets.RUBYGEMS_API_KEY }}
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
|
@@ -1,33 +1,49 @@
|
|
|
1
|
+
AllCops:
|
|
2
|
+
TargetRubyVersion: 3.4
|
|
3
|
+
NewCops: enable
|
|
4
|
+
SuggestExtensions: false
|
|
5
|
+
|
|
1
6
|
Layout/LineLength:
|
|
2
|
-
Max:
|
|
7
|
+
Max: 160
|
|
8
|
+
Layout/SpaceAroundEqualsInParameterDefault:
|
|
9
|
+
EnforcedStyle: space
|
|
10
|
+
Layout/HashAlignment:
|
|
11
|
+
EnforcedHashRocketStyle: table
|
|
12
|
+
EnforcedColonStyle: table
|
|
13
|
+
|
|
3
14
|
Metrics/MethodLength:
|
|
4
|
-
Max:
|
|
15
|
+
Max: 50
|
|
5
16
|
Metrics/ClassLength:
|
|
6
17
|
Max: 1500
|
|
18
|
+
Metrics/ModuleLength:
|
|
19
|
+
Max: 1500
|
|
7
20
|
Metrics/BlockLength:
|
|
8
|
-
Max:
|
|
21
|
+
Max: 40
|
|
22
|
+
Exclude:
|
|
23
|
+
- 'spec/**/*'
|
|
9
24
|
Metrics/AbcSize:
|
|
10
|
-
Max:
|
|
25
|
+
Max: 60
|
|
11
26
|
Metrics/CyclomaticComplexity:
|
|
12
|
-
Max:
|
|
27
|
+
Max: 15
|
|
13
28
|
Metrics/PerceivedComplexity:
|
|
14
|
-
Max:
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
Enabled: true
|
|
19
|
-
Layout/HashAlignment:
|
|
20
|
-
EnforcedHashRocketStyle: table
|
|
21
|
-
EnforcedColonStyle: table
|
|
29
|
+
Max: 17
|
|
30
|
+
Metrics/ParameterLists:
|
|
31
|
+
Enabled: false
|
|
32
|
+
|
|
22
33
|
Style/Documentation:
|
|
23
34
|
Enabled: false
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
NewCops: enable
|
|
27
|
-
SuggestExtensions: false
|
|
35
|
+
Style/SymbolArray:
|
|
36
|
+
Enabled: true
|
|
28
37
|
Style/FrozenStringLiteralComment:
|
|
29
|
-
Enabled:
|
|
38
|
+
Enabled: true
|
|
39
|
+
EnforcedStyle: always
|
|
40
|
+
|
|
30
41
|
Naming/FileName:
|
|
31
42
|
Enabled: false
|
|
32
|
-
|
|
33
|
-
Enabled: false
|
|
43
|
+
Naming/PredicateMethod:
|
|
44
|
+
Enabled: false
|
|
45
|
+
Naming/PredicatePrefix:
|
|
46
|
+
Enabled: false
|
|
47
|
+
|
|
48
|
+
Gemspec/DevelopmentDependencies:
|
|
49
|
+
Enabled: false
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.3.1] - 2026-03-22
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
- Migrated to sub-gem helpers (Tier 1): added runtime dependencies on legion-cache >= 1.3.11, legion-crypt >= 1.4.9, legion-data >= 1.4.17, legion-json >= 1.2.1, legion-logging >= 1.3.2, legion-settings >= 1.3.14, legion-transport >= 1.3.9
|
|
7
|
+
- Replaced direct `Legion::Logging` calls in runner with `log` helper method via `Helpers::Lex` (includes `Legion::Logging::Helper`)
|
|
8
|
+
- Updated spec_helper to use real sub-gem helpers with `Helpers::Lex` module and actor stubs; removed inline Legion::Logging and Legion::JSON stubs
|
|
9
|
+
- Updated runner spec to remove framework stubs covered by spec_helper; added scoped `Helpers::Task`, `Transport::Messages::SubTask`, and `Exception::MissingArgument` stubs
|
|
10
|
+
|
|
11
|
+
## [0.3.0] - 2026-03-17
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
- 14 new operators: `greater_than`, `less_than`, `greater_or_equal`, `less_or_equal`, `between` (numeric); `contains`, `starts_with`, `ends_with`, `matches` (string); `in_set`, `not_in_set`, `empty`, `not_empty`, `size_equal` (collection) — 23 total
|
|
15
|
+
- Structured condition explanation via `Condition#explain` returning per-rule results with `fact`, `operator`, `value`, `actual`, and `result`
|
|
16
|
+
- Standalone `Client` class for framework-independent evaluation
|
|
17
|
+
- SimpleCov coverage reporting
|
|
18
|
+
- Modern packaging: grouped test dependencies, `require_relative` in gemspec, `rubocop-rspec`
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
- `Comparator.false?` returning `nil` instead of `false` for truthy values
|
|
22
|
+
- Typo `funciton_id` corrected to `function_id` in runner `send_task` whitelist
|
|
23
|
+
|
|
24
|
+
## [0.2.5] - 2026-03-13
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
- Initial release
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# lex-conditioner: Conditional Rule Engine for LegionIO
|
|
2
|
+
|
|
3
|
+
**Repository Level 3 Documentation**
|
|
4
|
+
- **Parent**: `/Users/miverso2/rubymine/legion/extensions-core/CLAUDE.md`
|
|
5
|
+
- **Grandparent**: `/Users/miverso2/rubymine/legion/CLAUDE.md`
|
|
6
|
+
|
|
7
|
+
## Purpose
|
|
8
|
+
|
|
9
|
+
Legion Extension that applies conditional statements to tasks within relationships. Evaluates JSON-based rules against task payloads to determine whether downstream tasks should execute, enabling branching logic in task chains.
|
|
10
|
+
|
|
11
|
+
**GitHub**: https://github.com/LegionIO/lex-conditioner
|
|
12
|
+
**License**: MIT
|
|
13
|
+
**Version**: 0.3.0
|
|
14
|
+
|
|
15
|
+
## Architecture
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
Legion::Extensions::Conditioner
|
|
19
|
+
├── Actors/
|
|
20
|
+
│ └── Conditioner # Subscription actor consuming condition evaluation requests
|
|
21
|
+
├── Runners/
|
|
22
|
+
│ └── Conditioner # Executes conditional logic against task payloads
|
|
23
|
+
│ ├── check # Main entry point: evaluates conditions, dispatches or fails
|
|
24
|
+
│ └── send_task # Routes to transformation or task queue based on result
|
|
25
|
+
├── Helpers/
|
|
26
|
+
│ ├── Condition # Condition parsing (all/any nesting, dotted-path facts)
|
|
27
|
+
│ └── Comparator # Comparison operator implementations (class methods)
|
|
28
|
+
└── Transport/
|
|
29
|
+
├── Exchanges/Task # Publishes to the task exchange
|
|
30
|
+
├── Queues/Conditioner # Subscribes to condition evaluation queue
|
|
31
|
+
└── Messages/Conditioner # Message format for condition requests
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Key Files
|
|
35
|
+
|
|
36
|
+
| Path | Purpose |
|
|
37
|
+
|------|---------|
|
|
38
|
+
| `lib/legion/extensions/conditioner.rb` | Entry point, extension registration |
|
|
39
|
+
| `lib/legion/extensions/conditioner/runners/conditioner.rb` | Core condition evaluation logic |
|
|
40
|
+
| `lib/legion/extensions/conditioner/helpers/condition.rb` | Condition parsing and all/any evaluation |
|
|
41
|
+
| `lib/legion/extensions/conditioner/helpers/comparator.rb` | Comparison operator implementations |
|
|
42
|
+
| `lib/legion/extensions/conditioner/actors/conditioner.rb` | AMQP subscription actor |
|
|
43
|
+
|
|
44
|
+
## Condition Rule Format
|
|
45
|
+
|
|
46
|
+
Rules use `all`/`any` grouping with `fact`/`operator`/`value` entries. Facts use dot notation to address nested payload keys. Groups can be nested arbitrarily.
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"all": [
|
|
51
|
+
{ "fact": "response.code", "operator": "greater_or_equal", "value": 200 },
|
|
52
|
+
{ "any": [
|
|
53
|
+
{ "fact": "action", "operator": "equal", "value": "opened" },
|
|
54
|
+
{ "fact": "action", "operator": "equal", "value": "reopened" }
|
|
55
|
+
]}
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Operators
|
|
61
|
+
|
|
62
|
+
### Binary (require `fact` + `value`)
|
|
63
|
+
|
|
64
|
+
| Operator | Description |
|
|
65
|
+
|----------|-------------|
|
|
66
|
+
| `equal` | Exact equality |
|
|
67
|
+
| `not_equal` | Inequality |
|
|
68
|
+
| `greater_than` | Numeric greater than |
|
|
69
|
+
| `less_than` | Numeric less than |
|
|
70
|
+
| `greater_or_equal` | Numeric greater than or equal |
|
|
71
|
+
| `less_or_equal` | Numeric less than or equal |
|
|
72
|
+
| `between` | Value within range (`value` is `[min, max]`) |
|
|
73
|
+
| `contains` | String contains substring |
|
|
74
|
+
| `starts_with` | String starts with prefix |
|
|
75
|
+
| `ends_with` | String ends with suffix |
|
|
76
|
+
| `matches` | String matches regex pattern |
|
|
77
|
+
| `in_set` | Value is in the given array |
|
|
78
|
+
| `not_in_set` | Value is not in the given array |
|
|
79
|
+
| `size_equal` | Collection or string size equals value |
|
|
80
|
+
|
|
81
|
+
### Unary (require `fact` only)
|
|
82
|
+
|
|
83
|
+
| Operator | Description |
|
|
84
|
+
|----------|-------------|
|
|
85
|
+
| `nil` | Value is nil |
|
|
86
|
+
| `not_nil` | Value is not nil |
|
|
87
|
+
| `is_true` | Value is truthy |
|
|
88
|
+
| `is_false` | Value is falsy |
|
|
89
|
+
| `is_string` | Value is a String |
|
|
90
|
+
| `is_array` | Value is an Array |
|
|
91
|
+
| `is_integer` | Value is an Integer |
|
|
92
|
+
| `empty` | Value is nil or empty |
|
|
93
|
+
| `not_empty` | Value is present and not empty |
|
|
94
|
+
|
|
95
|
+
## Condition Evaluation Logic
|
|
96
|
+
|
|
97
|
+
After evaluation, the runner routes based on outcome:
|
|
98
|
+
- Condition passes + transformation present: `transformation.queued` (routes to `task.subtask.transform`)
|
|
99
|
+
- Condition passes + routing key present: `task.queued` (routes to `runner_routing_key`)
|
|
100
|
+
- Condition passes but no routing info: `task.exception` (`send_task` raises `MissingArgument`)
|
|
101
|
+
- Condition fails: `conditioner.failed` (`send_task` is skipped)
|
|
102
|
+
|
|
103
|
+
Each evaluation also returns an explanation chain with per-rule results including actual values, for use by lex-synapse and standalone callers.
|
|
104
|
+
|
|
105
|
+
## Standalone Client
|
|
106
|
+
|
|
107
|
+
`Legion::Extensions::Conditioner::Client` includes the `Conditioner` runner. Instantiate without the full framework:
|
|
108
|
+
|
|
109
|
+
```ruby
|
|
110
|
+
require 'legion/extensions/conditioner/client'
|
|
111
|
+
client = Legion::Extensions::Conditioner::Client.new
|
|
112
|
+
result = client.evaluate(conditions: { all: [...] }, values: { ... })
|
|
113
|
+
result[:valid] # => true/false
|
|
114
|
+
result[:explanation] # => per-rule results with actual values
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Dependencies
|
|
118
|
+
|
|
119
|
+
| Gem | Purpose |
|
|
120
|
+
|-----|---------|
|
|
121
|
+
| `legion-exceptions` | Exception handling |
|
|
122
|
+
|
|
123
|
+
## Testing
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
bundle install
|
|
127
|
+
bundle exec rspec # 140 examples, 0 failures
|
|
128
|
+
bundle exec rubocop # 0 offenses
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Spec files: `spec/legion/extensions/conditioner_spec.rb`, `spec/legion/extensions/comparator_spec.rb`, `spec/legion/extensions/condition_spec.rb`, `spec/legion/extensions/conditioner/runners/conditioner_spec.rb`
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
**Maintained By**: Matthew Iverson (@Esity)
|
data/Dockerfile
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -1,38 +1,125 @@
|
|
|
1
|
-
#
|
|
1
|
+
# lex-conditioner
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Conditional rule engine for [LegionIO](https://github.com/LegionIO/LegionIO). Evaluates JSON-based rules against task payloads to determine whether downstream tasks should execute, enabling branching logic in task chains.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
```bash
|
|
8
|
+
gem install lex-conditioner
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or add to your Gemfile:
|
|
8
12
|
|
|
9
13
|
```ruby
|
|
10
14
|
gem 'lex-conditioner'
|
|
11
15
|
```
|
|
12
16
|
|
|
13
|
-
|
|
17
|
+
## Standalone Client
|
|
18
|
+
|
|
19
|
+
Use the conditioner without the full LegionIO framework:
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
require 'legion/extensions/conditioner/client'
|
|
23
|
+
|
|
24
|
+
client = Legion::Extensions::Conditioner::Client.new
|
|
25
|
+
|
|
26
|
+
result = client.evaluate(
|
|
27
|
+
conditions: { all: [
|
|
28
|
+
{ fact: 'response.code', operator: 'greater_or_equal', value: 200 },
|
|
29
|
+
{ fact: 'response.code', operator: 'less_than', value: 300 }
|
|
30
|
+
] },
|
|
31
|
+
values: { response: { code: 200, body: 'OK' } }
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
result[:valid] # => true
|
|
35
|
+
result[:explanation] # => { valid: true, group: :all, rules: [...] }
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The explanation includes per-rule results with actual values:
|
|
14
39
|
|
|
15
|
-
|
|
40
|
+
```ruby
|
|
41
|
+
result[:explanation][:rules]
|
|
42
|
+
# => [
|
|
43
|
+
# { fact: "response.code", operator: "greater_or_equal", value: 200, actual: 200, result: true },
|
|
44
|
+
# { fact: "response.code", operator: "less_than", value: 300, actual: 200, result: true }
|
|
45
|
+
# ]
|
|
46
|
+
```
|
|
16
47
|
|
|
17
|
-
|
|
48
|
+
## Condition Format
|
|
18
49
|
|
|
19
|
-
|
|
50
|
+
Rules use `all`/`any` grouping with `fact`/`operator`/`value` entries. Facts use dot notation for nested keys.
|
|
20
51
|
|
|
21
|
-
## Adding to Legion
|
|
22
|
-
You can manually install with a `gem install lex-http` command or by adding it into your settings with something like this
|
|
23
52
|
```json
|
|
24
53
|
{
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
54
|
+
"all": [
|
|
55
|
+
{ "fact": "response.code", "operator": "greater_or_equal", "value": 200 },
|
|
56
|
+
{ "any": [
|
|
57
|
+
{ "fact": "action", "operator": "equal", "value": "opened" },
|
|
58
|
+
{ "fact": "action", "operator": "equal", "value": "reopened" }
|
|
59
|
+
]}
|
|
60
|
+
]
|
|
30
61
|
}
|
|
31
62
|
```
|
|
32
63
|
|
|
33
|
-
##
|
|
64
|
+
## Operators
|
|
65
|
+
|
|
66
|
+
### Binary (require `fact` + `value`)
|
|
67
|
+
|
|
68
|
+
| Operator | Description |
|
|
69
|
+
|----------|-------------|
|
|
70
|
+
| `equal` | Exact equality |
|
|
71
|
+
| `not_equal` | Inequality |
|
|
72
|
+
| `greater_than` | Numeric greater than |
|
|
73
|
+
| `less_than` | Numeric less than |
|
|
74
|
+
| `greater_or_equal` | Numeric greater than or equal |
|
|
75
|
+
| `less_or_equal` | Numeric less than or equal |
|
|
76
|
+
| `between` | Value within range (value is `[min, max]`) |
|
|
77
|
+
| `contains` | String contains substring |
|
|
78
|
+
| `starts_with` | String starts with prefix |
|
|
79
|
+
| `ends_with` | String ends with suffix |
|
|
80
|
+
| `matches` | String matches regex pattern |
|
|
81
|
+
| `in_set` | Value is in the given array |
|
|
82
|
+
| `not_in_set` | Value is not in the given array |
|
|
83
|
+
| `size_equal` | Collection/string size equals value |
|
|
84
|
+
|
|
85
|
+
### Unary (require `fact` only)
|
|
86
|
+
|
|
87
|
+
| Operator | Description |
|
|
88
|
+
|----------|-------------|
|
|
89
|
+
| `nil` | Value is nil |
|
|
90
|
+
| `not_nil` | Value is not nil |
|
|
91
|
+
| `is_true` | Value is truthy |
|
|
92
|
+
| `is_false` | Value is falsy |
|
|
93
|
+
| `is_string` | Value is a String |
|
|
94
|
+
| `is_array` | Value is an Array |
|
|
95
|
+
| `is_integer` | Value is an Integer |
|
|
96
|
+
| `empty` | Value is nil or empty |
|
|
97
|
+
| `not_empty` | Value is present and not empty |
|
|
98
|
+
|
|
99
|
+
## Runners
|
|
100
|
+
|
|
101
|
+
### Conditioner
|
|
102
|
+
|
|
103
|
+
#### `check(conditions:, **payload)`
|
|
104
|
+
|
|
105
|
+
Evaluates conditions against the payload. Routes to transformation or task queue based on result:
|
|
106
|
+
|
|
107
|
+
- Condition passes + transformation present: routes to `task.subtask.transform`
|
|
108
|
+
- Condition passes + routing key present: routes to `runner_routing_key`
|
|
109
|
+
- Condition fails: `conditioner.failed`, no dispatch
|
|
110
|
+
|
|
111
|
+
## Transport
|
|
112
|
+
|
|
113
|
+
- **Exchange**: `task` (inherits from `Legion::Transport::Exchanges::Task`)
|
|
114
|
+
- **Queue**: `task.conditioner`
|
|
115
|
+
- **Routing keys**: `task.subtask`, `task.subtask.conditioner`
|
|
116
|
+
|
|
117
|
+
## Requirements
|
|
34
118
|
|
|
119
|
+
- Ruby >= 3.4
|
|
120
|
+
- [LegionIO](https://github.com/LegionIO/LegionIO) framework (for AMQP actor mode)
|
|
121
|
+
- Standalone Client works without the framework
|
|
35
122
|
|
|
36
123
|
## License
|
|
37
124
|
|
|
38
|
-
|
|
125
|
+
MIT
|
data/Rakefile
CHANGED
data/docker_deploy.rb
CHANGED
data/lex-conditioner.gemspec
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'lib/legion/extensions/conditioner/version'
|
|
4
4
|
|
|
5
5
|
Gem::Specification.new do |spec|
|
|
6
6
|
spec.name = 'lex-conditioner'
|
|
@@ -8,28 +8,29 @@ Gem::Specification.new do |spec|
|
|
|
8
8
|
spec.authors = ['Esity']
|
|
9
9
|
spec.email = ['matthewdiverson@gmail.com']
|
|
10
10
|
|
|
11
|
-
spec.summary = '
|
|
12
|
-
spec.description = '
|
|
13
|
-
spec.homepage = 'https://
|
|
11
|
+
spec.summary = 'Conditional rule engine for LegionIO task chains'
|
|
12
|
+
spec.description = 'Evaluates JSON-based rules against task payloads with 18+ operators, structured explanations, and standalone client support'
|
|
13
|
+
spec.homepage = 'https://github.com/LegionIO/lex-conditioner'
|
|
14
14
|
spec.license = 'MIT'
|
|
15
|
-
spec.required_ruby_version =
|
|
15
|
+
spec.required_ruby_version = '>= 3.4'
|
|
16
16
|
|
|
17
17
|
spec.metadata['homepage_uri'] = spec.homepage
|
|
18
|
-
spec.metadata['source_code_uri'] = 'https://
|
|
19
|
-
spec.metadata['documentation_uri'] = 'https://
|
|
20
|
-
spec.metadata['changelog_uri'] = 'https://
|
|
21
|
-
spec.metadata['bug_tracker_uri'] = 'https://
|
|
18
|
+
spec.metadata['source_code_uri'] = 'https://github.com/LegionIO/lex-conditioner'
|
|
19
|
+
spec.metadata['documentation_uri'] = 'https://github.com/LegionIO/lex-conditioner'
|
|
20
|
+
spec.metadata['changelog_uri'] = 'https://github.com/LegionIO/lex-conditioner/blob/main/CHANGELOG.md'
|
|
21
|
+
spec.metadata['bug_tracker_uri'] = 'https://github.com/LegionIO/lex-conditioner/issues'
|
|
22
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
|
22
23
|
|
|
23
24
|
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
24
25
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
25
26
|
end
|
|
26
27
|
spec.require_paths = ['lib']
|
|
27
28
|
|
|
28
|
-
spec.
|
|
29
|
-
spec.
|
|
30
|
-
spec.
|
|
31
|
-
spec.
|
|
32
|
-
spec.
|
|
33
|
-
|
|
34
|
-
spec.add_dependency 'legion-
|
|
29
|
+
spec.add_dependency 'legion-cache', '>= 1.3.11'
|
|
30
|
+
spec.add_dependency 'legion-crypt', '>= 1.4.9'
|
|
31
|
+
spec.add_dependency 'legion-data', '>= 1.4.17'
|
|
32
|
+
spec.add_dependency 'legion-json', '>= 1.2.1'
|
|
33
|
+
spec.add_dependency 'legion-logging', '>= 1.3.2'
|
|
34
|
+
spec.add_dependency 'legion-settings', '>= 1.3.14'
|
|
35
|
+
spec.add_dependency 'legion-transport', '>= 1.3.9'
|
|
35
36
|
end
|
|
@@ -1,16 +1,22 @@
|
|
|
1
|
-
|
|
2
|
-
module Actor
|
|
3
|
-
class Conditioner < Legion::Extensions::Actors::Subscription
|
|
4
|
-
def runner_function
|
|
5
|
-
'check'
|
|
6
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
7
2
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Conditioner
|
|
6
|
+
module Actor
|
|
7
|
+
class Conditioner < Legion::Extensions::Actors::Subscription
|
|
8
|
+
def runner_function
|
|
9
|
+
'check'
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def check_subtask?
|
|
13
|
+
false
|
|
14
|
+
end
|
|
11
15
|
|
|
12
|
-
|
|
13
|
-
|
|
16
|
+
def generate_task?
|
|
17
|
+
false
|
|
18
|
+
end
|
|
19
|
+
end
|
|
14
20
|
end
|
|
15
21
|
end
|
|
16
22
|
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'helpers/condition'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Extensions
|
|
7
|
+
module Conditioner
|
|
8
|
+
class Client
|
|
9
|
+
def evaluate(conditions:, values:)
|
|
10
|
+
conditions_json = conditions.is_a?(String) ? conditions : Legion::JSON.dump(conditions)
|
|
11
|
+
condition = Condition.new(conditions: conditions_json, values: values)
|
|
12
|
+
{
|
|
13
|
+
valid: condition.valid?,
|
|
14
|
+
explanation: condition.explain
|
|
15
|
+
}
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Legion
|
|
2
4
|
module Extensions
|
|
3
5
|
module Conditioner
|
|
@@ -18,25 +20,84 @@ module Legion
|
|
|
18
20
|
!values[fact].nil?
|
|
19
21
|
end
|
|
20
22
|
|
|
21
|
-
def self.
|
|
22
|
-
|
|
23
|
+
def self.false?(fact, values)
|
|
24
|
+
values[fact] ? false : true
|
|
23
25
|
end
|
|
24
26
|
|
|
25
|
-
def self.
|
|
27
|
+
def self.true?(fact, values)
|
|
26
28
|
values[fact]
|
|
27
29
|
end
|
|
28
30
|
|
|
29
|
-
def self.
|
|
30
|
-
|
|
31
|
+
def self.array?(fact, values)
|
|
32
|
+
values[fact].is_a? Array
|
|
31
33
|
end
|
|
32
34
|
|
|
33
|
-
def self.
|
|
35
|
+
def self.string?(fact, values)
|
|
34
36
|
values[fact].is_a? String
|
|
35
37
|
end
|
|
36
38
|
|
|
37
|
-
def self.
|
|
39
|
+
def self.integer?(fact, values)
|
|
38
40
|
values[fact].is_a? Integer
|
|
39
41
|
end
|
|
42
|
+
|
|
43
|
+
def self.greater_than?(fact, value, values)
|
|
44
|
+
values[fact] > value
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def self.less_than?(fact, value, values)
|
|
48
|
+
values[fact] < value
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def self.greater_or_equal?(fact, value, values)
|
|
52
|
+
values[fact] >= value
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def self.less_or_equal?(fact, value, values)
|
|
56
|
+
values[fact] <= value
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def self.between?(fact, value, values)
|
|
60
|
+
values[fact].between?(value[0], value[1])
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def self.contains?(fact, value, values)
|
|
64
|
+
values[fact].to_s.include?(value.to_s)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def self.starts_with?(fact, value, values)
|
|
68
|
+
values[fact].to_s.start_with?(value.to_s)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def self.ends_with?(fact, value, values)
|
|
72
|
+
values[fact].to_s.end_with?(value.to_s)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def self.matches?(fact, value, values)
|
|
76
|
+
Regexp.new(value).match?(values[fact].to_s)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def self.in_set?(fact, value, values)
|
|
80
|
+
Array(value).include?(values[fact])
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def self.not_in_set?(fact, value, values)
|
|
84
|
+
!Array(value).include?(values[fact])
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def self.empty?(fact, values)
|
|
88
|
+
val = values[fact]
|
|
89
|
+
val.nil? || (val.respond_to?(:empty?) && val.empty?)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def self.not_empty?(fact, values)
|
|
93
|
+
val = values[fact]
|
|
94
|
+
!val.nil? && !(val.respond_to?(:empty?) && val.empty?)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def self.size_equal?(fact, value, values)
|
|
98
|
+
val = values[fact]
|
|
99
|
+
val.respond_to?(:size) && val.size == value
|
|
100
|
+
end
|
|
40
101
|
end
|
|
41
102
|
end
|
|
42
103
|
end
|