cmdx 1.7.5 → 1.9.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/.DS_Store +0 -0
- data/.cursor/prompts/docs.md +3 -3
- data/.cursor/prompts/llms.md +1 -3
- data/.cursor/prompts/rspec.md +1 -1
- data/.irbrc +14 -2
- data/CHANGELOG.md +62 -29
- data/LLM.md +203 -78
- data/README.md +23 -85
- data/docs/.DS_Store +0 -0
- data/docs/assets/favicon.ico +0 -0
- data/docs/assets/favicon.svg +1 -0
- data/docs/attributes/coercions.md +19 -29
- data/docs/attributes/defaults.md +3 -16
- data/docs/attributes/definitions.md +29 -39
- data/docs/attributes/naming.md +3 -13
- data/docs/attributes/transformations.md +63 -0
- data/docs/attributes/validations.md +23 -40
- data/docs/basics/chain.md +14 -23
- data/docs/basics/context.md +13 -22
- data/docs/basics/execution.md +8 -26
- data/docs/basics/setup.md +8 -19
- data/docs/callbacks.md +19 -32
- data/docs/deprecation.md +8 -25
- data/docs/getting_started.md +101 -77
- data/docs/index.md +120 -0
- data/docs/internationalization.md +6 -18
- data/docs/interruptions/exceptions.md +10 -16
- data/docs/interruptions/faults.md +8 -25
- data/docs/interruptions/halt.md +31 -25
- data/docs/logging.md +7 -17
- data/docs/middlewares.md +13 -29
- data/docs/outcomes/result.md +21 -38
- data/docs/outcomes/states.md +8 -22
- data/docs/outcomes/statuses.md +10 -21
- data/docs/stylesheets/extra.css +42 -0
- data/docs/tips_and_tricks.md +7 -46
- data/docs/workflows.md +23 -38
- data/examples/active_record_query_tagging.md +46 -0
- data/examples/paper_trail_whatdunnit.md +39 -0
- data/lib/cmdx/attribute.rb +9 -2
- data/lib/cmdx/attribute_value.rb +31 -10
- data/lib/cmdx/callback_registry.rb +12 -2
- data/lib/cmdx/coercions/hash.rb +6 -1
- data/lib/cmdx/configuration.rb +10 -2
- data/lib/cmdx/deprecator.rb +3 -3
- data/lib/cmdx/errors.rb +1 -1
- data/lib/cmdx/executor.rb +97 -9
- data/lib/cmdx/log_formatters/logstash.rb +4 -4
- data/lib/cmdx/pipeline.rb +4 -4
- data/lib/cmdx/railtie.rb +9 -0
- data/lib/cmdx/result.rb +10 -1
- data/lib/cmdx/task.rb +12 -7
- data/lib/cmdx/version.rb +1 -1
- data/lib/cmdx.rb +1 -0
- data/lib/generators/cmdx/templates/install.rb +9 -0
- data/lib/locales/af.yml +2 -2
- data/lib/locales/ar.yml +2 -2
- data/lib/locales/az.yml +2 -2
- data/lib/locales/be.yml +2 -2
- data/lib/locales/bg.yml +2 -2
- data/lib/locales/bn.yml +2 -2
- data/lib/locales/bs.yml +2 -2
- data/lib/locales/ca.yml +2 -2
- data/lib/locales/cnr.yml +2 -2
- data/lib/locales/cs.yml +2 -2
- data/lib/locales/cy.yml +2 -2
- data/lib/locales/da.yml +2 -2
- data/lib/locales/de.yml +2 -2
- data/lib/locales/dz.yml +2 -2
- data/lib/locales/el.yml +2 -2
- data/lib/locales/en.yml +2 -2
- data/lib/locales/eo.yml +2 -2
- data/lib/locales/es.yml +2 -2
- data/lib/locales/et.yml +2 -2
- data/lib/locales/eu.yml +2 -2
- data/lib/locales/fa.yml +2 -2
- data/lib/locales/fi.yml +2 -2
- data/lib/locales/fr.yml +2 -2
- data/lib/locales/fy.yml +2 -2
- data/lib/locales/gd.yml +2 -2
- data/lib/locales/gl.yml +2 -2
- data/lib/locales/he.yml +2 -2
- data/lib/locales/hi.yml +2 -2
- data/lib/locales/hr.yml +2 -2
- data/lib/locales/hu.yml +2 -2
- data/lib/locales/hy.yml +2 -2
- data/lib/locales/id.yml +2 -2
- data/lib/locales/is.yml +2 -2
- data/lib/locales/it.yml +2 -2
- data/lib/locales/ja.yml +2 -2
- data/lib/locales/ka.yml +2 -2
- data/lib/locales/kk.yml +2 -2
- data/lib/locales/km.yml +2 -2
- data/lib/locales/kn.yml +2 -2
- data/lib/locales/ko.yml +2 -2
- data/lib/locales/lb.yml +2 -2
- data/lib/locales/lo.yml +2 -2
- data/lib/locales/lt.yml +2 -2
- data/lib/locales/lv.yml +2 -2
- data/lib/locales/mg.yml +2 -2
- data/lib/locales/mk.yml +2 -2
- data/lib/locales/ml.yml +2 -2
- data/lib/locales/mn.yml +2 -2
- data/lib/locales/mr-IN.yml +2 -2
- data/lib/locales/ms.yml +2 -2
- data/lib/locales/nb.yml +2 -2
- data/lib/locales/ne.yml +2 -2
- data/lib/locales/nl.yml +2 -2
- data/lib/locales/nn.yml +2 -2
- data/lib/locales/oc.yml +2 -2
- data/lib/locales/or.yml +2 -2
- data/lib/locales/pa.yml +2 -2
- data/lib/locales/pl.yml +2 -2
- data/lib/locales/pt.yml +2 -2
- data/lib/locales/rm.yml +2 -2
- data/lib/locales/ro.yml +2 -2
- data/lib/locales/ru.yml +2 -2
- data/lib/locales/sc.yml +2 -2
- data/lib/locales/sk.yml +2 -2
- data/lib/locales/sl.yml +2 -2
- data/lib/locales/sq.yml +2 -2
- data/lib/locales/sr.yml +2 -2
- data/lib/locales/st.yml +2 -2
- data/lib/locales/sv.yml +2 -2
- data/lib/locales/sw.yml +2 -2
- data/lib/locales/ta.yml +2 -2
- data/lib/locales/te.yml +2 -2
- data/lib/locales/th.yml +2 -2
- data/lib/locales/tl.yml +2 -2
- data/lib/locales/tr.yml +2 -2
- data/lib/locales/tt.yml +2 -2
- data/lib/locales/ug.yml +2 -2
- data/lib/locales/uk.yml +2 -2
- data/lib/locales/ur.yml +2 -2
- data/lib/locales/uz.yml +2 -2
- data/lib/locales/vi.yml +2 -2
- data/lib/locales/wo.yml +2 -2
- data/lib/locales/zh-CN.yml +2 -2
- data/lib/locales/zh-HK.yml +2 -2
- data/lib/locales/zh-TW.yml +2 -2
- data/lib/locales/zh-YUE.yml +2 -2
- data/mkdocs.yml +122 -0
- data/src/cmdx-dark-logo.png +0 -0
- data/src/cmdx-favicon.svg +1 -0
- data/src/cmdx-light-logo.png +0 -0
- data/src/cmdx-logo.svg +1 -0
- metadata +15 -4
- data/lib/cmdx/freezer.rb +0 -51
- data/src/cmdx-logo.png +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5ac9af727a0dd815dad8285e7e46d666e3f26f51eb03c3f1ec014c12e4af0e23
|
|
4
|
+
data.tar.gz: 8279a9ebcf5339fb281cd685cb0484b005f72b2605749f1418baea282be1afdf
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f97ef5703cacbfa7c51e4e513e214322c330750066e2420727d1bdc6469dac092b4f9b601291c8b0a721cba6bca863d22d4189bf10a8aa4b0ec53683f6598ccc
|
|
7
|
+
data.tar.gz: 3a1019a2f2db8088ec70821d51c5d13a10554f788a7732a70a8c14830cd9804fee354447e61dae3ca86fcf4a8a664932bdff5465a78256845d224870e78951ce
|
data/.DS_Store
CHANGED
|
Binary file
|
data/.cursor/prompts/docs.md
CHANGED
|
@@ -3,10 +3,10 @@ You are a senior Ruby developer with expert knowledge of CMDx and writing docume
|
|
|
3
3
|
Update the active tab using the following guidelines:
|
|
4
4
|
|
|
5
5
|
- Follow best practices and implementation
|
|
6
|
-
- Use a consistent professional voice
|
|
6
|
+
- Use a consistent warm, friendly and professional voice
|
|
7
7
|
- Examples should be concise, non-repetitive, and realistic
|
|
8
8
|
- Update any pre-existing documentation to match stated rules
|
|
9
9
|
- Examples should not cross boundaries or focus
|
|
10
|
-
- Docs must cover both typical use cases, including invalid
|
|
11
|
-
- Use
|
|
10
|
+
- Docs must cover both typical use cases, including invalid and error conditions
|
|
11
|
+
- Use mkdocs Admonitions to emphasize critical information (https://squidfunk.github.io/mkdocs-material/reference/admonitions/)
|
|
12
12
|
- Optimize for LLM's including coding and AI agents
|
data/.cursor/prompts/llms.md
CHANGED
|
@@ -4,9 +4,7 @@ Process the following instructions in the order given:
|
|
|
4
4
|
2. Append all files within `docs/**/*.md` into @LLM.md
|
|
5
5
|
2a. Use order outlined in the table of contents of @README.md
|
|
6
6
|
2b. Process one file at a time faster performance and improved accuracy
|
|
7
|
-
2c.
|
|
8
|
-
2c. Remove the navigations below `---` from the chunk
|
|
9
|
-
2d. Wrap the chunk the files GitHub url the top and a spacer at the bottom like so:
|
|
7
|
+
2c. Wrap the chunk the files GitHub url the top and a spacer at the bottom like so:
|
|
10
8
|
```
|
|
11
9
|
|
|
12
10
|
---
|
data/.cursor/prompts/rspec.md
CHANGED
|
@@ -18,7 +18,7 @@ Add tests for the active tab using the following guidelines:
|
|
|
18
18
|
- Use clear and descriptive names for describe, context, and it blocks
|
|
19
19
|
- Prefer the expect syntax for assertions to improve readability
|
|
20
20
|
- Keep test code concise; avoid unnecessary complexity or duplication
|
|
21
|
-
- Tests must cover both typical cases and edge cases, including
|
|
21
|
+
- Tests must cover both typical cases and edge cases, including Invalid and error conditions
|
|
22
22
|
- Consider all possible scenarios for each method or behavior and ensure they are tested
|
|
23
23
|
- Do NOT include integration or real world examples
|
|
24
24
|
- Verify all specs are passing
|
data/.irbrc
CHANGED
|
@@ -2,5 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
require "pp"
|
|
4
4
|
|
|
5
|
-
#
|
|
6
|
-
|
|
5
|
+
# rubocop:disable Style/MixinUsage
|
|
6
|
+
unless defined?(CMDx)
|
|
7
|
+
require_relative "lib/cmdx"
|
|
8
|
+
|
|
9
|
+
require_relative "spec/support/helpers/task_builders"
|
|
10
|
+
require_relative "spec/support/helpers/workflow_builders"
|
|
11
|
+
include CMDx::Testing::TaskBuilders
|
|
12
|
+
include CMDx::Testing::WorkflowBuilders
|
|
13
|
+
end
|
|
14
|
+
# rubocop:enable Style/MixinUsage
|
|
15
|
+
|
|
16
|
+
def reload!
|
|
17
|
+
exec("irb")
|
|
18
|
+
end
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,37 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
6
6
|
|
|
7
7
|
## [UNRELEASED]
|
|
8
8
|
|
|
9
|
+
## [1.9.0] - 2025-10-21
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- Added `transform` option to attributes
|
|
13
|
+
- Added option to output failure backtraces
|
|
14
|
+
- Added exception handling for non-bang methods
|
|
15
|
+
- Added durability with automatic retries to execution
|
|
16
|
+
- Added `to_h` hash coercion support
|
|
17
|
+
- Added comprehensive MkDocs configuration with material theme
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
- Improved performance of task settings setup
|
|
21
|
+
- Improved error messages for raised exceptions
|
|
22
|
+
- Improved inheritance of parent settings
|
|
23
|
+
- Cleaned halt backtrace frames for better readability
|
|
24
|
+
|
|
25
|
+
### Removed
|
|
26
|
+
- Removed `Freezer` module and moved logic into executor `freeze_execution!` method
|
|
27
|
+
- Removed task parameter from callback signature
|
|
28
|
+
- Removed task and workflow arguments from conditional checks
|
|
29
|
+
- Removed chain persistence after execution in specs
|
|
30
|
+
|
|
31
|
+
## [1.8.0] - 2025-09-22
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
- Generalized locale values for fault `invalid` and `unspecified`
|
|
35
|
+
- Nested attribute error messages under `error` key within metadata
|
|
36
|
+
- Reordered logstash formatter keys for consistency
|
|
37
|
+
- Improved error message for already defined items
|
|
38
|
+
- Changed hash coercion for `nil` to return `{}`
|
|
39
|
+
|
|
9
40
|
## [1.7.5] - 2025-09-10
|
|
10
41
|
|
|
11
42
|
### Added
|
|
@@ -22,69 +53,71 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
22
53
|
|
|
23
54
|
## [1.7.3] - 2025-09-03
|
|
24
55
|
|
|
25
|
-
###
|
|
26
|
-
-
|
|
27
|
-
-
|
|
56
|
+
### Changed
|
|
57
|
+
- Changed validation reasons to use generic values
|
|
58
|
+
- Moved validation full message string to `:full_message` key within metadata
|
|
28
59
|
|
|
29
60
|
## [1.7.2] - 2025-09-03
|
|
30
61
|
|
|
31
|
-
###
|
|
32
|
-
-
|
|
62
|
+
### Changed
|
|
63
|
+
- Changed correlation ID to be set before continuing to further steps
|
|
33
64
|
|
|
34
65
|
## [1.7.1] - 2025-08-26
|
|
35
66
|
|
|
36
67
|
### Added
|
|
37
|
-
-
|
|
68
|
+
- Added result yielding when block is given to `execute` and `execute!` methods
|
|
38
69
|
|
|
39
70
|
## [1.7.0] - 2025-08-25
|
|
40
71
|
|
|
41
72
|
### Added
|
|
42
|
-
-
|
|
73
|
+
- Added workflow generator
|
|
43
74
|
|
|
44
|
-
###
|
|
45
|
-
-
|
|
46
|
-
-
|
|
75
|
+
### Changed
|
|
76
|
+
- Ported `cmdx-parallel` changes into core
|
|
77
|
+
- Ported `cmdx-i18n` changes into core
|
|
47
78
|
|
|
48
79
|
## [1.6.2] - 2025-08-24
|
|
49
80
|
|
|
50
|
-
###
|
|
51
|
-
-
|
|
52
|
-
-
|
|
81
|
+
### Changed
|
|
82
|
+
- Prefixed railtie I18n with `::` for compatibility with `CMDx::I18n`
|
|
83
|
+
- Changed to use `cmdx-rspec` for matchers support
|
|
53
84
|
|
|
54
85
|
## [1.6.1] - 2025-08-23
|
|
55
86
|
|
|
56
|
-
###
|
|
57
|
-
-
|
|
58
|
-
-
|
|
87
|
+
### Changed
|
|
88
|
+
- Changed task results to be logged before freezing
|
|
89
|
+
- Renamed `execute_tasks_sequentially` to `execute_tasks_in_sequence`
|
|
59
90
|
|
|
60
91
|
## [1.6.0] - 2025-08-22
|
|
61
92
|
|
|
62
|
-
###
|
|
63
|
-
-
|
|
64
|
-
|
|
65
|
-
|
|
93
|
+
### Added
|
|
94
|
+
- Added workflow task `:breakpoints` support
|
|
95
|
+
|
|
96
|
+
### Changed
|
|
97
|
+
- Renamed `Worker` class to `Executor`
|
|
98
|
+
- Moved workflow `work` logic into `Pipeline`
|
|
66
99
|
|
|
67
100
|
## [1.5.2] - 2025-08-22
|
|
68
101
|
|
|
69
|
-
###
|
|
70
|
-
-
|
|
102
|
+
### Changed
|
|
103
|
+
- Renamed workflow `execution_groups` attribute to `pipeline`
|
|
71
104
|
|
|
72
105
|
## [1.5.1] - 2025-08-21
|
|
73
106
|
|
|
74
|
-
###
|
|
75
|
-
-
|
|
76
|
-
-
|
|
77
|
-
-
|
|
107
|
+
### Changed
|
|
108
|
+
- Prefixed locale I18n with `::` for compatibility with `CMDx::I18n`
|
|
109
|
+
- Added safe navigation to length and numeric validators
|
|
110
|
+
- Updated railtie file path to point to correct directory
|
|
78
111
|
|
|
79
112
|
## [1.5.0] - 2025-08-21
|
|
80
113
|
|
|
81
|
-
###
|
|
82
|
-
- BREAKING
|
|
114
|
+
### Changed
|
|
115
|
+
- **BREAKING**: Revamped CMDx for improved clarity, transparency, and higher performance
|
|
83
116
|
|
|
84
117
|
## [1.1.2] - 2025-07-20
|
|
85
118
|
|
|
86
119
|
### Changed
|
|
87
|
-
- All
|
|
120
|
+
- All changes between versions `0.1.0` and `1.1.2` should be reviewed within their respective git tags
|
|
88
121
|
|
|
89
122
|
## [0.1.0] - 2025-03-07
|
|
90
123
|
|
data/LLM.md
CHANGED
|
@@ -87,6 +87,45 @@ CMDx.configure do |config|
|
|
|
87
87
|
end
|
|
88
88
|
```
|
|
89
89
|
|
|
90
|
+
### Backtraces
|
|
91
|
+
|
|
92
|
+
Enable backtraces to be logged on any non-fault exceptions for improved debugging context. Run them through a cleaner to remove unwanted stack trace noise.
|
|
93
|
+
|
|
94
|
+
> [!NOTE]
|
|
95
|
+
> The `backtrace_cleaner` is set to `Rails.backtrace_cleaner.clean` in a Rails env by default.
|
|
96
|
+
|
|
97
|
+
```ruby
|
|
98
|
+
CMDx.configure do |config|
|
|
99
|
+
# Truthy
|
|
100
|
+
config.backtrace = true
|
|
101
|
+
|
|
102
|
+
# Via callable (must respond to `call(backtrace)`)
|
|
103
|
+
config.backtrace_cleaner = AdvanceCleaner.new
|
|
104
|
+
|
|
105
|
+
# Via proc or lambda
|
|
106
|
+
config.backtrace_cleaner = ->(backtrace) { backtrace[0..5] }
|
|
107
|
+
end
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Exception Handlers
|
|
111
|
+
|
|
112
|
+
Use exception handlers are called on non-fault standard error based exceptions.
|
|
113
|
+
|
|
114
|
+
> [!TIP]
|
|
115
|
+
> Use exception handlers to send errors to your APM of choice.
|
|
116
|
+
|
|
117
|
+
```ruby
|
|
118
|
+
CMDx.configure do |config|
|
|
119
|
+
# Via callable (must respond to `call(task, exception)`)
|
|
120
|
+
config.exception_handler = NewRelicReporter
|
|
121
|
+
|
|
122
|
+
# Via proc or lambda
|
|
123
|
+
config.exception_handler = proc do |task, exception|
|
|
124
|
+
APMService.report(exception, extra_data: { task: task.name, id: task.id })
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
```
|
|
128
|
+
|
|
90
129
|
### Logging
|
|
91
130
|
|
|
92
131
|
```ruby
|
|
@@ -213,6 +252,8 @@ class GenerateInvoice < CMDx::Task
|
|
|
213
252
|
# Global configuration overrides
|
|
214
253
|
task_breakpoints: ["failed"], # Breakpoint override
|
|
215
254
|
workflow_breakpoints: [], # Breakpoint override
|
|
255
|
+
backtrace: true, # Toggle backtrace
|
|
256
|
+
backtrace_cleaner: ->(bt) { bt[0..5] }, # Backtrace cleaner
|
|
216
257
|
logger: CustomLogger.new($stdout), # Custom logger
|
|
217
258
|
|
|
218
259
|
# Task configuration settings
|
|
@@ -220,7 +261,10 @@ class GenerateInvoice < CMDx::Task
|
|
|
220
261
|
log_level: :info, # Log level override
|
|
221
262
|
log_formatter: CMDx::LogFormatters::Json.new # Log formatter override
|
|
222
263
|
tags: ["billing", "financial"], # Logging tags
|
|
223
|
-
deprecated: true
|
|
264
|
+
deprecated: true, # Task deprecations
|
|
265
|
+
retries: 3, # Non-fault exception retries
|
|
266
|
+
retry_on: [External::ApiError], # List of exceptions to retry on
|
|
267
|
+
retry_jitter: 1 # Space between retry iteration, eg: current retry num + 1
|
|
224
268
|
)
|
|
225
269
|
|
|
226
270
|
def work
|
|
@@ -229,8 +273,8 @@ class GenerateInvoice < CMDx::Task
|
|
|
229
273
|
end
|
|
230
274
|
```
|
|
231
275
|
|
|
232
|
-
> [!
|
|
233
|
-
>
|
|
276
|
+
> [!IMPORTANT]
|
|
277
|
+
> Retries reuse the same context when executing its work. By default all `StandardErrors` will be retried if no `retry_on` option is passed.
|
|
234
278
|
|
|
235
279
|
### Registrations
|
|
236
280
|
|
|
@@ -783,7 +827,7 @@ result = ProcessInventory.execute(inventory_id: 456)
|
|
|
783
827
|
result.status #=> "skipped"
|
|
784
828
|
|
|
785
829
|
# Without a reason
|
|
786
|
-
result.reason #=> "
|
|
830
|
+
result.reason #=> "Unspecified"
|
|
787
831
|
|
|
788
832
|
# With a reason
|
|
789
833
|
result.reason #=> "Warehouse closed"
|
|
@@ -818,7 +862,7 @@ result = ProcessRefund.execute(refund_id: 789)
|
|
|
818
862
|
result.status #=> "failed"
|
|
819
863
|
|
|
820
864
|
# Without a reason
|
|
821
|
-
result.reason #=> "
|
|
865
|
+
result.reason #=> "Unspecified"
|
|
822
866
|
|
|
823
867
|
# With a reason
|
|
824
868
|
result.reason #=> "Refund period has expired"
|
|
@@ -938,8 +982,28 @@ skip!("Paused")
|
|
|
938
982
|
fail!("Unsupported")
|
|
939
983
|
|
|
940
984
|
# Bad: Default, cannot determine reason
|
|
941
|
-
skip! #=> "
|
|
942
|
-
fail! #=> "
|
|
985
|
+
skip! #=> "Unspecified"
|
|
986
|
+
fail! #=> "Unspecified"
|
|
987
|
+
```
|
|
988
|
+
|
|
989
|
+
## Manual Errors
|
|
990
|
+
|
|
991
|
+
There are rare cases where you need to manually assign errors.
|
|
992
|
+
|
|
993
|
+
> [!IMPORTANT]
|
|
994
|
+
> Keep in mind you will still need to initiate a fault if a stoppage of work is required.
|
|
995
|
+
|
|
996
|
+
```ruby
|
|
997
|
+
class ProcessRenewal < CMDx::Task
|
|
998
|
+
def work
|
|
999
|
+
if document.nonrenewable?
|
|
1000
|
+
errors.add(:document, "not renewable")
|
|
1001
|
+
fail!("document could not be renewed")
|
|
1002
|
+
else
|
|
1003
|
+
document.renew!
|
|
1004
|
+
end
|
|
1005
|
+
end
|
|
1006
|
+
end
|
|
943
1007
|
```
|
|
944
1008
|
|
|
945
1009
|
---
|
|
@@ -1150,6 +1214,9 @@ result.reason #=> "[ActiveRecord::NotFoundError] record not found"
|
|
|
1150
1214
|
result.cause #=> <ActiveRecord::NotFoundError>
|
|
1151
1215
|
```
|
|
1152
1216
|
|
|
1217
|
+
> [!NOTE]
|
|
1218
|
+
> The `exception_handler` setting only works with non-bang execution as it catches all exceptions preventing them from reaching your apps global error handler.
|
|
1219
|
+
|
|
1153
1220
|
### Bang execution
|
|
1154
1221
|
|
|
1155
1222
|
The `execute!` method allows unhandled exceptions to propagate, enabling standard Ruby exception handling while respecting CMDx fault configuration.
|
|
@@ -1301,19 +1368,19 @@ result = BuildApplication.execute(version: "1.2.3")
|
|
|
1301
1368
|
|
|
1302
1369
|
# Status-based handlers
|
|
1303
1370
|
result
|
|
1304
|
-
.
|
|
1305
|
-
.
|
|
1306
|
-
.
|
|
1371
|
+
.handle_success { |result| notify_deployment_ready(result) }
|
|
1372
|
+
.handle_failed { |result| handle_build_failure(result) }
|
|
1373
|
+
.handle_skipped { |result| log_skip_reason(result) }
|
|
1307
1374
|
|
|
1308
1375
|
# State-based handlers
|
|
1309
1376
|
result
|
|
1310
|
-
.
|
|
1311
|
-
.
|
|
1377
|
+
.handle_complete { |result| update_build_status(result) }
|
|
1378
|
+
.handle_interrupted { |result| cleanup_partial_artifacts(result) }
|
|
1312
1379
|
|
|
1313
1380
|
# Outcome-based handlers
|
|
1314
1381
|
result
|
|
1315
|
-
.
|
|
1316
|
-
.
|
|
1382
|
+
.handle_good { |result| increment_success_counter(result) }
|
|
1383
|
+
.handle_bad { |result| alert_operations_team(result) }
|
|
1317
1384
|
```
|
|
1318
1385
|
|
|
1319
1386
|
## Pattern Matching
|
|
@@ -1435,9 +1502,9 @@ result = ProcessVideoUpload.execute
|
|
|
1435
1502
|
|
|
1436
1503
|
# Individual state handlers
|
|
1437
1504
|
result
|
|
1438
|
-
.
|
|
1439
|
-
.
|
|
1440
|
-
.
|
|
1505
|
+
.handle_complete { |result| send_upload_notification(result) }
|
|
1506
|
+
.handle_interrupted { |result| cleanup_temp_files(result) }
|
|
1507
|
+
.handle_executed { |result| log_upload_metrics(result) }
|
|
1441
1508
|
```
|
|
1442
1509
|
|
|
1443
1510
|
---
|
|
@@ -1500,14 +1567,14 @@ result = ProcessNotification.execute
|
|
|
1500
1567
|
|
|
1501
1568
|
# Individual status handlers
|
|
1502
1569
|
result
|
|
1503
|
-
.
|
|
1504
|
-
.
|
|
1505
|
-
.
|
|
1570
|
+
.handle_success { |result| mark_notification_sent(result) }
|
|
1571
|
+
.handle_skipped { |result| log_notification_skipped(result) }
|
|
1572
|
+
.handle_failed { |result| queue_retry_notification(result) }
|
|
1506
1573
|
|
|
1507
1574
|
# Outcome-based handlers
|
|
1508
1575
|
result
|
|
1509
|
-
.
|
|
1510
|
-
.
|
|
1576
|
+
.handle_good { |result| update_message_stats(result) }
|
|
1577
|
+
.handle_bad { |result| track_delivery_failure(result) }
|
|
1511
1578
|
```
|
|
1512
1579
|
|
|
1513
1580
|
---
|
|
@@ -1754,12 +1821,14 @@ result = ConfigureServer.execute(server_id: "srv-001")
|
|
|
1754
1821
|
|
|
1755
1822
|
result.state #=> "interrupted"
|
|
1756
1823
|
result.status #=> "failed"
|
|
1757
|
-
result.reason #=> "Invalid
|
|
1824
|
+
result.reason #=> "Invalid"
|
|
1758
1825
|
result.metadata #=> {
|
|
1759
|
-
#
|
|
1760
|
-
#
|
|
1761
|
-
#
|
|
1762
|
-
#
|
|
1826
|
+
# errors: {
|
|
1827
|
+
# full_message: "environment is required. network_config is required.",
|
|
1828
|
+
# messages: {
|
|
1829
|
+
# environment: ["is required"],
|
|
1830
|
+
# network_config: ["is required"]
|
|
1831
|
+
# }
|
|
1763
1832
|
# }
|
|
1764
1833
|
# }
|
|
1765
1834
|
|
|
@@ -1772,11 +1841,13 @@ result = ConfigureServer.execute(
|
|
|
1772
1841
|
|
|
1773
1842
|
result.state #=> "interrupted"
|
|
1774
1843
|
result.status #=> "failed"
|
|
1775
|
-
result.reason #=> "Invalid
|
|
1844
|
+
result.reason #=> "Invalid"
|
|
1776
1845
|
result.metadata #=> {
|
|
1777
|
-
#
|
|
1778
|
-
#
|
|
1779
|
-
#
|
|
1846
|
+
# errors: {
|
|
1847
|
+
# full_message: "port is required.",
|
|
1848
|
+
# messages: {
|
|
1849
|
+
# port: ["is required"]
|
|
1850
|
+
# }
|
|
1780
1851
|
# }
|
|
1781
1852
|
# }
|
|
1782
1853
|
```
|
|
@@ -2000,12 +2071,14 @@ result = AnalyzePerformance.execute(
|
|
|
2000
2071
|
|
|
2001
2072
|
result.state #=> "interrupted"
|
|
2002
2073
|
result.status #=> "failed"
|
|
2003
|
-
result.reason #=> "Invalid
|
|
2074
|
+
result.reason #=> "Invalid"
|
|
2004
2075
|
result.metadata #=> {
|
|
2005
|
-
#
|
|
2006
|
-
#
|
|
2007
|
-
#
|
|
2008
|
-
#
|
|
2076
|
+
# errors: {
|
|
2077
|
+
# full_message: "iterations could not coerce into an integer. score could not coerce into one of: float, big_decimal.",
|
|
2078
|
+
# messages: {
|
|
2079
|
+
# iterations: ["could not coerce into an integer"],
|
|
2080
|
+
# score: ["could not coerce into one of: float, big_decimal"]
|
|
2081
|
+
# }
|
|
2009
2082
|
# }
|
|
2010
2083
|
# }
|
|
2011
2084
|
```
|
|
@@ -2294,14 +2367,16 @@ result = CreateProject.execute(
|
|
|
2294
2367
|
|
|
2295
2368
|
result.state #=> "interrupted"
|
|
2296
2369
|
result.status #=> "failed"
|
|
2297
|
-
result.reason #=> "Invalid
|
|
2370
|
+
result.reason #=> "Invalid"
|
|
2298
2371
|
result.metadata #=> {
|
|
2299
|
-
#
|
|
2300
|
-
#
|
|
2301
|
-
#
|
|
2302
|
-
#
|
|
2303
|
-
#
|
|
2304
|
-
#
|
|
2372
|
+
# errors: {
|
|
2373
|
+
# full_message: "project_name is too short (minimum is 3 characters). budget must be greater than 1000. priority is not included in the list. contact_email is invalid.",
|
|
2374
|
+
# messages: {
|
|
2375
|
+
# project_name: ["is too short (minimum is 3 characters)"],
|
|
2376
|
+
# budget: ["must be greater than 1000"],
|
|
2377
|
+
# priority: ["is not included in the list"],
|
|
2378
|
+
# contact_email: ["is invalid"]
|
|
2379
|
+
# }
|
|
2305
2380
|
# }
|
|
2306
2381
|
# }
|
|
2307
2382
|
```
|
|
@@ -2391,6 +2466,80 @@ end
|
|
|
2391
2466
|
|
|
2392
2467
|
---
|
|
2393
2468
|
|
|
2469
|
+
url: https://github.com/drexed/cmdx/blob/main/docs/attributes/transformations.md
|
|
2470
|
+
---
|
|
2471
|
+
|
|
2472
|
+
# Attributes - Transformations
|
|
2473
|
+
|
|
2474
|
+
Transformations allow you to modify attribute values after they are derived and coerced from their source but before any validations. This enables data normalization, formatting, and conditional processing within the attribute pipeline.
|
|
2475
|
+
|
|
2476
|
+
## Declarations
|
|
2477
|
+
|
|
2478
|
+
### Symbol References
|
|
2479
|
+
|
|
2480
|
+
Reference instance methods by symbol for dynamic value transformations:
|
|
2481
|
+
|
|
2482
|
+
```ruby
|
|
2483
|
+
class ProcessAnalytics < CMDx::Task
|
|
2484
|
+
attribute :options, transform: :compact_blank
|
|
2485
|
+
end
|
|
2486
|
+
```
|
|
2487
|
+
|
|
2488
|
+
### Proc or Lambda
|
|
2489
|
+
|
|
2490
|
+
Use anonymous functions for dynamic value transformations:
|
|
2491
|
+
|
|
2492
|
+
```ruby
|
|
2493
|
+
class CacheContent < CMDx::Task
|
|
2494
|
+
# Proc
|
|
2495
|
+
attribute :expire_hours, transform: proc { |v| v * 2 }
|
|
2496
|
+
|
|
2497
|
+
# Lambda
|
|
2498
|
+
attribute :compression, transform: ->(v) { v.to_s.upcase.strip[0..2] }
|
|
2499
|
+
end
|
|
2500
|
+
```
|
|
2501
|
+
|
|
2502
|
+
### Class or Module
|
|
2503
|
+
|
|
2504
|
+
Use any object that responds to `call` for reusable transformation logic:
|
|
2505
|
+
|
|
2506
|
+
```ruby
|
|
2507
|
+
class EmailNormalizer
|
|
2508
|
+
def call(value)
|
|
2509
|
+
value.to_s.downcase.strip
|
|
2510
|
+
end
|
|
2511
|
+
end
|
|
2512
|
+
|
|
2513
|
+
class ProcessContacts < CMDx::Task
|
|
2514
|
+
# Class or Module
|
|
2515
|
+
attribute :email, transform: EmailNormalizer
|
|
2516
|
+
|
|
2517
|
+
# Instance
|
|
2518
|
+
attribute :email, transform: EmailNormalizer.new
|
|
2519
|
+
end
|
|
2520
|
+
```
|
|
2521
|
+
|
|
2522
|
+
## Validations
|
|
2523
|
+
|
|
2524
|
+
Transformed values are subject to the same validation rules as untransformed values, ensuring consistency and catching configuration errors early.
|
|
2525
|
+
|
|
2526
|
+
```ruby
|
|
2527
|
+
class ScheduleBackup < CMDx::Task
|
|
2528
|
+
# Coercions
|
|
2529
|
+
attribute :retention_days, type: :integer, transform: proc { |v| v.clamp(1, 5) }
|
|
2530
|
+
|
|
2531
|
+
# Validations
|
|
2532
|
+
optional :frequency, transform: :downcase, inclusion: { in: %w[hourly daily weekly monthly] }
|
|
2533
|
+
end
|
|
2534
|
+
```
|
|
2535
|
+
|
|
2536
|
+
---
|
|
2537
|
+
|
|
2538
|
+
- **Prev:** [Attributes - Defaults](defaults.md)
|
|
2539
|
+
- **Next:** [Callbacks](../callbacks.md)
|
|
2540
|
+
|
|
2541
|
+
---
|
|
2542
|
+
|
|
2394
2543
|
url: https://github.com/drexed/cmdx/blob/main/docs/callbacks.md
|
|
2395
2544
|
---
|
|
2396
2545
|
|
|
@@ -2459,7 +2608,7 @@ Use anonymous functions for inline callback logic:
|
|
|
2459
2608
|
```ruby
|
|
2460
2609
|
class ProcessBooking < CMDx::Task
|
|
2461
2610
|
# Proc
|
|
2462
|
-
on_interrupted proc {
|
|
2611
|
+
on_interrupted proc { ReservationSystem.pause! }
|
|
2463
2612
|
|
|
2464
2613
|
# Lambda
|
|
2465
2614
|
on_complete -> { ReservationSystem.resume! }
|
|
@@ -2506,10 +2655,10 @@ class ProcessBooking < CMDx::Task
|
|
|
2506
2655
|
before_execution :notify_guest, if: :messaging_enabled?, unless: :messaging_blocked?
|
|
2507
2656
|
|
|
2508
2657
|
# Proc
|
|
2509
|
-
on_failure :increment_failure, if: ->
|
|
2658
|
+
on_failure :increment_failure, if: -> { Rails.env.production? && self.class.name.include?("Legacy") }
|
|
2510
2659
|
|
|
2511
2660
|
# Lambda
|
|
2512
|
-
on_success :ping_housekeeping, if: proc {
|
|
2661
|
+
on_success :ping_housekeeping, if: proc { context.rooms_need_cleaning? }
|
|
2513
2662
|
|
|
2514
2663
|
# Class or Module
|
|
2515
2664
|
on_complete :send_confirmation, unless: MessagingPermissionCheck
|
|
@@ -2524,7 +2673,7 @@ class ProcessBooking < CMDx::Task
|
|
|
2524
2673
|
private
|
|
2525
2674
|
|
|
2526
2675
|
def messaging_enabled?
|
|
2527
|
-
context.guest.messaging_preference
|
|
2676
|
+
context.guest.messaging_preference == true
|
|
2528
2677
|
end
|
|
2529
2678
|
|
|
2530
2679
|
def messaging_blocked?
|
|
@@ -3048,7 +3197,7 @@ result = ProcessOldData.execute
|
|
|
3048
3197
|
result.successful? #=> true
|
|
3049
3198
|
|
|
3050
3199
|
# Ruby warning appears in stderr:
|
|
3051
|
-
# [ProcessOldData] DEPRECATED: migrate to replacement or discontinue use
|
|
3200
|
+
# [ProcessOldData] DEPRECATED: migrate to a replacement or discontinue use
|
|
3052
3201
|
```
|
|
3053
3202
|
|
|
3054
3203
|
## Declarations
|
|
@@ -3199,10 +3348,10 @@ class OnboardingWorkflow < CMDx::Task
|
|
|
3199
3348
|
task SendWelcomeEmail, if: :email_configured?, unless: :email_disabled?
|
|
3200
3349
|
|
|
3201
3350
|
# Proc
|
|
3202
|
-
task SendWelcomeEmail, if: ->
|
|
3351
|
+
task SendWelcomeEmail, if: -> { Rails.env.production? && self.class.name.include?("Premium") }
|
|
3203
3352
|
|
|
3204
3353
|
# Lambda
|
|
3205
|
-
task SendWelcomeEmail, if: proc {
|
|
3354
|
+
task SendWelcomeEmail, if: proc { context.features_enabled? }
|
|
3206
3355
|
|
|
3207
3356
|
# Class or Module
|
|
3208
3357
|
task SendWelcomeEmail, unless: ContentAccessCheck
|
|
@@ -3216,7 +3365,7 @@ class OnboardingWorkflow < CMDx::Task
|
|
|
3216
3365
|
private
|
|
3217
3366
|
|
|
3218
3367
|
def email_configured?
|
|
3219
|
-
context.user.email_address
|
|
3368
|
+
context.user.email_address == true
|
|
3220
3369
|
end
|
|
3221
3370
|
|
|
3222
3371
|
def email_disabled?
|
|
@@ -3508,33 +3657,9 @@ class ConfigureCompany < CMDx::Task
|
|
|
3508
3657
|
end
|
|
3509
3658
|
```
|
|
3510
3659
|
|
|
3511
|
-
##
|
|
3512
|
-
|
|
3513
|
-
Automatically tag SQL queries for better debugging:
|
|
3514
|
-
|
|
3515
|
-
```ruby
|
|
3516
|
-
# config/application.rb
|
|
3517
|
-
config.active_record.query_log_tags_enabled = true
|
|
3518
|
-
config.active_record.query_log_tags << :cmdx_task_class
|
|
3519
|
-
config.active_record.query_log_tags << :cmdx_chain_id
|
|
3660
|
+
## Advance Examples
|
|
3520
3661
|
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
before_execution :set_execution_context
|
|
3524
|
-
|
|
3525
|
-
private
|
|
3526
|
-
|
|
3527
|
-
def set_execution_context
|
|
3528
|
-
# NOTE: This could easily be made into a middleware
|
|
3529
|
-
ActiveSupport::ExecutionContext.set(
|
|
3530
|
-
cmdx_task_class: self.class.name,
|
|
3531
|
-
cmdx_chain_id: chain.id
|
|
3532
|
-
)
|
|
3533
|
-
end
|
|
3534
|
-
end
|
|
3535
|
-
|
|
3536
|
-
# SQL queries will now include comments like:
|
|
3537
|
-
# /*cmdx_task_class:ExportReportTask,cmdx_chain_id:018c2b95-b764-7615*/ SELECT * FROM reports WHERE id = 1
|
|
3538
|
-
```
|
|
3662
|
+
- [Active Record Query Tagging](https://github.com/drexed/cmdx/blob/main/examples/active_record_query_tagging.md)
|
|
3663
|
+
- [Paper Trail Whatdunnit](https://github.com/drexed/cmdx/blob/main/examples/paper_trail_whatdunnit.md)
|
|
3539
3664
|
|
|
3540
3665
|
---
|