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
deregistermethod. -
.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
registermethod. -
.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
#workand marks the Result asfailed. -
#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
#workand marks the Result asskipped. -
#success!(reason = nil, **sigdata) ⇒ void
Halts
#workearly 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.
444 445 446 447 448 449 450 451 |
# File 'lib/cmdx/task.rb', line 444 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.
436 437 438 |
# File 'lib/cmdx/task.rb', line 436 def context @context end |
#errors ⇒ Object (readonly)
Returns the value of attribute errors.
436 437 438 |
# File 'lib/cmdx/task.rb', line 436 def errors @errors end |
#metadata ⇒ Object (readonly)
Returns the value of attribute metadata.
436 437 438 |
# File 'lib/cmdx/task.rb', line 436 def @metadata end |
#tid ⇒ Object (readonly)
Returns the value of attribute tid.
436 437 438 |
# File 'lib/cmdx/task.rb', line 436 def tid @tid end |
Class Method Details
.call {|result| ... } ⇒ Result, Object
Executes the task. Never raises on failure; inspect the returned Result instead.
389 390 391 |
# File 'lib/cmdx/task.rb', line 389 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.
386 387 388 |
# File 'lib/cmdx/task.rb', line 386 def execute(context = EMPTY_HASH, &) new(context).execute(strict: false, &) end |
.execute!(context = EMPTY_HASH) {|result| ... } ⇒ Result, Object Also known as: call!
398 399 400 |
# File 'lib/cmdx/task.rb', line 398 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.
282 283 284 285 286 287 288 289 290 291 292 293 |
# File 'lib/cmdx/task.rb', line 282 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.
343 344 345 |
# File 'lib/cmdx/task.rb', line 343 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.
315 316 317 |
# File 'lib/cmdx/task.rb', line 315 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.
356 357 358 359 360 361 362 363 364 365 366 367 |
# File 'lib/cmdx/task.rb', line 356 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.
371 372 373 |
# File 'lib/cmdx/task.rb', line 371 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.
338 339 340 |
# File 'lib/cmdx/task.rb', line 338 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".
376 377 378 |
# File 'lib/cmdx/task.rb', line 376 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.
461 462 463 464 |
# File 'lib/cmdx/task.rb', line 461 def execute(strict: false) result = Runtime.execute(self, strict:) block_given? ? yield(result) : result end |
#fail!(reason = nil, **sigdata) ⇒ void
530 531 532 533 534 535 536 537 538 539 540 |
# File 'lib/cmdx/task.rb', line 530 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.
468 469 470 |
# File 'lib/cmdx/task.rb', line 468 def logger @logger ||= LoggerProxy.logger(self) end |
#skip!(reason = nil, **sigdata) ⇒ void
510 511 512 513 514 515 516 517 518 519 520 |
# File 'lib/cmdx/task.rb', line 510 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.
491 492 493 494 495 496 497 498 499 500 501 |
# File 'lib/cmdx/task.rb', line 491 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?
552 553 554 555 556 557 558 559 560 561 562 563 564 |
# File 'lib/cmdx/task.rb', line 552 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.
477 478 479 480 481 482 |
# File 'lib/cmdx/task.rb', line 477 def work raise ImplementationError, <<~MSG.chomp #{self.class} must implement #work. See https://drexed.github.io/cmdx/basics/setup/#structure MSG end |