Class: CMDx::Task
- Inherits:
-
Object
- Object
- CMDx::Task
- Defined in:
- lib/cmdx/task.rb
Overview
Base class for all units of work. Subclasses override ‘#work` and declare their contract via `required`, `optional`, `output`, `callbacks`, `retry_on`, `deprecation`, and `settings`. Invoked via Task.execute (safe) or Task.execute! (strict, raises on failure).
Inheritance: every registry accessor (middlewares, callbacks, coercions, validators, executors, mergers, telemetry, inputs, outputs) lazily clones from the superclass’s registry (or the global configuration at the top of the hierarchy), so subclasses extend rather than replace.
Instance Attribute Summary collapse
-
#context ⇒ Object
(also: #ctx)
readonly
Returns the value of attribute context.
-
#errors ⇒ Object
readonly
Returns the value of attribute errors.
-
#metadata ⇒ Object
readonly
Returns the value of attribute metadata.
-
#tid ⇒ Object
readonly
Returns the value of attribute tid.
Class Method Summary collapse
-
.call {|result| ... } ⇒ Result, Object
Executes the task.
-
.callbacks ⇒ Callbacks
Cloned from superclass/configuration on first call.
-
.coercions ⇒ Coercions
Cloned from superclass/configuration on first call.
-
.deprecation(value = nil, **options, &block) { ... } ⇒ Deprecation?
Reads, sets, or inherits the task class’s Deprecation.
-
.deprecators ⇒ Deprecators
Cloned from superclass/configuration on first call.
-
.deregister(type) ⇒ Object
Dispatches to the appropriate registry’s ‘deregister` method.
-
.execute(context = EMPTY_HASH) {|result| ... } ⇒ Result, Object
Executes the task.
-
.execute!(context = EMPTY_HASH) {|result| ... } ⇒ Result, Object
(also: call!)
Strict execution.
-
.executors ⇒ Executors
Cloned from superclass/configuration on first call.
-
.inputs(*names, **options) { ... } ⇒ Inputs
(also: input)
Reads, or declares more, inputs.
-
.inputs_schema ⇒ Hash{Symbol => Hash}
Serialized input definitions.
-
.mergers ⇒ Mergers
Cloned from superclass/configuration on first call.
-
.middlewares ⇒ Middlewares
Cloned from superclass/configuration on first call.
-
.optional(*names, **options) { ... } ⇒ Object
Declares optional inputs (shorthand for ‘inputs …, required: false`).
-
.outputs(*keys, **options) ⇒ Outputs
(also: output)
Reads, or declares more, outputs.
-
.outputs_schema ⇒ Hash{Symbol => Hash}
Serialized output definitions.
-
.register(type) ⇒ Object
Dispatches to the appropriate registry’s ‘register` method.
-
.required(*names, **options) { ... } ⇒ Object
Declares required inputs (shorthand for ‘inputs …, required: true`).
-
.retriers ⇒ Retriers
Cloned from superclass/configuration on first call.
-
.retry_on(*exceptions, **options) {|attempt, delay| ... } ⇒ Retry
Declares exceptions to retry on.
-
.settings(options = EMPTY_HASH) ⇒ Settings
Reads or extends this class’s Settings.
-
.telemetry ⇒ Telemetry
Cloned from superclass/configuration on first call.
-
.type ⇒ String
‘“Workflow”` when the class includes Workflow, else `“Task”`.
-
.validators ⇒ Validators
Cloned from superclass/configuration on first call.
Instance Method Summary collapse
-
#execute(strict: false) {|result| ... } ⇒ Result, Object
(also: #call)
Executes this task instance through Runtime.
-
#fail!(reason = nil, **sigdata) ⇒ void
Halts ‘#work` and marks the Result as `failed`.
-
#initialize(context = EMPTY_HASH) ⇒ Task
constructor
A new instance of Task.
-
#logger ⇒ Logger
A logger tailored to this task’s settings.
-
#skip!(reason = nil, **sigdata) ⇒ void
Halts ‘#work` and marks the Result as `skipped`.
-
#success!(reason = nil, **sigdata) ⇒ void
Halts ‘#work` early with a successful outcome.
- #throw!(other, **sigdata) ⇒ void?
-
#work ⇒ void
abstract
The task’s core logic.
Constructor Details
#initialize(context = EMPTY_HASH) ⇒ Task
The built Context inherits ‘strict` mode from Settings#strict_context (falling back to Configuration#strict_context), so dynamic reads for unknown keys raise `NoMethodError` instead of returning `nil`.
Returns a new instance of Task.
441 442 443 444 445 446 447 448 |
# File 'lib/cmdx/task.rb', line 441 def initialize(context = EMPTY_HASH) @metadata = {} @tid = SecureRandom.uuid_v7 @errors = Errors.new @context = Context.build(context).tap do |c| c.strict = self.class.settings.strict_context end end |
Instance Attribute Details
#context ⇒ Object (readonly) Also known as: ctx
Returns the value of attribute context.
433 434 435 |
# File 'lib/cmdx/task.rb', line 433 def context @context end |
#errors ⇒ Object (readonly)
Returns the value of attribute errors.
433 434 435 |
# File 'lib/cmdx/task.rb', line 433 def errors @errors end |
#metadata ⇒ Object (readonly)
Returns the value of attribute metadata.
433 434 435 |
# File 'lib/cmdx/task.rb', line 433 def @metadata end |
#tid ⇒ Object (readonly)
Returns the value of attribute tid.
433 434 435 |
# File 'lib/cmdx/task.rb', line 433 def tid @tid end |
Class Method Details
.call {|result| ... } ⇒ Result, Object
Executes the task. Never raises on failure; inspect the returned Result instead.
386 387 388 |
# File 'lib/cmdx/task.rb', line 386 def execute(context = EMPTY_HASH, &) new(context).execute(strict: false, &) end |
.callbacks ⇒ Callbacks
Returns cloned from superclass/configuration on first call.
84 85 86 87 88 89 90 91 |
# File 'lib/cmdx/task.rb', line 84 def callbacks @callbacks ||= if superclass.respond_to?(:callbacks) superclass.callbacks.dup else CMDx.configuration.callbacks.dup end end |
.coercions ⇒ Coercions
Returns cloned from superclass/configuration on first call.
110 111 112 113 114 115 116 117 |
# File 'lib/cmdx/task.rb', line 110 def coercions @coercions ||= if superclass.respond_to?(:coercions) superclass.coercions.dup else CMDx.configuration.coercions.dup end end |
.deprecation(value = nil, **options, &block) { ... } ⇒ Deprecation?
Reads, sets, or inherits the task class’s Deprecation. With a ‘value` or block, replaces any current deprecation. Otherwise returns the locally defined one, or the superclass’s.
252 253 254 255 256 257 258 259 260 |
# File 'lib/cmdx/task.rb', line 252 def deprecation(value = nil, **, &block) if value || block @deprecation = Deprecation.new(value || block, ) elsif defined?(@deprecation) @deprecation elsif superclass.respond_to?(:deprecation) superclass.deprecation end end |
.deprecators ⇒ Deprecators
Returns cloned from superclass/configuration on first call.
160 161 162 163 164 165 166 167 |
# File 'lib/cmdx/task.rb', line 160 def deprecators @deprecators ||= if superclass.respond_to?(:deprecators) superclass.deprecators.dup else CMDx.configuration.deprecators.dup end end |
.deregister(type) ⇒ Object
Dispatches to the appropriate registry’s ‘deregister` method.
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 |
# File 'lib/cmdx/task.rb', line 210 def deregister(type, ...) case type when :middleware middlewares.deregister(...) when :callback callbacks.deregister(...) when :coercion coercions.deregister(...) when :validator validators.deregister(...) when :executor executors.deregister(...) when :merger mergers.deregister(...) when :retrier retriers.deregister(...) when :deprecator deprecators.deregister(...) when :input inputs.deregister(self, ...) when :output outputs.deregister(...) else raise ArgumentError, <<~MSG.chomp unknown registry type #{type.inspect}; expected one of [:middleware, :callback, :coercion, :validator, :executor, :merger, :retrier, :deprecator, :input, :output]. See https://drexed.github.io/cmdx/configuration/#registrations-register-deregister MSG end end |
.execute(context = EMPTY_HASH) {|result| ... } ⇒ Result, Object
Executes the task. Never raises on failure; inspect the returned Result instead.
383 384 385 |
# File 'lib/cmdx/task.rb', line 383 def execute(context = EMPTY_HASH, &) new(context).execute(strict: false, &) end |
.execute!(context = EMPTY_HASH) {|result| ... } ⇒ Result, Object Also known as: call!
395 396 397 |
# File 'lib/cmdx/task.rb', line 395 def execute!(context = EMPTY_HASH, &) new(context).execute(strict: true, &) end |
.executors ⇒ Executors
Returns cloned from superclass/configuration on first call.
130 131 132 133 134 135 136 137 |
# File 'lib/cmdx/task.rb', line 130 def executors @executors ||= if superclass.respond_to?(:executors) superclass.executors.dup else CMDx.configuration.executors.dup end end |
.inputs(*names, **options) { ... } ⇒ Inputs Also known as: input
Reads, or declares more, inputs. With no names, returns the registry; with names, registers them and defines accessors.
281 282 283 284 285 286 287 288 289 290 291 292 |
# File 'lib/cmdx/task.rb', line 281 def inputs(*names, **, &) @inputs ||= if superclass.respond_to?(:inputs) superclass.inputs.dup else Inputs.new end return @inputs if names.empty? @inputs.register(self, *names, **, &) end |
.inputs_schema ⇒ Hash{Symbol => Hash}
Returns serialized input definitions.
340 341 342 |
# File 'lib/cmdx/task.rb', line 340 def inputs_schema inputs.registry.transform_values(&:to_h) end |
.mergers ⇒ Mergers
Returns cloned from superclass/configuration on first call.
140 141 142 143 144 145 146 147 |
# File 'lib/cmdx/task.rb', line 140 def mergers @mergers ||= if superclass.respond_to?(:mergers) superclass.mergers.dup else CMDx.configuration.mergers.dup end end |
.middlewares ⇒ Middlewares
Returns cloned from superclass/configuration on first call.
74 75 76 77 78 79 80 81 |
# File 'lib/cmdx/task.rb', line 74 def middlewares @middlewares ||= if superclass.respond_to?(:middlewares) superclass.middlewares.dup else CMDx.configuration.middlewares.dup end end |
.optional(*names, **options) { ... } ⇒ Object
Declares optional inputs (shorthand for ‘inputs …, required: false`). An explicit `required:` in `options` is ignored — use inputs when you need to set the flag dynamically.
313 314 315 |
# File 'lib/cmdx/task.rb', line 313 def optional(*names, **, &) register(:input, *names, **, required: false, &) end |
.outputs(*keys, **options) ⇒ Outputs Also known as: output
Reads, or declares more, outputs. With no keys, returns the registry.
353 354 355 356 357 358 359 360 361 362 363 364 |
# File 'lib/cmdx/task.rb', line 353 def outputs(*keys, **) @outputs ||= if superclass.respond_to?(:outputs) superclass.outputs.dup else Outputs.new end return @outputs if keys.empty? @outputs.register(*keys, **) end |
.outputs_schema ⇒ Hash{Symbol => Hash}
Returns serialized output definitions.
368 369 370 |
# File 'lib/cmdx/task.rb', line 368 def outputs_schema outputs.registry.transform_values(&:to_h) end |
.register(type) ⇒ Object
Dispatches to the appropriate registry’s ‘register` method.
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 |
# File 'lib/cmdx/task.rb', line 174 def register(type, ...) case type when :middleware middlewares.register(...) when :callback callbacks.register(...) when :coercion coercions.register(...) when :validator validators.register(...) when :executor executors.register(...) when :merger mergers.register(...) when :retrier retriers.register(...) when :deprecator deprecators.register(...) when :input inputs.register(self, ...) when :output outputs.register(...) else raise ArgumentError, <<~MSG.chomp unknown registry type #{type.inspect}; expected one of [:middleware, :callback, :coercion, :validator, :executor, :merger, :retrier, :deprecator, :input, :output]. See https://drexed.github.io/cmdx/configuration/#registrations-register-deregister MSG end end |
.required(*names, **options) { ... } ⇒ Object
Declares required inputs (shorthand for ‘inputs …, required: true`). An explicit `required:` in `options` is ignored — use inputs when you need to set the flag dynamically.
335 336 337 |
# File 'lib/cmdx/task.rb', line 335 def required(*names, **, &) register(:input, *names, **, required: true, &) end |
.retriers ⇒ Retriers
Returns cloned from superclass/configuration on first call.
150 151 152 153 154 155 156 157 |
# File 'lib/cmdx/task.rb', line 150 def retriers @retriers ||= if superclass.respond_to?(:retriers) superclass.retriers.dup else CMDx.configuration.retriers.dup end end |
.retry_on(*exceptions, **options) {|attempt, delay| ... } ⇒ Retry
Declares exceptions to retry on. Builds on the superclass’s ‘Retry`. Passing no exceptions returns the current (possibly inherited) Retry.
33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/cmdx/task.rb', line 33 def retry_on(*exceptions, **, &) @retry_on ||= if superclass.respond_to?(:retry_on) superclass.retry_on.build(exceptions, , &) else Retry.new(exceptions, , &) end return @retry_on if exceptions.empty? @retry_on = @retry_on.build(exceptions, , &) end |
.settings(options = EMPTY_HASH) ⇒ Settings
Reads or extends this class’s Settings. Inherits from the superclass.
60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/cmdx/task.rb', line 60 def settings( = EMPTY_HASH) @settings ||= if superclass.respond_to?(:settings) superclass.settings.build() else Settings.new() end return @settings if .empty? @settings = @settings.build() end |
.telemetry ⇒ Telemetry
Returns cloned from superclass/configuration on first call.
100 101 102 103 104 105 106 107 |
# File 'lib/cmdx/task.rb', line 100 def telemetry @telemetry ||= if superclass.respond_to?(:telemetry) superclass.telemetry.dup else CMDx.configuration.telemetry.dup end end |
.type ⇒ String
Returns ‘“Workflow”` when the class includes Workflow, else `“Task”`.
373 374 375 |
# File 'lib/cmdx/task.rb', line 373 def type @type ||= include?(Workflow) ? "Workflow" : "Task" end |
.validators ⇒ Validators
Returns cloned from superclass/configuration on first call.
120 121 122 123 124 125 126 127 |
# File 'lib/cmdx/task.rb', line 120 def validators @validators ||= if superclass.respond_to?(:validators) superclass.validators.dup else CMDx.configuration.validators.dup end end |
Instance Method Details
#execute(strict: false) {|result| ... } ⇒ Result, Object Also known as: call
Executes this task instance through Runtime.
458 459 460 461 |
# File 'lib/cmdx/task.rb', line 458 def execute(strict: false) result = Runtime.execute(self, strict:) block_given? ? yield(result) : result end |
#fail!(reason = nil, **sigdata) ⇒ void
527 528 529 530 531 532 533 534 535 536 537 |
# File 'lib/cmdx/task.rb', line 527 def fail!(reason = nil, **sigdata) if frozen? raise FrozenTaskError, <<~MSG.chomp cannot call :fail! on #{self.class}; the task has already been executed and frozen. See https://drexed.github.io/cmdx/outcomes/result/#lifecycle-predicates MSG end .merge!(sigdata) unless sigdata.empty? throw(Signal::TAG, Signal.failed(reason, metadata:, backtrace: caller_locations(1))) end |
#logger ⇒ Logger
Returns a logger tailored to this task’s settings.
465 466 467 |
# File 'lib/cmdx/task.rb', line 465 def logger @logger ||= LoggerProxy.logger(self) end |
#skip!(reason = nil, **sigdata) ⇒ void
507 508 509 510 511 512 513 514 515 516 517 |
# File 'lib/cmdx/task.rb', line 507 def skip!(reason = nil, **sigdata) if frozen? raise FrozenTaskError, <<~MSG.chomp cannot call :skip! on #{self.class}; the task has already been executed and frozen. See https://drexed.github.io/cmdx/outcomes/result/#lifecycle-predicates MSG end .merge!(sigdata) unless sigdata.empty? throw(Signal::TAG, Signal.skipped(reason, metadata:)) end |
#success!(reason = nil, **sigdata) ⇒ void
This method returns an undefined value.
Halts ‘#work` early with a successful outcome. Any `sigdata` is merged onto #metadata before the signal is thrown.
488 489 490 491 492 493 494 495 496 497 498 |
# File 'lib/cmdx/task.rb', line 488 def success!(reason = nil, **sigdata) if frozen? raise FrozenTaskError, <<~MSG.chomp cannot call :success! on #{self.class}; the task has already been executed and frozen. See https://drexed.github.io/cmdx/outcomes/result/#lifecycle-predicates MSG end .merge!(sigdata) unless sigdata.empty? throw(Signal::TAG, Signal.success(reason, metadata:)) end |
#throw!(other, **sigdata) ⇒ void?
549 550 551 552 553 554 555 556 557 558 559 560 561 |
# File 'lib/cmdx/task.rb', line 549 def throw!(other, **sigdata) if frozen? raise FrozenTaskError, <<~MSG.chomp cannot call :throw! on #{self.class}; the task has already been executed and frozen. See https://drexed.github.io/cmdx/outcomes/result/#lifecycle-predicates MSG end return unless other.failed? .merge!(sigdata) unless sigdata.empty? throw(Signal::TAG, Signal.echoed(other, metadata:, backtrace: caller_locations(1))) end |
#work ⇒ void
This method returns an undefined value.
The task’s core logic. Subclasses must override.
474 475 476 477 478 479 |
# File 'lib/cmdx/task.rb', line 474 def work raise ImplementationError, <<~MSG.chomp #{self.class} must implement #work. See https://drexed.github.io/cmdx/basics/setup/#structure MSG end |