cmdx 1.12.0 → 1.14.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.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +88 -71
  3. data/LICENSE.txt +3 -20
  4. data/README.md +8 -7
  5. data/lib/cmdx/attribute.rb +21 -5
  6. data/lib/cmdx/chain.rb +18 -4
  7. data/lib/cmdx/context.rb +18 -0
  8. data/lib/cmdx/executor.rb +35 -30
  9. data/lib/cmdx/result.rb +45 -2
  10. data/lib/cmdx/task.rb +22 -1
  11. data/lib/cmdx/version.rb +1 -1
  12. data/mkdocs.yml +67 -37
  13. metadata +3 -57
  14. data/.cursor/prompts/docs.md +0 -12
  15. data/.cursor/prompts/llms.md +0 -8
  16. data/.cursor/prompts/rspec.md +0 -24
  17. data/.cursor/prompts/yardoc.md +0 -15
  18. data/.cursor/rules/cursor-instructions.mdc +0 -68
  19. data/.irbrc +0 -18
  20. data/.rspec +0 -4
  21. data/.rubocop.yml +0 -95
  22. data/.ruby-version +0 -1
  23. data/.yard-lint.yml +0 -174
  24. data/.yardopts +0 -7
  25. data/docs/.DS_Store +0 -0
  26. data/docs/assets/favicon.ico +0 -0
  27. data/docs/assets/favicon.svg +0 -1
  28. data/docs/attributes/coercions.md +0 -155
  29. data/docs/attributes/defaults.md +0 -77
  30. data/docs/attributes/definitions.md +0 -283
  31. data/docs/attributes/naming.md +0 -68
  32. data/docs/attributes/transformations.md +0 -63
  33. data/docs/attributes/validations.md +0 -336
  34. data/docs/basics/chain.md +0 -108
  35. data/docs/basics/context.md +0 -121
  36. data/docs/basics/execution.md +0 -96
  37. data/docs/basics/setup.md +0 -84
  38. data/docs/callbacks.md +0 -157
  39. data/docs/configuration.md +0 -314
  40. data/docs/deprecation.md +0 -145
  41. data/docs/getting_started.md +0 -126
  42. data/docs/index.md +0 -134
  43. data/docs/internationalization.md +0 -126
  44. data/docs/interruptions/exceptions.md +0 -52
  45. data/docs/interruptions/faults.md +0 -169
  46. data/docs/interruptions/halt.md +0 -216
  47. data/docs/logging.md +0 -94
  48. data/docs/middlewares.md +0 -191
  49. data/docs/outcomes/result.md +0 -194
  50. data/docs/outcomes/states.md +0 -66
  51. data/docs/outcomes/statuses.md +0 -65
  52. data/docs/retries.md +0 -121
  53. data/docs/stylesheets/extra.css +0 -42
  54. data/docs/tips_and_tricks.md +0 -157
  55. data/docs/workflows.md +0 -226
  56. data/examples/active_record_database_transaction.md +0 -27
  57. data/examples/active_record_query_tagging.md +0 -46
  58. data/examples/flipper_feature_flags.md +0 -50
  59. data/examples/paper_trail_whatdunnit.md +0 -39
  60. data/examples/redis_idempotency.md +0 -71
  61. data/examples/sentry_error_tracking.md +0 -46
  62. data/examples/sidekiq_async_execution.md +0 -29
  63. data/examples/stoplight_circuit_breaker.md +0 -36
  64. data/src/cmdx-dark-logo.png +0 -0
  65. data/src/cmdx-favicon.svg +0 -1
  66. data/src/cmdx-light-logo.png +0 -0
  67. data/src/cmdx-logo.svg +0 -1
@@ -1,194 +0,0 @@
1
- # Outcomes - Result
2
-
3
- Results are your window into task execution. They expose everything: outcome, state, timing, context, and metadata.
4
-
5
- ## Result Attributes
6
-
7
- Access essential execution information:
8
-
9
- !!! warning "Important"
10
-
11
- Results are immutable after execution completes.
12
-
13
- ```ruby
14
- result = BuildApplication.execute(version: "1.2.3")
15
-
16
- # Object data
17
- result.task #=> <BuildApplication>
18
- result.context #=> <CMDx::Context>
19
- result.chain #=> <CMDx::Chain>
20
-
21
- # Execution data
22
- result.state #=> "interrupted"
23
- result.status #=> "failed"
24
-
25
- # Fault data
26
- result.reason #=> "Build tool not found"
27
- result.cause #=> <CMDx::FailFault>
28
- result.metadata #=> { error_code: "BUILD_TOOL.NOT_FOUND" }
29
- ```
30
-
31
- ## Lifecycle Information
32
-
33
- Check execution state and status with predicate methods:
34
-
35
- ```ruby
36
- result = BuildApplication.execute(version: "1.2.3")
37
-
38
- # State predicates (execution lifecycle)
39
- result.complete? #=> true (successful completion)
40
- result.interrupted? #=> false (no interruption)
41
- result.executed? #=> true (execution finished)
42
-
43
- # Status predicates (execution outcome)
44
- result.success? #=> true (successful execution)
45
- result.failed? #=> false (no failure)
46
- result.skipped? #=> false (not skipped)
47
-
48
- # Outcome categorization
49
- result.good? #=> true (success or skipped)
50
- result.bad? #=> false (skipped or failed)
51
- ```
52
-
53
- ## Outcome Analysis
54
-
55
- Get a unified outcome string combining state and status:
56
-
57
- ```ruby
58
- result = BuildApplication.execute(version: "1.2.3")
59
-
60
- result.outcome #=> "success" (state and status)
61
- ```
62
-
63
- ## Chain Analysis
64
-
65
- Trace fault origins and propagation:
66
-
67
- ```ruby
68
- result = DeploymentWorkflow.execute(app_name: "webapp")
69
-
70
- if result.failed?
71
- # Find the original cause of failure
72
- if original_failure = result.caused_failure
73
- puts "Root cause: #{original_failure.task.class.name}"
74
- puts "Reason: #{original_failure.reason}"
75
- end
76
-
77
- # Find what threw the failure to this result
78
- if throwing_task = result.threw_failure
79
- puts "Failure source: #{throwing_task.task.class.name}"
80
- puts "Reason: #{throwing_task.reason}"
81
- end
82
-
83
- # Failure classification
84
- result.caused_failure? #=> true if this result was the original cause
85
- result.threw_failure? #=> true if this result threw a failure
86
- result.thrown_failure? #=> true if this result received a thrown failure
87
- end
88
- ```
89
-
90
- ## Index and Position
91
-
92
- Results track their position within execution chains:
93
-
94
- ```ruby
95
- result = BuildApplication.execute(version: "1.2.3")
96
-
97
- # Position in execution sequence
98
- result.index #=> 0 (first task in chain)
99
-
100
- # Access via chain
101
- result.chain.results[result.index] == result #=> true
102
- ```
103
-
104
- ## Block Yield
105
-
106
- Execute code with direct result access:
107
-
108
- ```ruby
109
- BuildApplication.execute(version: "1.2.3") do |result|
110
- if result.success?
111
- notify_deployment_ready(result)
112
- elsif result.failed?
113
- handle_build_failure(result)
114
- else
115
- log_skip_reason(result)
116
- end
117
- end
118
- ```
119
-
120
- ## Handlers
121
-
122
- Handle outcomes with functional-style methods. Handlers return the result for chaining:
123
-
124
- ```ruby
125
- result = BuildApplication.execute(version: "1.2.3")
126
-
127
- # Status-based handlers
128
- result
129
- .on(:success) { |result| notify_deployment_ready(result) }
130
- .on(:failed) { |result| handle_build_failure(result) }
131
- .on(:skipped) { |result| log_skip_reason(result) }
132
-
133
- # State-based handlers
134
- result
135
- .on(:complete) { |result| update_build_status(result) }
136
- .on(:interrupted) { |result| cleanup_partial_artifacts(result) }
137
- .on(:executed) { |result| alert_operations_team(result) } #=> .on(:complete, :interrupted)
138
-
139
- # Outcome-based handlers
140
- result
141
- .on(:good) { |result| increment_success_counter(result) } #=> .on(:success, :skipped)
142
- .on(:bad) { |result| alert_operations_team(result) } #=> .on(:failed, :skipped)
143
- ```
144
-
145
- ## Pattern Matching
146
-
147
- Use Ruby 3.0+ pattern matching for elegant outcome handling:
148
-
149
- !!! warning "Important"
150
-
151
- Pattern matching works with both array and hash deconstruction.
152
-
153
- ### Array Pattern
154
-
155
- ```ruby
156
- result = BuildApplication.execute(version: "1.2.3")
157
-
158
- case result
159
- in ["complete", "success"]
160
- redirect_to build_success_page
161
- in ["interrupted", "failed"]
162
- retry_build_with_backoff(result)
163
- in ["interrupted", "skipped"]
164
- log_skip_and_continue
165
- end
166
- ```
167
-
168
- ### Hash Pattern
169
-
170
- ```ruby
171
- result = BuildApplication.execute(version: "1.2.3")
172
-
173
- case result
174
- in { state: "complete", status: "success" }
175
- celebrate_build_success
176
- in { status: "failed", metadata: { retryable: true } }
177
- schedule_build_retry(result)
178
- in { bad: true, metadata: { reason: String => reason } }
179
- escalate_build_error("Build failed: #{reason}")
180
- end
181
- ```
182
-
183
- ### Pattern Guards
184
-
185
- ```ruby
186
- case result
187
- in { status: "failed", metadata: { attempts: n } } if n < 3
188
- retry_build_with_delay(result, n * 2)
189
- in { status: "failed", metadata: { attempts: n } } if n >= 3
190
- mark_build_permanently_failed(result)
191
- in { runtime: time } if time > performance_threshold
192
- investigate_build_performance(result)
193
- end
194
- ```
@@ -1,66 +0,0 @@
1
- # Outcomes - States
2
-
3
- States track where a task is in its execution lifecycle—from creation through completion or interruption.
4
-
5
- ## Definitions
6
-
7
- | State | Description |
8
- | ----- | ----------- |
9
- | `initialized` | Task created but execution not yet started. Default state for new tasks. |
10
- | `executing` | Task is actively running its business logic. Transient state during execution. |
11
- | `complete` | Task finished execution successfully without any interruption or halt. |
12
- | `interrupted` | Task execution was stopped due to a fault, exception, or explicit halt. |
13
-
14
- State-Status combinations:
15
-
16
- | State | Status | Meaning |
17
- | ----- | ------ | ------- |
18
- | `initialized` | `success` | Task created, not yet executed |
19
- | `executing` | `success` | Task currently running |
20
- | `complete` | `success` | Task finished successfully |
21
- | `complete` | `skipped` | Task finished by skipping execution |
22
- | `interrupted` | `failed` | Task stopped due to failure |
23
- | `interrupted` | `skipped` | Task stopped by skip condition |
24
-
25
- ## Transitions
26
-
27
- !!! danger "Caution"
28
-
29
- States are managed automatically—never modify them manually.
30
-
31
- ```ruby
32
- # Valid state transition flow
33
- initialized → executing → complete (successful execution)
34
- initialized → executing → interrupted (skipped/failed execution)
35
- ```
36
-
37
- ## Predicates
38
-
39
- Use state predicates to check the current execution lifecycle:
40
-
41
- ```ruby
42
- result = ProcessVideoUpload.execute
43
-
44
- # Individual state checks
45
- result.initialized? #=> false (after execution)
46
- result.executing? #=> false (after execution)
47
- result.complete? #=> true (successful completion)
48
- result.interrupted? #=> false (no interruption)
49
-
50
- # State categorization
51
- result.executed? #=> true (complete OR interrupted)
52
- ```
53
-
54
- ## Handlers
55
-
56
- Handle lifecycle events with state-based handlers. Use `on(:executed)` for cleanup that runs regardless of outcome:
57
-
58
- ```ruby
59
- result = ProcessVideoUpload.execute
60
-
61
- # Individual state handlers
62
- result
63
- .on(:complete) { |result| send_upload_notification(result) }
64
- .on(:interrupted) { |result| cleanup_temp_files(result) }
65
- .on(:executed) { |result| log_upload_metrics(result) } #=> .on(:complete, :interrupted)
66
- ```
@@ -1,65 +0,0 @@
1
- # Outcomes - Statuses
2
-
3
- Statuses represent the business outcome—did the task succeed, skip, or fail? This differs from state, which tracks the execution lifecycle.
4
-
5
- ## Definitions
6
-
7
- | Status | Description |
8
- | ------ | ----------- |
9
- | `success` | Task execution completed successfully with expected business outcome. Default status for all tasks. |
10
- | `skipped` | Task intentionally stopped execution because conditions weren't met or continuation was unnecessary. |
11
- | `failed` | Task stopped execution due to business rule violations, validation errors, or exceptions. |
12
-
13
- ## Transitions
14
-
15
- !!! warning "Important"
16
-
17
- Status transitions are final and unidirectional. Once skipped or failed, tasks can't return to success.
18
-
19
- ```ruby
20
- # Valid status transitions
21
- success → skipped # via skip!
22
- success → failed # via fail! or exception
23
-
24
- # Invalid transitions (will raise errors)
25
- skipped → success # ❌ Cannot transition
26
- skipped → failed # ❌ Cannot transition
27
- failed → success # ❌ Cannot transition
28
- failed → skipped # ❌ Cannot transition
29
- ```
30
-
31
- ## Predicates
32
-
33
- Use status predicates to check execution outcomes:
34
-
35
- ```ruby
36
- result = ProcessNotification.execute
37
-
38
- # Individual status checks
39
- result.success? #=> true/false
40
- result.skipped? #=> true/false
41
- result.failed? #=> true/false
42
-
43
- # Outcome categorization
44
- result.good? #=> true if success OR skipped
45
- result.bad? #=> true if skipped OR failed (not success)
46
- ```
47
-
48
- ## Handlers
49
-
50
- Branch business logic with status-based handlers. Use `on(:good)` and `on(:bad)` for success/skip vs failed outcomes:
51
-
52
- ```ruby
53
- result = ProcessNotification.execute
54
-
55
- # Individual status handlers
56
- result
57
- .on(:success) { |result| mark_notification_sent(result) }
58
- .on(:skipped) { |result| log_notification_skipped(result) }
59
- .on(:failed){ |result| queue_retry_notification(result) }
60
-
61
- # Outcome-based handlers
62
- result
63
- .on(:good) { |result| update_message_stats(result) } #=> .on(:success, :skipped)
64
- .on(:bad) { |result| track_delivery_failure(result) } #=> .on(:failed, :skipped)
65
- ```
data/docs/retries.md DELETED
@@ -1,121 +0,0 @@
1
- # Retries
2
-
3
- CMDx provides automatic retry functionality for tasks that encounter transient failures. This is essential for handling temporary issues like network timeouts, rate limits, or database locks without manual intervention.
4
-
5
- ## Basic Usage
6
-
7
- Configure retries upto n attempts without any delay.
8
-
9
- ```ruby
10
- class FetchExternalData < CMDx::Task
11
- settings retries: 3
12
-
13
- def work
14
- response = HTTParty.get("https://api.example.com/data")
15
- context.data = response.parsed_response
16
- end
17
- end
18
- ```
19
-
20
- When an exception occurs during execution, CMDx automatically retries up to the configured limit. Each retry attempt is logged at the `warn` level with retry metadata. If all retries are exhausted, the task fails with the original exception.
21
-
22
- ## Selective Retries
23
-
24
- By default, CMDx retries on `StandardError` and its subclasses. Narrow this to specific exception types:
25
-
26
- ```ruby
27
- class ProcessPayment < CMDx::Task
28
- settings retries: 5, retry_on: [Stripe::RateLimitError, Net::ReadTimeout]
29
-
30
- def work
31
- # Your logic here...
32
- end
33
- end
34
- ```
35
-
36
- !!! warning "Important"
37
-
38
- Only exceptions matching the `retry_on` configuration will trigger retries. Uncaught exceptions immediately fail the task.
39
-
40
- ## Retry Jitter
41
-
42
- Add delays between retry attempts to avoid overwhelming external services or to implement exponential backoff strategies.
43
-
44
- ### Fixed Value
45
-
46
- Use a numeric value to calculate linear delay (`jitter * current_retry`):
47
-
48
- ```ruby
49
- class ImportRecords < CMDx::Task
50
- settings retries: 3, retry_jitter: 0.5
51
-
52
- def work
53
- # Delays: 0s, 0.5s (retry 1), 1.0s (retry 2), 1.5s (retry 3)
54
- context.records = ExternalAPI.fetch_records
55
- end
56
- end
57
- ```
58
-
59
- ### Symbol References
60
-
61
- Define an instance method for custom delay logic:
62
-
63
- ```ruby
64
- class SyncInventory < CMDx::Task
65
- settings retries: 5, retry_jitter: :exponential_backoff
66
-
67
- def work
68
- context.inventory = InventoryAPI.sync
69
- end
70
-
71
- private
72
-
73
- def exponential_backoff(current_retry)
74
- 2 ** current_retry # 2s, 4s, 8s, 16s, 32s
75
- end
76
- end
77
- ```
78
-
79
- ### Proc or Lambda
80
-
81
- Pass a proc for inline delay calculations:
82
-
83
- ```ruby
84
- class PollJobStatus < CMDx::Task
85
- # Proc
86
- settings retries: 10, retry_jitter: proc { |retry_count| [retry_count * 0.5, 5.0].min }
87
-
88
- # Lambda
89
- settings retries: 10, retry_jitter: ->(retry_count) { [retry_count * 0.5, 5.0].min }
90
-
91
- def work
92
- # Delays: 0.5s, 1.0s, 1.5s, 2.0s, 2.5s, 3.0s, 3.5s, 4.0s, 4.5s, 5.0s (capped)
93
- context.status = JobAPI.check_status(context.job_id)
94
- end
95
- end
96
- ```
97
-
98
- ### Class or Module
99
-
100
- Implement reusable delay logic in dedicated modules and classes:
101
-
102
- ```ruby
103
- class ExponentialBackoff
104
- def call(task, retry_count)
105
- base_delay = task.context.base_delay || 1.0
106
- [base_delay * (2 ** retry_count), 60.0].min
107
- end
108
- end
109
-
110
- class FetchUserProfile < CMDx::Task
111
- # Class or Module
112
- settings retries: 4, retry_jitter: ExponentialBackoff
113
-
114
- # Instance
115
- settings retries: 4, retry_jitter: ExponentialBackoff.new
116
-
117
- def work
118
- # Your logic here...
119
- end
120
- end
121
- ```
@@ -1,42 +0,0 @@
1
- :root > * {
2
- /* Primary color shades */
3
- --md-primary-fg-color: #fe1817;
4
- --md-primary-fg-color--light: #fe1817;
5
- --md-primary-fg-color--dark: #fe1817;
6
-
7
- /* Accent color shades */
8
- --md-accent-fg-color: hsla(#{hex2hsl(#fe1817)}, 1);
9
- --md-accent-fg-color--transparent: hsla(#{hex2hsl(#fe1817)}, 0.1);
10
- }
11
-
12
- /* GitHub High Contrast Light syntax highlighting */
13
- [data-md-color-scheme="default"] {
14
- --md-code-hl-color: #0e1116;
15
- --md-code-hl-keyword-color: #a0095d;
16
- --md-code-hl-string-color: #024c1a;
17
- --md-code-hl-name-color: #622cbc;
18
- --md-code-hl-function-color: #622cbc;
19
- --md-code-hl-number-color: #0349b4;
20
- --md-code-hl-constant-color: #702c00;
21
- --md-code-hl-comment-color: #66707b;
22
- --md-code-hl-operator-color: #a0095d;
23
- --md-code-hl-punctuation-color:#0e1116;
24
- --md-code-hl-variable-color: #702c00;
25
- --md-code-hl-generic-color: #622cbc;
26
- }
27
-
28
- /* GitHub High Contrast Dark syntax highlighting */
29
- [data-md-color-scheme="slate"] {
30
- --md-code-hl-color: #f0f3f6;
31
- --md-code-hl-keyword-color: #ff9492;
32
- --md-code-hl-string-color: #addcff;
33
- --md-code-hl-name-color: #dbb7ff;
34
- --md-code-hl-function-color: #dbb7ff;
35
- --md-code-hl-number-color: #91cbff;
36
- --md-code-hl-constant-color: #ffb757;
37
- --md-code-hl-comment-color: #9ea7b3;
38
- --md-code-hl-operator-color: #ff9492;
39
- --md-code-hl-punctuation-color:#f0f3f6;
40
- --md-code-hl-variable-color: #ffb757;
41
- --md-code-hl-generic-color: #dbb7ff;
42
- }
@@ -1,157 +0,0 @@
1
- # Tips and Tricks
2
-
3
- Best practices, patterns, and techniques to build maintainable CMDx applications.
4
-
5
- ## Project Organization
6
-
7
- ### Directory Structure
8
-
9
- Create a well-organized command structure for maintainable applications:
10
-
11
- ```text
12
- /app/
13
- └── /tasks/
14
- ├── /invoices/
15
- │ ├── calculate_tax.rb
16
- │ ├── validate_invoice.rb
17
- │ ├── send_invoice.rb
18
- │ └── process_invoice.rb # workflow
19
- ├── /reports/
20
- │ ├── generate_pdf.rb
21
- │ ├── compile_data.rb
22
- │ ├── export_csv.rb
23
- │ └── create_reports.rb # workflow
24
- ├── application_task.rb # base class
25
- ├── authenticate_session.rb
26
- └── activate_account.rb
27
- ```
28
-
29
- ### Naming Conventions
30
-
31
- Follow consistent naming patterns for clarity and maintainability:
32
-
33
- ```ruby
34
- # Verb + Noun
35
- class ExportData < CMDx::Task; end
36
- class CompressFile < CMDx::Task; end
37
- class ValidateSchema < CMDx::Task; end
38
-
39
- # Use present tense verbs for actions
40
- class GenerateToken < CMDx::Task; end # ✓ Good
41
- class GeneratingToken < CMDx::Task; end # ❌ Avoid
42
- class TokenGeneration < CMDx::Task; end # ❌ Avoid
43
- ```
44
-
45
- ### Story Telling
46
-
47
- Break down complex logic into descriptive methods that read like a narrative:
48
-
49
- ```ruby
50
- class ProcessOrder < CMDx::Task
51
- def work
52
- charge_payment_method
53
- assign_to_warehouse
54
- send_notification
55
- end
56
-
57
- private
58
-
59
- def charge_payment_method
60
- order.primary_payment_method.charge!
61
- end
62
-
63
- def assign_to_warehouse
64
- order.ready_for_shipping!
65
- end
66
-
67
- def send_notification
68
- if order.products_out_of_stock?
69
- OrderMailer.pending(order).deliver
70
- else
71
- OrderMailer.preparing(order).deliver
72
- end
73
- end
74
- end
75
- ```
76
-
77
- ### Style Guide
78
-
79
- Follow this order for consistent, readable tasks:
80
-
81
- ```ruby
82
- class ExportReport < CMDx::Task
83
-
84
- # 1. Register functions
85
- register :middleware, CMDx::Middlewares::Correlate
86
- register :validator, :format, FormatValidator
87
-
88
- # 2. Define callbacks
89
- before_execution :find_report
90
- on_complete :track_export_metrics, if: ->(task) { Current.tenant.analytics? }
91
-
92
- # 3. Declare attributes
93
- attributes :user_id
94
- required :report_id
95
- optional :format_type
96
-
97
- # 4. Define work method
98
- def work
99
- report.compile!
100
- report.export!
101
-
102
- context.exported_at = Time.now
103
- end
104
-
105
- # TIP: Favor private business logic to reduce the surface of the public API.
106
- private
107
-
108
- # 5. Build helper functions
109
- def find_report
110
- @report ||= Report.find(report_id)
111
- end
112
-
113
- def track_export_metrics
114
- Analytics.increment(:report_exported)
115
- end
116
-
117
- end
118
- ```
119
-
120
- ## Attribute Options
121
-
122
- Use `with_options` to reduce duplication:
123
-
124
- ```ruby
125
- class ConfigureCompany < CMDx::Task
126
- # Apply common options to multiple attributes
127
- with_options(type: :string, presence: true) do
128
- attributes :website, format: { with: URI::DEFAULT_PARSER.make_regexp(%w[http https]) }
129
- required :company_name, :industry
130
- optional :description, format: { with: /\A[\w\s\-\.,!?]+\z/ }
131
- end
132
-
133
- # Nested attributes with shared prefix
134
- required :headquarters do
135
- with_options(prefix: :hq_) do
136
- attributes :street, :city, :zip_code, type: :string
137
- required :country, type: :string, inclusion: { in: VALID_COUNTRIES }
138
- optional :region, type: :string
139
- end
140
- end
141
-
142
- def work
143
- # Your logic here...
144
- end
145
- end
146
- ```
147
-
148
- ## Useful Examples
149
-
150
- - [Active Record Database Transaction](https://github.com/drexed/cmdx/blob/main/examples/active_record_database_transaction.md)
151
- - [Active Record Query Tagging](https://github.com/drexed/cmdx/blob/main/examples/active_record_query_tagging.md)
152
- - [Flipper Feature Flags](https://github.com/drexed/cmdx/blob/main/examples/flipper_feature_flags.md)
153
- - [Paper Trail Whatdunnit](https://github.com/drexed/cmdx/blob/main/examples/paper_trail_whatdunnit.md)
154
- - [Redis Idempotency](https://github.com/drexed/cmdx/blob/main/examples/redis_idempotency.md)
155
- - [Sentry Error Tracking](https://github.com/drexed/cmdx/blob/main/examples/sentry_error_tracking.md)
156
- - [Sidekiq Async Execution](https://github.com/drexed/cmdx/blob/main/examples/sidekiq_async_execution.md)
157
- - [Stoplight Circuit Breaker](https://github.com/drexed/cmdx/blob/main/examples/stoplight_circuit_breaker.md)