cmdx 1.7.1 → 1.7.2
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 +5 -0
- data/LLM.md +29 -6
- data/README.md +57 -20
- data/docs/getting_started.md +26 -0
- data/docs/logging.md +4 -6
- data/lib/cmdx/middlewares/correlate.rb +3 -4
- data/lib/cmdx/version.rb +1 -1
- data/lib/generators/cmdx/locale_generator.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 528f3b3b1d4f23045a295cc15c05106c3bd0f9efde6400bb88c01bb2a6455637
|
4
|
+
data.tar.gz: 473ca317fd214b48e4e4988ba4358bdcff4a3fc9f9805300dba292f4401e51c1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5b48be81fa184343f76c1891a3b87c25853e0dc859764dbf92946c7b31d69fd4af85b75eee7720470e29949edb395bce668343dbc6b17916079ba9befd1fbc80
|
7
|
+
data.tar.gz: b95fe96f12cf2aeb724782808d80b4ee36e083d0f6c60876edac45a6046c7925437f329dc2f98a42432b4794996c3269f71ba7f93452f161a38f365c6d40afa6
|
data/CHANGELOG.md
CHANGED
data/LLM.md
CHANGED
@@ -11,6 +11,31 @@ url: https://github.com/drexed/cmdx/blob/main/docs/getting_started.md
|
|
11
11
|
|
12
12
|
CMDx is a Ruby framework for building maintainable, observable business logic through composable command objects. Design robust workflows with automatic attribute validation, structured error handling, comprehensive logging, and intelligent execution flow control.
|
13
13
|
|
14
|
+
**Common Challenges:**
|
15
|
+
|
16
|
+
- Inconsistent patterns across implementations
|
17
|
+
- Minimal or no logging, making debugging painful
|
18
|
+
- Fragile designs that erode developer confidence
|
19
|
+
|
20
|
+
**CMDx Solutions:**
|
21
|
+
|
22
|
+
- Establishes a consistent, standardized design
|
23
|
+
- Provides flow control and error handling
|
24
|
+
- Supports composable, reusable workflows
|
25
|
+
- Includes detailed logging for observability
|
26
|
+
- Defines input attributes with fallback defaults
|
27
|
+
- Adds validations and type coercions
|
28
|
+
- Plus many other developer-friendly tools
|
29
|
+
|
30
|
+
## Compose, Execute, React, Observe pattern
|
31
|
+
|
32
|
+
CMDx encourages breaking business logic into composable tasks. Each task can be combined into larger workflows, executed with standardized flow control, and fully observed through logging, validations, and context.
|
33
|
+
|
34
|
+
- **Compose** → Define small, contract-driven tasks with typed attributes, validations, and natural workflow composition.
|
35
|
+
- **Execute** → Run tasks with clear outcomes, intentional halts, and pluggable behaviors via middlewares and callbacks.
|
36
|
+
- **React** → Adapt to outcomes by chaining follow-up tasks, handling faults, or shaping future flows.
|
37
|
+
- **Observe** → Capture immutable results, structured logs, and full execution chains for reliable tracing and insight.
|
38
|
+
|
14
39
|
## Installation
|
15
40
|
|
16
41
|
Add CMDx to your Gemfile:
|
@@ -2743,21 +2768,19 @@ Sample output:
|
|
2743
2768
|
```log
|
2744
2769
|
<!-- Success (INFO level) -->
|
2745
2770
|
I, [2022-07-17T18:43:15.000000 #3784] INFO -- GenerateInvoice:
|
2746
|
-
index=0 chain_id="018c2b95-b764-7615-a924-cc5b910ed1e5" type="Task"
|
2747
|
-
class="GenerateInvoice" state="complete" status="success" metadata={runtime: 187}
|
2771
|
+
index=0 chain_id="018c2b95-b764-7615-a924-cc5b910ed1e5" type="Task" class="GenerateInvoice" state="complete" status="success" metadata={runtime: 187}
|
2748
2772
|
|
2749
2773
|
<!-- Skipped (WARN level) -->
|
2750
2774
|
W, [2022-07-17T18:43:15.000000 #3784] WARN -- ValidateCustomer:
|
2751
|
-
index=1 state="interrupted" status="skipped" reason="Customer already validated"
|
2775
|
+
index=1 chain_id="018c2b95-b764-7615-a924-cc5b910ed1e5" type="Task" class="ValidateCustomer" state="interrupted" status="skipped" reason="Customer already validated"
|
2752
2776
|
|
2753
2777
|
<!-- Failed (ERROR level) -->
|
2754
2778
|
E, [2022-07-17T18:43:15.000000 #3784] ERROR -- CalculateTax:
|
2755
|
-
index=2 state="interrupted" status="failed" metadata={error_code: "TAX_SERVICE_UNAVAILABLE"}
|
2779
|
+
index=2 chain_id="018c2b95-b764-7615-a924-cc5b910ed1e5" type="Task" class="CalculateTax" state="interrupted" status="failed" metadata={error_code: "TAX_SERVICE_UNAVAILABLE"}
|
2756
2780
|
|
2757
2781
|
<!-- Failed Chain -->
|
2758
2782
|
E, [2022-07-17T18:43:15.000000 #3784] ERROR -- BillingWorkflow:
|
2759
|
-
caused_failure={index: 2, class: "CalculateTax", status: "failed"}
|
2760
|
-
threw_failure={index: 1, class: "ValidateCustomer", status: "failed"}
|
2783
|
+
index=3 chain_id="018c2b95-b764-7615-a924-cc5b910ed1e5" type="Task" class="BillingWorkflow" state="interrupted" status="failed" caused_failure={index: 2, class: "CalculateTax", status: "failed"} threw_failure={index: 1, class: "ValidateCustomer", status: "failed"}
|
2761
2784
|
```
|
2762
2785
|
|
2763
2786
|
> [!TIP]
|
data/README.md
CHANGED
@@ -8,16 +8,24 @@
|
|
8
8
|
<img alt="License" src="https://img.shields.io/github/license/drexed/cmdx">
|
9
9
|
</p>
|
10
10
|
|
11
|
-
# CMDx
|
11
|
+
# 🚀 CMDx — Business logic without the chaos
|
12
12
|
|
13
|
-
|
13
|
+
Stop wrestling with messy service objects. CMDx gives you a clean, consistent way to design business processes:
|
14
14
|
|
15
|
-
-
|
16
|
-
-
|
17
|
-
-
|
18
|
-
|
19
|
-
|
20
|
-
|
15
|
+
- Start small with a single `work` method
|
16
|
+
- Scale to complex tasks and multi-step workflows
|
17
|
+
- Get built-in flow control, logging, validations, and more...
|
18
|
+
|
19
|
+
*Build faster. Debug easier. Stay sane.*
|
20
|
+
|
21
|
+
## Compose, Execute, React, Observe pattern
|
22
|
+
|
23
|
+
CMDx encourages breaking business logic into composable tasks. Each task can be combined into larger workflows, executed with standardized flow control, and fully observed through logging, validations, and context.
|
24
|
+
|
25
|
+
- **Compose** → Define small, contract-driven tasks with typed attributes, validations, and natural workflow composition.
|
26
|
+
- **Execute** → Run tasks with clear outcomes, intentional halts, and pluggable behaviors via middlewares and callbacks.
|
27
|
+
- **React** → Adapt to outcomes by chaining follow-up tasks, handling faults, or shaping future flows.
|
28
|
+
- **Observe** → Capture immutable results, structured logs, and full execution chains for reliable tracing and insight.
|
21
29
|
|
22
30
|
## Installation
|
23
31
|
|
@@ -37,11 +45,24 @@ Or install it yourself as:
|
|
37
45
|
|
38
46
|
## Quick Example
|
39
47
|
|
40
|
-
Here's how a quick
|
48
|
+
Here's how a quick 4 step process can open up a world of possibilities:
|
49
|
+
|
50
|
+
### 1. Compose
|
51
|
+
|
52
|
+
#### Minimum Viable Task
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
class SendAnalyzedEmail < CMDx::Task
|
56
|
+
def work
|
57
|
+
user = User.find(context.user_id)
|
58
|
+
MetricsMailer.analyzed(user).deliver_now
|
59
|
+
end
|
60
|
+
end
|
61
|
+
```
|
62
|
+
|
63
|
+
#### Fully Featured Task
|
41
64
|
|
42
65
|
```ruby
|
43
|
-
# 1. Setup task
|
44
|
-
# ---------------------------------
|
45
66
|
class AnalyzeMetrics < CMDx::Task
|
46
67
|
register :middleware, CMDx::Middlewares::Correlate, id: -> { Current.request_id }
|
47
68
|
|
@@ -56,8 +77,10 @@ class AnalyzeMetrics < CMDx::Task
|
|
56
77
|
elsif dataset.unprocessed?
|
57
78
|
skip!("Dataset not ready for analysis")
|
58
79
|
else
|
59
|
-
context.result = PValueAnalyzer.
|
80
|
+
context.result = PValueAnalyzer.execute(dataset:, analysis_type:)
|
60
81
|
context.analyzed_at = Time.now
|
82
|
+
|
83
|
+
SendAnalyzedEmail.execute(user_id: Current.account.manager_id)
|
61
84
|
end
|
62
85
|
end
|
63
86
|
|
@@ -71,16 +94,20 @@ class AnalyzeMetrics < CMDx::Task
|
|
71
94
|
dataset.update!(analysis_result_id: context.result.id)
|
72
95
|
end
|
73
96
|
end
|
97
|
+
```
|
74
98
|
|
75
|
-
|
76
|
-
|
99
|
+
### 2. Execute
|
100
|
+
|
101
|
+
```ruby
|
77
102
|
result = AnalyzeMetrics.execute(
|
78
103
|
dataset_id: 123,
|
79
104
|
"analysis_type" => "advanced"
|
80
105
|
)
|
106
|
+
```
|
81
107
|
|
82
|
-
|
83
|
-
|
108
|
+
### 3. React
|
109
|
+
|
110
|
+
```ruby
|
84
111
|
if result.success?
|
85
112
|
puts "Metrics analyzed at #{result.context.analyzed_at}"
|
86
113
|
elsif result.skipped?
|
@@ -90,6 +117,16 @@ elsif result.failed?
|
|
90
117
|
end
|
91
118
|
```
|
92
119
|
|
120
|
+
### 4. Observe
|
121
|
+
|
122
|
+
```log
|
123
|
+
I, [2022-07-17T18:42:37.000000 #3784] INFO -- CMDx:
|
124
|
+
index=1 chain_id="018c2b95-23j4-2kj3-32kj-3n4jk3n4jknf" type="Task" class="SendAnalyzedEmail" state="complete" status="success" metadata={runtime: 347}
|
125
|
+
|
126
|
+
I, [2022-07-17T18:43:15.000000 #3784] INFO -- CMDx:
|
127
|
+
index=0 chain_id="018c2b95-b764-7615-a924-cc5b910ed1e5" type="Task" class="AnalyzeMetrics" state="complete" status="success" metadata={runtime: 187}
|
128
|
+
```
|
129
|
+
|
93
130
|
## Table of contents
|
94
131
|
|
95
132
|
- [Getting Started](docs/getting_started.md)
|
@@ -122,12 +159,12 @@ end
|
|
122
159
|
|
123
160
|
## Ecosystem
|
124
161
|
|
125
|
-
- [cmdx-
|
126
|
-
- [cmdx-parallel](https://github.com/drexed/cmdx-parallel) - Parallel workflow tasks
|
162
|
+
- [cmdx-rspec](https://github.com/drexed/cmdx-rspec) - RSpec test matchers
|
127
163
|
|
128
|
-
|
164
|
+
For backwards compatibility of certain functionality:
|
129
165
|
|
130
|
-
-
|
166
|
+
- [cmdx-i18n](https://github.com/drexed/cmdx-i18n) - 85+ translations, `v1.5.0` - `v1.6.2`
|
167
|
+
- [cmdx-parallel](https://github.com/drexed/cmdx-parallel) - Parallel workflow tasks, `v1.6.1` - `v1.6.2`
|
131
168
|
|
132
169
|
## Development
|
133
170
|
|
data/docs/getting_started.md
CHANGED
@@ -2,8 +2,25 @@
|
|
2
2
|
|
3
3
|
CMDx is a Ruby framework for building maintainable, observable business logic through composable command objects. Design robust workflows with automatic attribute validation, structured error handling, comprehensive logging, and intelligent execution flow control.
|
4
4
|
|
5
|
+
**Common Challenges:**
|
6
|
+
|
7
|
+
- Inconsistent patterns across implementations
|
8
|
+
- Minimal or no logging, making debugging painful
|
9
|
+
- Fragile designs that erode developer confidence
|
10
|
+
|
11
|
+
**CMDx Solutions:**
|
12
|
+
|
13
|
+
- Establishes a consistent, standardized design
|
14
|
+
- Provides flow control and error handling
|
15
|
+
- Supports composable, reusable workflows
|
16
|
+
- Includes detailed logging for observability
|
17
|
+
- Defines input attributes with fallback defaults
|
18
|
+
- Adds validations and type coercions
|
19
|
+
- Plus many other developer-friendly tools
|
20
|
+
|
5
21
|
## Table of Contents
|
6
22
|
|
23
|
+
- [Compose, Execute, React, Observe pattern](#compose-execute-react-observe-pattern)
|
7
24
|
- [Installation](#installation)
|
8
25
|
- [Configuration Hierarchy](#configuration-hierarchy)
|
9
26
|
- [Global Configuration](#global-configuration)
|
@@ -21,6 +38,15 @@ CMDx is a Ruby framework for building maintainable, observable business logic th
|
|
21
38
|
- [Resetting](#resetting)
|
22
39
|
- [Task Generator](#task-generator)
|
23
40
|
|
41
|
+
## Compose, Execute, React, Observe pattern
|
42
|
+
|
43
|
+
CMDx encourages breaking business logic into composable tasks. Each task can be combined into larger workflows, executed with standardized flow control, and fully observed through logging, validations, and context.
|
44
|
+
|
45
|
+
- *Compose* → Define small, contract-driven tasks with typed attributes, validations, and natural workflow composition.
|
46
|
+
- *Execute* → Run tasks with clear outcomes, intentional halts, and pluggable behaviors via middlewares and callbacks.
|
47
|
+
- *React* → Adapt to outcomes by chaining follow-up tasks, handling faults, or shaping future flows.
|
48
|
+
- *Observe* → Capture immutable results, structured logs, and full execution chains for reliable tracing and insight.
|
49
|
+
|
24
50
|
## Installation
|
25
51
|
|
26
52
|
Add CMDx to your Gemfile:
|
data/docs/logging.md
CHANGED
@@ -25,21 +25,19 @@ Sample output:
|
|
25
25
|
```log
|
26
26
|
<!-- Success (INFO level) -->
|
27
27
|
I, [2022-07-17T18:43:15.000000 #3784] INFO -- GenerateInvoice:
|
28
|
-
index=0 chain_id="018c2b95-b764-7615-a924-cc5b910ed1e5" type="Task"
|
29
|
-
class="GenerateInvoice" state="complete" status="success" metadata={runtime: 187}
|
28
|
+
index=0 chain_id="018c2b95-b764-7615-a924-cc5b910ed1e5" type="Task" class="GenerateInvoice" state="complete" status="success" metadata={runtime: 187}
|
30
29
|
|
31
30
|
<!-- Skipped (WARN level) -->
|
32
31
|
W, [2022-07-17T18:43:15.000000 #3784] WARN -- ValidateCustomer:
|
33
|
-
index=1 state="interrupted" status="skipped" reason="Customer already validated"
|
32
|
+
index=1 chain_id="018c2b95-b764-7615-a924-cc5b910ed1e5" type="Task" class="ValidateCustomer" state="interrupted" status="skipped" reason="Customer already validated"
|
34
33
|
|
35
34
|
<!-- Failed (ERROR level) -->
|
36
35
|
E, [2022-07-17T18:43:15.000000 #3784] ERROR -- CalculateTax:
|
37
|
-
index=2 state="interrupted" status="failed" metadata={error_code: "TAX_SERVICE_UNAVAILABLE"}
|
36
|
+
index=2 chain_id="018c2b95-b764-7615-a924-cc5b910ed1e5" type="Task" class="CalculateTax" state="interrupted" status="failed" metadata={error_code: "TAX_SERVICE_UNAVAILABLE"}
|
38
37
|
|
39
38
|
<!-- Failed Chain -->
|
40
39
|
E, [2022-07-17T18:43:15.000000 #3784] ERROR -- BillingWorkflow:
|
41
|
-
caused_failure={index: 2, class: "CalculateTax", status: "failed"}
|
42
|
-
threw_failure={index: 1, class: "ValidateCustomer", status: "failed"}
|
40
|
+
index=3 chain_id="018c2b95-b764-7615-a924-cc5b910ed1e5" type="Task" class="BillingWorkflow" state="interrupted" status="failed" caused_failure={index: 2, class: "CalculateTax", status: "failed"} threw_failure={index: 1, class: "ValidateCustomer", status: "failed"}
|
43
41
|
```
|
44
42
|
|
45
43
|
> [!TIP]
|
@@ -95,7 +95,8 @@ module CMDx
|
|
95
95
|
def call(task, **options, &)
|
96
96
|
return yield unless Utils::Condition.evaluate(task, options)
|
97
97
|
|
98
|
-
correlation_id =
|
98
|
+
correlation_id = task.result.metadata[:correlation_id] ||=
|
99
|
+
id ||
|
99
100
|
case callable = options[:id]
|
100
101
|
when Symbol then task.send(callable)
|
101
102
|
when Proc then task.instance_eval(&callable)
|
@@ -107,9 +108,7 @@ module CMDx
|
|
107
108
|
end
|
108
109
|
end
|
109
110
|
|
110
|
-
|
111
|
-
task.result.metadata[:correlation_id] = correlation_id
|
112
|
-
result
|
111
|
+
use(correlation_id, &)
|
113
112
|
end
|
114
113
|
|
115
114
|
end
|
data/lib/cmdx/version.rb
CHANGED
@@ -10,7 +10,7 @@ module Cmdx
|
|
10
10
|
|
11
11
|
source_root File.expand_path("../../locales", __dir__)
|
12
12
|
|
13
|
-
desc "Copies the locale with the given
|
13
|
+
desc "Copies the locale with the given ISO 639 code"
|
14
14
|
|
15
15
|
argument :locale, type: :string, default: "en", banner: "locale: en, es, fr, etc"
|
16
16
|
|