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,68 +0,0 @@
1
- # Attributes - Naming
2
-
3
- Customize accessor method names to avoid conflicts and improve clarity. Affixing changes only the generated methods—not the original attribute names.
4
-
5
- !!! note
6
-
7
- Use naming when attributes conflict with existing methods or need better clarity in your code.
8
-
9
- ## Prefix
10
-
11
- Adds a prefix to the generated accessor method name.
12
-
13
- ```ruby
14
- class GenerateReport < CMDx::Task
15
- # Dynamic from attribute source
16
- attribute :template, prefix: true
17
-
18
- # Static
19
- attribute :format, prefix: "report_"
20
-
21
- def work
22
- context_template #=> "monthly_sales"
23
- report_format #=> "pdf"
24
- end
25
- end
26
-
27
- # Attributes passed as original attribute names
28
- GenerateReport.execute(template: "monthly_sales", format: "pdf")
29
- ```
30
-
31
- ## Suffix
32
-
33
- Adds a suffix to the generated accessor method name.
34
-
35
- ```ruby
36
- class DeployApplication < CMDx::Task
37
- # Dynamic from attribute source
38
- attribute :branch, suffix: true
39
-
40
- # Static
41
- attribute :version, suffix: "_tag"
42
-
43
- def work
44
- branch_context #=> "main"
45
- version_tag #=> "v1.2.3"
46
- end
47
- end
48
-
49
- # Attributes passed as original attribute names
50
- DeployApplication.execute(branch: "main", version: "v1.2.3")
51
- ```
52
-
53
- ## As
54
-
55
- Completely renames the generated accessor method.
56
-
57
- ```ruby
58
- class ScheduleMaintenance < CMDx::Task
59
- attribute :scheduled_at, as: :when
60
-
61
- def work
62
- when #=> <DateTime>
63
- end
64
- end
65
-
66
- # Attributes passed as original attribute names
67
- ScheduleMaintenance.execute(scheduled_at: DateTime.new(2024, 12, 15, 2, 0, 0))
68
- ```
@@ -1,63 +0,0 @@
1
- # Attributes - Transformations
2
-
3
- Modify attribute values after coercion but before validation. Perfect for normalization, formatting, and data cleanup.
4
-
5
- ## Declarations
6
-
7
- ### Symbol References
8
-
9
- Reference instance methods by symbol for dynamic value transformations:
10
-
11
- ```ruby
12
- class ProcessAnalytics < CMDx::Task
13
- attribute :options, transform: :compact_blank
14
- end
15
- ```
16
-
17
- ### Proc or Lambda
18
-
19
- Use anonymous functions for dynamic value transformations:
20
-
21
- ```ruby
22
- class CacheContent < CMDx::Task
23
- # Proc
24
- attribute :expire_hours, transform: proc { |v| v * 2 }
25
-
26
- # Lambda
27
- attribute :compression, transform: ->(v) { v.to_s.upcase.strip[0..2] }
28
- end
29
- ```
30
-
31
- ### Class or Module
32
-
33
- Use any object that responds to `call` for reusable transformation logic:
34
-
35
- ```ruby
36
- class EmailNormalizer
37
- def call(value)
38
- value.to_s.downcase.strip
39
- end
40
- end
41
-
42
- class ProcessContacts < CMDx::Task
43
- # Class or Module
44
- attribute :email, transform: EmailNormalizer
45
-
46
- # Instance
47
- attribute :email, transform: EmailNormalizer.new
48
- end
49
- ```
50
-
51
- ## Validations
52
-
53
- Validations run on transformed values, ensuring data consistency:
54
-
55
- ```ruby
56
- class ScheduleBackup < CMDx::Task
57
- # Coercions
58
- attribute :retention_days, type: :integer, transform: proc { |v| v.clamp(1, 5) }
59
-
60
- # Validations
61
- optional :frequency, transform: :downcase, inclusion: { in: %w[hourly daily weekly monthly] }
62
- end
63
- ```
@@ -1,336 +0,0 @@
1
- # Attributes - Validations
2
-
3
- Ensure inputs meet requirements before execution. Validations run after coercions, giving you declarative data integrity checks.
4
-
5
- See [Global Configuration](../getting_started.md#validators) for custom validator setup.
6
-
7
- ## Usage
8
-
9
- Define validation rules on attributes to enforce data requirements:
10
-
11
- ```ruby
12
- class ProcessSubscription < CMDx::Task
13
- # Required field with presence validation
14
- attribute :user_id, presence: true
15
-
16
- # String with length constraints
17
- optional :preferences, length: { minimum: 10, maximum: 500 }
18
-
19
- # Numeric range validation
20
- required :tier_level, inclusion: { in: 1..5 }
21
-
22
- # Format validation for email
23
- attribute :contact_email, format: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
24
-
25
- def work
26
- user_id #=> "98765"
27
- preferences #=> "Send weekly digest emails"
28
- tier_level #=> 3
29
- contact_email #=> "user@company.com"
30
- end
31
- end
32
-
33
- ProcessSubscription.execute(
34
- user_id: "98765",
35
- preferences: "Send weekly digest emails",
36
- tier_level: 3,
37
- contact_email: "user@company.com"
38
- )
39
- ```
40
-
41
- !!! tip
42
-
43
- Validations run after coercions, so you can validate the final coerced values rather than raw input.
44
-
45
- ## Built-in Validators
46
-
47
- ### Common Options
48
-
49
- ```ruby
50
- class ProcessProduct < CMDx::Task
51
- # Allow nil
52
- attribute :tier_level, inclusion: {
53
- in: 1..5,
54
- allow_nil: true
55
- }
56
-
57
- # Conditionals
58
- optional :contact_email, format: {
59
- with: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i,
60
- if: ->(value) { value.includes?("@") }
61
- }
62
- required :status, exclusion: {
63
- in: %w[recalled archived],
64
- unless: :product_sunsetted?
65
- }
66
-
67
- # Custom message
68
- attribute :title, length: {
69
- within: 5..100,
70
- message: "must be in optimal size"
71
- }
72
-
73
- def work
74
- # Your logic here...
75
- end
76
-
77
- private
78
-
79
- def product_defunct?(value)
80
- context.company.out_of_business? || value == "deprecated"
81
- end
82
- end
83
- ```
84
-
85
- This list of options is available to all validators:
86
-
87
- | Option | Description |
88
- |--------|-------------|
89
- | `:allow_nil` | Skip validation when value is `nil` |
90
- | `:if` | Symbol, proc, lambda, or callable determining when to validate |
91
- | `:unless` | Symbol, proc, lambda, or callable determining when to skip validation |
92
- | `:message` | Custom error message for validation failures |
93
-
94
- ### Exclusion
95
-
96
- ```ruby
97
- class ProcessProduct < CMDx::Task
98
- attribute :status, exclusion: { in: %w[recalled archived] }
99
-
100
- def work
101
- # Your logic here...
102
- end
103
- end
104
- ```
105
-
106
- | Options | Description |
107
- |---------|-------------|
108
- | `:in` | The collection of forbidden values or range |
109
- | `:within` | Alias for :in option |
110
- | `:of_message` | Custom message for discrete value exclusions |
111
- | `:in_message` | Custom message for range-based exclusions |
112
- | `:within_message` | Alias for :in_message option |
113
-
114
- ### Format
115
-
116
- ```ruby
117
- class ProcessProduct < CMDx::Task
118
- attribute :sku, format: /\A[A-Z]{3}-[0-9]{4}\z/
119
-
120
- attribute :sku, format: { with: /\A[A-Z]{3}-[0-9]{4}\z/ }
121
-
122
- def work
123
- # Your logic here...
124
- end
125
- end
126
- ```
127
-
128
- | Options | Description |
129
- |---------|-------------|
130
- | `regexp` | Alias for :with option |
131
- | `:with` | Regex pattern that the value must match |
132
- | `:without` | Regex pattern that the value must not match |
133
-
134
- ### Inclusion
135
-
136
- ```ruby
137
- class ProcessProduct < CMDx::Task
138
- attribute :availability, inclusion: { in: %w[available limited] }
139
-
140
- def work
141
- # Your logic here...
142
- end
143
- end
144
- ```
145
-
146
- | Options | Description |
147
- |---------|-------------|
148
- | `:in` | The collection of allowed values or range |
149
- | `:within` | Alias for :in option |
150
- | `:of_message` | Custom message for discrete value inclusions |
151
- | `:in_message` | Custom message for range-based inclusions |
152
- | `:within_message` | Alias for :in_message option |
153
-
154
- ### Length
155
-
156
- ```ruby
157
- class CreateBlogPost < CMDx::Task
158
- attribute :title, length: { within: 5..100 }
159
-
160
- def work
161
- # Your logic here...
162
- end
163
- end
164
- ```
165
-
166
- | Options | Description |
167
- |---------|-------------|
168
- | `:within` | Range that the length must fall within (inclusive) |
169
- | `:not_within` | Range that the length must not fall within |
170
- | `:in` | Alias for :within |
171
- | `:not_in` | Range that the length must not fall within |
172
- | `:min` | Minimum allowed length |
173
- | `:max` | Maximum allowed length |
174
- | `:is` | Exact required length |
175
- | `:is_not` | Length that is not allowed |
176
- | `:within_message` | Custom message for within/range validations |
177
- | `:in_message` | Custom message for :in validation |
178
- | `:not_within_message` | Custom message for not_within validation |
179
- | `:not_in_message` | Custom message for not_in validation |
180
- | `:min_message` | Custom message for minimum length validation |
181
- | `:max_message` | Custom message for maximum length validation |
182
- | `:is_message` | Custom message for exact length validation |
183
- | `:is_not_message` | Custom message for is_not validation |
184
-
185
- ### Numeric
186
-
187
- ```ruby
188
- class CreateBlogPost < CMDx::Task
189
- attribute :word_count, numeric: { min: 100 }
190
-
191
- def work
192
- # Your logic here...
193
- end
194
- end
195
- ```
196
-
197
- | Options | Description |
198
- |---------|-------------|
199
- | `:within` | Range that the value must fall within (inclusive) |
200
- | `:not_within` | Range that the value must not fall within |
201
- | `:in` | Alias for :within option |
202
- | `:not_in` | Alias for :not_within option |
203
- | `:min` | Minimum allowed value (inclusive, >=) |
204
- | `:max` | Maximum allowed value (inclusive, <=) |
205
- | `:is` | Exact value that must match |
206
- | `:is_not` | Value that must not match |
207
- | `:within_message` | Custom message for range validations |
208
- | `:not_within_message` | Custom message for exclusion validations |
209
- | `:min_message` | Custom message for minimum validation |
210
- | `:max_message` | Custom message for maximum validation |
211
- | `:is_message` | Custom message for exact match validation |
212
- | `:is_not_message` | Custom message for exclusion validation |
213
-
214
- ### Presence
215
-
216
- ```ruby
217
- class CreateBlogPost < CMDx::Task
218
- attribute :content, presence: true
219
-
220
- attribute :content, presence: { message: "cannot be blank" }
221
-
222
- def work
223
- # Your logic here...
224
- end
225
- end
226
- ```
227
-
228
- | Options | Description |
229
- |---------|-------------|
230
- | `true` | Ensures value is not nil, empty string, or whitespace |
231
-
232
- ## Declarations
233
-
234
- !!! warning "Important"
235
-
236
- Custom validators must raise `CMDx::ValidationError` with a descriptive message.
237
-
238
- ### Proc or Lambda
239
-
240
- Use anonymous functions for simple validation logic:
241
-
242
- ```ruby
243
- class SetupApplication < CMDx::Task
244
- # Proc
245
- register :validator, :api_key, proc do |value, options = {}|
246
- unless value.match?(/\A[a-zA-Z0-9]{32}\z/)
247
- raise CMDx::ValidationError, "invalid API key format"
248
- end
249
- end
250
-
251
- # Lambda
252
- register :validator, :api_key, ->(value, options = {}) {
253
- unless value.match?(/\A[a-zA-Z0-9]{32}\z/)
254
- raise CMDx::ValidationError, "invalid API key format"
255
- end
256
- }
257
- end
258
- ```
259
-
260
- ### Class or Module
261
-
262
- Register custom validation logic for specialized requirements:
263
-
264
- ```ruby
265
- class ApiKeyValidator
266
- def self.call(value, options = {})
267
- unless value.match?(/\A[a-zA-Z0-9]{32}\z/)
268
- raise CMDx::ValidationError, "invalid API key format"
269
- end
270
- end
271
- end
272
-
273
- class SetupApplication < CMDx::Task
274
- register :validator, :api_key, ApiKeyValidator
275
-
276
- attribute :access_key, api_key: true
277
- end
278
- ```
279
-
280
- ## Removals
281
-
282
- Remove unwanted validators:
283
-
284
- !!! warning
285
-
286
- Each `deregister` call removes one validator. Use multiple calls for batch removals.
287
-
288
- ```ruby
289
- class SetupApplication < CMDx::Task
290
- deregister :validator, :api_key
291
- end
292
- ```
293
-
294
- ## Error Handling
295
-
296
- Validation failures provide detailed, structured error messages:
297
-
298
- ```ruby
299
- class CreateProject < CMDx::Task
300
- attribute :project_name,
301
- presence: true,
302
- length: { minimum: 3, maximum: 50 }
303
- optional :budget,
304
- numeric: { greater_than: 1000, less_than: 1000000 }
305
- required :priority,
306
- inclusion: { in: [:low, :medium, :high] }
307
- attribute :contact_email,
308
- format: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
309
-
310
- def work
311
- # Your logic here...
312
- end
313
- end
314
-
315
- result = CreateProject.execute(
316
- project_name: "AB", # Too short
317
- budget: 500, # Too low
318
- priority: :urgent, # Not in allowed list
319
- contact_email: "invalid-email" # Invalid format
320
- )
321
-
322
- result.state #=> "interrupted"
323
- result.status #=> "failed"
324
- result.reason #=> "Invalid"
325
- result.metadata #=> {
326
- # errors: {
327
- # 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.",
328
- # messages: {
329
- # project_name: ["is too short (minimum is 3 characters)"],
330
- # budget: ["must be greater than 1000"],
331
- # priority: ["is not included in the list"],
332
- # contact_email: ["is invalid"]
333
- # }
334
- # }
335
- # }
336
- ```
data/docs/basics/chain.md DELETED
@@ -1,108 +0,0 @@
1
- # Basics - Chain
2
-
3
- Chains automatically track related task executions within a thread. Think of them as execution traces that help you understand what happened and in what order.
4
-
5
- ## Management
6
-
7
- Each thread maintains its own isolated chain using thread-local storage.
8
-
9
- !!! warning
10
-
11
- Chains are thread-local. Don't share chain references across threads—it causes race conditions.
12
-
13
- ```ruby
14
- # Thread A
15
- Thread.new do
16
- result = ImportDataset.execute(file_path: "/data/batch1.csv")
17
- result.chain.id #=> "018c2b95-b764-7615-a924-cc5b910ed1e5"
18
- end
19
-
20
- # Thread B (completely separate chain)
21
- Thread.new do
22
- result = ImportDataset.execute(file_path: "/data/batch2.csv")
23
- result.chain.id #=> "z3a42b95-c821-7892-b156-dd7c921fe2a3"
24
- end
25
-
26
- # Access current thread's chain
27
- CMDx::Chain.current #=> Returns current chain or nil
28
- CMDx::Chain.clear #=> Clears current thread's chain
29
- ```
30
-
31
- ## Links
32
-
33
- Tasks automatically create or join the current thread's chain:
34
-
35
- !!! warning "Important"
36
-
37
- Chain management is automatic—no manual lifecycle handling needed.
38
-
39
- ```ruby
40
- class ImportDataset < CMDx::Task
41
- def work
42
- # First task creates new chain
43
- result1 = ValidateHeaders.execute(file_path: context.file_path)
44
- result1.chain.id #=> "018c2b95-b764-7615-a924-cc5b910ed1e5"
45
- result1.chain.results.size #=> 1
46
-
47
- # Second task joins existing chain
48
- result2 = SendNotification.execute(to: "admin@company.com")
49
- result2.chain.id == result1.chain.id #=> true
50
- result2.chain.results.size #=> 2
51
-
52
- # Both results reference the same chain
53
- result1.chain.results == result2.chain.results #=> true
54
- end
55
- end
56
- ```
57
-
58
- ## Inheritance
59
-
60
- Subtasks automatically inherit the current thread's chain, building a unified execution trail:
61
-
62
- ```ruby
63
- class ImportDataset < CMDx::Task
64
- def work
65
- context.dataset = Dataset.find(context.dataset_id)
66
-
67
- # Subtasks automatically inherit current chain
68
- ValidateSchema.execute
69
- TransformData.execute!(context)
70
- SaveToDatabase.execute(dataset_id: context.dataset_id)
71
- end
72
- end
73
-
74
- result = ImportDataset.execute(dataset_id: 456)
75
- chain = result.chain
76
-
77
- # All tasks share the same chain
78
- chain.results.size #=> 4 (main task + 3 subtasks)
79
- chain.results.map { |r| r.task.class }
80
- #=> [ImportDataset, ValidateSchema, TransformData, SaveToDatabase]
81
- ```
82
-
83
- ## Structure
84
-
85
- Chains expose comprehensive execution information:
86
-
87
- !!! warning "Important"
88
-
89
- Chain state reflects the first (outermost) task result. Subtasks maintain their own states.
90
-
91
- ```ruby
92
- result = ImportDataset.execute(dataset_id: 456)
93
- chain = result.chain
94
-
95
- # Chain identification
96
- chain.id #=> "018c2b95-b764-7615-a924-cc5b910ed1e5"
97
- chain.results #=> Array of all results in execution order
98
-
99
- # State delegation (from first/outer-most result)
100
- chain.state #=> "complete"
101
- chain.status #=> "success"
102
- chain.outcome #=> "success"
103
-
104
- # Access individual results
105
- chain.results.each_with_index do |result, index|
106
- puts "#{index}: #{result.task.class} - #{result.status}"
107
- end
108
- ```
@@ -1,121 +0,0 @@
1
- # Basics - Context
2
-
3
- Context is your data container for inputs, intermediate values, and outputs. It makes sharing data between tasks effortless.
4
-
5
- ## Assigning Data
6
-
7
- Context automatically captures all task inputs, normalizing keys to symbols:
8
-
9
- ```ruby
10
- # Direct execution
11
- CalculateShipping.execute(weight: 2.5, destination: "CA")
12
-
13
- # Instance creation
14
- CalculateShipping.new(weight: 2.5, "destination" => "CA")
15
- ```
16
-
17
- !!! warning "Important"
18
-
19
- String keys convert to symbols automatically. Prefer symbols for consistency.
20
-
21
- ## Accessing Data
22
-
23
- Access context data using method notation, hash keys, or safe accessors:
24
-
25
- ```ruby
26
- class CalculateShipping < CMDx::Task
27
- def work
28
- # Method style access (preferred)
29
- weight = context.weight
30
- destination = context.destination
31
-
32
- # Hash style access
33
- service_type = context[:service_type]
34
- options = context["options"]
35
-
36
- # Safe access with defaults
37
- rush_delivery = context.fetch!(:rush_delivery, false)
38
- carrier = context.dig(:options, :carrier)
39
-
40
- # Shorter alias
41
- cost = ctx.weight * ctx.rate_per_pound # ctx aliases context
42
- end
43
- end
44
- ```
45
-
46
- !!! warning "Important"
47
-
48
- Undefined attributes return `nil` instead of raising errors—perfect for optional data.
49
-
50
- ## Modifying Context
51
-
52
- Context supports dynamic modification during task execution:
53
-
54
- ```ruby
55
- class CalculateShipping < CMDx::Task
56
- def work
57
- # Direct assignment
58
- context.carrier = Carrier.find_by(code: context.carrier_code)
59
- context.package = Package.new(weight: context.weight)
60
- context.calculated_at = Time.now
61
-
62
- # Hash-style assignment
63
- context[:status] = "calculating"
64
- context["tracking_number"] = "SHIP#{SecureRandom.hex(6)}"
65
-
66
- # Conditional assignment
67
- context.insurance_included ||= false
68
-
69
- # Batch updates
70
- context.merge!(
71
- status: "completed",
72
- shipping_cost: calculate_cost,
73
- estimated_delivery: Time.now + 3.days
74
- )
75
-
76
- # Remove sensitive data
77
- context.delete!(:credit_card_token)
78
- end
79
-
80
- private
81
-
82
- def calculate_cost
83
- base_rate = context.weight * context.rate_per_pound
84
- base_rate + (base_rate * context.tax_percentage)
85
- end
86
- end
87
- ```
88
-
89
- !!! tip
90
-
91
- Use context for both input values and intermediate results. This creates natural data flow through your task execution pipeline.
92
-
93
- ## Data Sharing
94
-
95
- Share context across tasks for seamless data flow:
96
-
97
- ```ruby
98
- # During execution
99
- class CalculateShipping < CMDx::Task
100
- def work
101
- # Validate shipping data
102
- validation_result = ValidateAddress.execute(context)
103
-
104
- # Via context
105
- CalculateInsurance.execute(context)
106
-
107
- # Via result
108
- NotifyShippingCalculated.execute(validation_result)
109
-
110
- # Context now contains accumulated data from all tasks
111
- context.address_validated #=> true (from validation)
112
- context.insurance_calculated #=> true (from insurance)
113
- context.notification_sent #=> true (from notification)
114
- end
115
- end
116
-
117
- # After execution
118
- result = CalculateShipping.execute(destination: "New York, NY")
119
-
120
- CreateShippingLabel.execute(result)
121
- ```