Class: CMDx::Task

Inherits:
Object
  • Object
show all
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, idempotent_by, lock_with, circuit_breaker, rate_limit, 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.

See Also:

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(context = EMPTY_HASH) ⇒ Task

Note:

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.

Parameters:



603
604
605
606
607
608
609
610
# File 'lib/cmdx/task.rb', line 603

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.



595
596
597
# File 'lib/cmdx/task.rb', line 595

def context
  @context
end

#errors ⇒ Object (readonly)

Returns the value of attribute errors.



595
596
597
# File 'lib/cmdx/task.rb', line 595

def errors
  @errors
end

#metadata ⇒ Object (readonly)

Returns the value of attribute metadata.



595
596
597
# File 'lib/cmdx/task.rb', line 595

def 
  @metadata
end

#tid ⇒ Object (readonly)

Returns the value of attribute tid.



595
596
597
# File 'lib/cmdx/task.rb', line 595

def tid
  @tid
end

Class Method Details

.call {|result| ... } ⇒ Result, Object

Executes the task. Never raises on failure; inspect the returned Result instead.

Parameters:

Yield Parameters:

Returns:

  • (Result, Object) —

    the yielded block's value when a block is given



548
549
550
# File 'lib/cmdx/task.rb', line 548

def execute(context = EMPTY_HASH, &)
  new(context).execute(strict: false, &)
end

.callbacks ⇒ Callbacks

Returns cloned from superclass/configuration on first call.

Returns:

  • (Callbacks) —

    cloned from superclass/configuration on first call



229
230
231
232
233
234
235
236
# File 'lib/cmdx/task.rb', line 229

def callbacks
  @callbacks ||=
    if superclass.respond_to?(:callbacks)
      superclass.callbacks.dup
    else
      CMDx.configuration.callbacks.dup
    end
end

.circuit_breaker(key = nil, **options) {|task| ... } ⇒ CircuitBreaker

Declares a shared circuit breaker for the task. Builds on the superclass's CircuitBreaker. Passing no key and no options returns the current (possibly inherited) configuration. Pass false to disable.

Parameters:

  • key (Symbol, String, Proc, #call, false, nil) (defaults to: nil) —

    :class scopes one breaker per task class; other forms resolve a store segment like Idempotency; false disables the breaker on this class

  • options (Hash{Symbol => Object}) —

Options Hash (**options):

  • :threshold (Integer) — default: see {CircuitBreaker#initialize}
  • :cool_off (Integer) — default: see {CircuitBreaker#initialize}
  • :window (Integer) — default: see {CircuitBreaker#initialize}
  • :namespace (String) — default: see {CircuitBreaker#initialize}
  • :on_open (Symbol) — default: see {CircuitBreaker#initialize}
  • :on (Array<Class>) — default: see {CircuitBreaker#initialize}
  • :if (Symbol, Proc, #call) —

    gate (task) before checking

  • :unless (Symbol, Proc, #call) —

    gate (task) before checking

Yield Parameters:

  • task (Task) —

    optional first argument when the block is instance_exec'd (same as callbacks)

Returns:

See Also:



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/cmdx/task.rb', line 138

def circuit_breaker(key = nil, **options, &block)
  @circuit_breaker ||=
    if superclass.respond_to?(:circuit_breaker)
      superclass.circuit_breaker
    else
      CircuitBreaker.disabled
    end

  @circuit_breaker =
    if key == false
      CircuitBreaker.disabled
    else
      @circuit_breaker.build(key || block, options)
    end
end

.coercions ⇒ Coercions

Returns cloned from superclass/configuration on first call.

Returns:

  • (Coercions) —

    cloned from superclass/configuration on first call



255
256
257
258
259
260
261
262
# File 'lib/cmdx/task.rb', line 255

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.

Parameters:

  • value (:log, :warn, :error, Symbol, Proc, #call, nil) (defaults to: nil)
  • block (#call, nil) —

    optional block used as the deprecation callable

  • options (Hash{Symbol => Object}) —

    :if/:unless conditions (see Deprecation#initialize)

Options Hash (**options):

  • :if (Symbol, Proc, #call) — default: see {Deprecation#initialize}
  • :unless (Symbol, Proc, #call) — default: see {Deprecation#initialize}

Yields:

  • optional block used as the deprecation callable

Returns:



411
412
413
414
415
416
417
418
419
# File 'lib/cmdx/task.rb', line 411

def deprecation(value = nil, **options, &block)
  if value || block
    @deprecation = Deprecation.new(value || block, options)
  elsif defined?(@deprecation)
    @deprecation
  elsif superclass.respond_to?(:deprecation)
    superclass.deprecation
  end
end

.deprecators ⇒ Deprecators

Returns cloned from superclass/configuration on first call.

Returns:

  • (Deprecators) —

    cloned from superclass/configuration on first call



315
316
317
318
319
320
321
322
# File 'lib/cmdx/task.rb', line 315

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.

Parameters:

  • type (:middleware, :callback, :coercion, :validator, :executor, :merger, :retrier, :limiter, :deprecator, :input, :output)

Returns:

  • (Object) —

    the registry's self

Raises:

  • (ArgumentError) —

    when type is unknown



367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
# File 'lib/cmdx/task.rb', line 367

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 :limiter
    limiters.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, :limiter, :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.

Parameters:

Yield Parameters:

Returns:

  • (Result, Object) —

    the yielded block's value when a block is given



545
546
547
# File 'lib/cmdx/task.rb', line 545

def execute(context = EMPTY_HASH, &)
  new(context).execute(strict: false, &)
end

.execute!(context = EMPTY_HASH) {|result| ... } ⇒ Result, Object Also known as: call!

Strict execution. Raises Fault (or the underlying exception) on failure; otherwise identical to execute.

Parameters:

Yield Parameters:

Returns:

Raises:

  • (Fault, StandardError) —

    on task failure



557
558
559
# File 'lib/cmdx/task.rb', line 557

def execute!(context = EMPTY_HASH, &)
  new(context).execute(strict: true, &)
end

.executors ⇒ Executors

Returns cloned from superclass/configuration on first call.

Returns:

  • (Executors) —

    cloned from superclass/configuration on first call



275
276
277
278
279
280
281
282
# File 'lib/cmdx/task.rb', line 275

def executors
  @executors ||=
    if superclass.respond_to?(:executors)
      superclass.executors.dup
    else
      CMDx.configuration.executors.dup
    end
end

.idempotent_by(key = nil, **options) {|task| ... } ⇒ Idempotency

Declares a stable idempotency key from context (or a callable). Builds on the superclass's Idempotency. Passing no key and no options returns the current (possibly inherited) configuration. Pass false to disable.

Parameters:

  • key (Symbol, String, Proc, #call, false, nil) (defaults to: nil) —

    context key or resolver; false disables idempotency on this class

  • options (Hash{Symbol => Object}) —

Options Hash (**options):

  • :ttl (Integer) — default: see {Idempotency#initialize}
  • :namespace (String) — default: see {Idempotency#initialize}
  • :on_duplicate (Symbol) — default: see {Idempotency#initialize}
  • :if (Symbol, Proc, #call) —

    gate (task) for claiming

  • :unless (Symbol, Proc, #call) —

    gate (task) for claiming

Yield Parameters:

  • task (Task) —

    optional first argument when the block is instance_exec'd (same as callbacks)

Returns:

See Also:



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/cmdx/task.rb', line 69

def idempotent_by(key = nil, **options, &block)
  @idempotent_by ||=
    if superclass.respond_to?(:idempotent_by)
      superclass.idempotent_by
    else
      Idempotency.disabled
    end

  @idempotent_by =
    if key == false
      Idempotency.disabled
    else
      @idempotent_by.build(key || block, options)
    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.

Parameters:

  • names (Array<Symbol>)
  • options (Hash{Symbol => Object}) —

Options Hash (**options):

  • :description (String) — default: also accepts `:desc`
  • :as (Symbol) —

    overrides the accessor name

  • :prefix (Boolean, String) —

    prefix for the accessor name

  • :suffix (Boolean, String) —

    suffix for the accessor name

  • :source (Symbol, Proc, #call) — default: `:context` —

    where to fetch from

  • :default (Object, Symbol, Proc, #call)
  • :transform (Symbol, Proc, #call) —

    mutator applied after coercion

  • :if (Symbol, Proc, #call)
  • :unless (Symbol, Proc, #call)
  • :required (Boolean)
  • :allow_nil (Boolean) — default: see {Input#allow_nil}
  • :coerce (Object) — default: see {Coercions#extract}
  • :validate (Object) — default: see {Validators#extract}

Yields:

Returns:



441
442
443
444
445
446
447
448
449
450
451
452
# File 'lib/cmdx/task.rb', line 441

def inputs(*names, **options, &)
  @inputs ||=
    if superclass.respond_to?(:inputs)
      superclass.inputs.dup
    else
      Inputs.new
    end

  return @inputs if names.empty?

  @inputs.register(self, *names, **options, &)
end

.inputs_schema ⇒ Hash{Symbol => Hash}

Returns serialized input definitions.

Returns:

  • (Hash{Symbol => Hash}) —

    serialized input definitions



502
503
504
# File 'lib/cmdx/task.rb', line 502

def inputs_schema
  inputs.registry.transform_values(&:to_h)
end

.limiters ⇒ Limiters

Returns cloned from superclass/configuration on first call.

Returns:

  • (Limiters) —

    cloned from superclass/configuration on first call



305
306
307
308
309
310
311
312
# File 'lib/cmdx/task.rb', line 305

def limiters
  @limiters ||=
    if superclass.respond_to?(:limiters)
      superclass.limiters.dup
    else
      CMDx.configuration.limiters.dup
    end
end

.lock_with(key = nil, **options) {|task| ... } ⇒ Lock

Declares a distributed lock from context (or a callable). Builds on the superclass's Lock. Passing no key and no options returns the current (possibly inherited) configuration. Pass false to disable.

Parameters:

  • key (Symbol, String, Proc, #call, false, nil) (defaults to: nil) —

    context key or resolver; false disables locking on this class

  • options (Hash{Symbol => Object}) —

Options Hash (**options):

  • :ttl (Integer) — default: see {Lock#initialize}
  • :namespace (String) — default: see {Lock#initialize}
  • :wait (Numeric, false, nil) — default: see {Lock#initialize}
  • :on_busy (Symbol) — default: see {Lock#initialize}
  • :if (Symbol, Proc, #call) —

    gate (task) for acquiring

  • :unless (Symbol, Proc, #call) —

    gate (task) for acquiring

Yield Parameters:

  • task (Task) —

    optional first argument when the block is instance_exec'd (same as callbacks)

Returns:

See Also:



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/cmdx/task.rb', line 102

def lock_with(key = nil, **options, &block)
  @lock_with ||=
    if superclass.respond_to?(:lock_with)
      superclass.lock_with
    else
      Lock.disabled
    end

  @lock_with =
    if key == false
      Lock.disabled
    else
      @lock_with.build(key || block, options)
    end
end

.mergers ⇒ Mergers

Returns cloned from superclass/configuration on first call.

Returns:

  • (Mergers) —

    cloned from superclass/configuration on first call



285
286
287
288
289
290
291
292
# File 'lib/cmdx/task.rb', line 285

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.

Returns:

  • (Middlewares) —

    cloned from superclass/configuration on first call



219
220
221
222
223
224
225
226
# File 'lib/cmdx/task.rb', line 219

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.

Parameters:

  • names (Array<Symbol>)
  • options (Hash{Symbol => Object}) —

Options Hash (**options):

  • :description (String) — default: also accepts `:desc`
  • :as (Symbol) —

    overrides the accessor name

  • :prefix (Boolean, String) —

    prefix for the accessor name

  • :suffix (Boolean, String) —

    suffix for the accessor name

  • :source (Symbol, Proc, #call) — default: `:context` —

    where to fetch from

  • :default (Object, Symbol, Proc, #call)
  • :transform (Symbol, Proc, #call) —

    mutator applied after coercion

  • :if (Symbol, Proc, #call)
  • :unless (Symbol, Proc, #call)
  • :allow_nil (Boolean) — default: see {Input#allow_nil}
  • :coerce (Object) — default: see {Coercions#extract}
  • :validate (Object) — default: see {Validators#extract}

Yields:



474
475
476
# File 'lib/cmdx/task.rb', line 474

def optional(*names, **options, &)
  register(:input, *names, **options, required: false, &)
end

.outputs(*keys, **options) ⇒ Outputs Also known as: output

Reads, or declares more, outputs. With no keys, returns the registry.

Parameters:

Options Hash (**options):

  • :description (String) — default: also accepts `:desc`
  • :if (Symbol, Proc, #call)
  • :unless (Symbol, Proc, #call)
  • :default (Object, Symbol, Proc, #call)

Returns:



515
516
517
518
519
520
521
522
523
524
525
526
# File 'lib/cmdx/task.rb', line 515

def outputs(*keys, **options)
  @outputs ||=
    if superclass.respond_to?(:outputs)
      superclass.outputs.dup
    else
      Outputs.new
    end

  return @outputs if keys.empty?

  @outputs.register(*keys, **options)
end

.outputs_schema ⇒ Hash{Symbol => Hash}

Returns serialized output definitions.

Returns:

  • (Hash{Symbol => Hash}) —

    serialized output definitions



530
531
532
# File 'lib/cmdx/task.rb', line 530

def outputs_schema
  outputs.registry.transform_values(&:to_h)
end

.rate_limit(key = nil, **options) {|task| ... } ⇒ RateLimit

Declares a shared rate limit for the task. Builds on the superclass's RateLimit. Passing no key and no options returns the current (possibly inherited) configuration. Pass false to disable.

Parameters:

  • key (Symbol, String, Proc, #call, false, nil) (defaults to: nil) —

    :class scopes one limit per task class; other forms resolve a store segment like Idempotency; false disables rate limiting on this class

  • options (Hash{Symbol => Object}) —

Options Hash (**options):

  • :max (Integer) — default: see {RateLimit#initialize}
  • :per (Integer) — default: see {RateLimit#initialize}
  • :limiter (Symbol, #call) — default: see {RateLimit#initialize}
  • :cost (Numeric) — default: see {RateLimit#initialize}
  • :namespace (String) — default: see {RateLimit#initialize}
  • :on_limit (Symbol) — default: see {RateLimit#initialize}
  • :if (Symbol, Proc, #call) —

    gate (task) before checking

  • :unless (Symbol, Proc, #call) —

    gate (task) before checking

Yield Parameters:

  • task (Task) —

    optional first argument when the block is instance_exec'd (same as callbacks)

Returns:

See Also:



175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/cmdx/task.rb', line 175

def rate_limit(key = nil, **options, &block)
  @rate_limit ||=
    if superclass.respond_to?(:rate_limit)
      superclass.rate_limit
    else
      RateLimit.disabled
    end

  @rate_limit =
    if key == false
      RateLimit.disabled
    else
      @rate_limit.build(key || block, options)
    end
end

.register(type) ⇒ Object

Dispatches to the appropriate registry's register method.

Parameters:

  • type (:middleware, :callback, :coercion, :validator, :executor, :merger, :retrier, :limiter, :deprecator, :input, :output)

Returns:

  • (Object) —

    the registry's self

Raises:

  • (ArgumentError) —

    when type is unknown



329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
# File 'lib/cmdx/task.rb', line 329

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 :limiter
    limiters.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, :limiter, :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.

Parameters:

  • names (Array<Symbol>)
  • options (Hash{Symbol => Object}) —

Options Hash (**options):

  • :description (String) — default: also accepts `:desc`
  • :as (Symbol) —

    overrides the accessor name

  • :prefix (Boolean, String) —

    prefix for the accessor name

  • :suffix (Boolean, String) —

    suffix for the accessor name

  • :source (Symbol, Proc, #call) — default: `:context` —

    where to fetch from

  • :default (Object, Symbol, Proc, #call)
  • :transform (Symbol, Proc, #call) —

    mutator applied after coercion

  • :if (Symbol, Proc, #call)
  • :unless (Symbol, Proc, #call)
  • :allow_nil (Boolean) — default: see {Input#allow_nil}
  • :coerce (Object) — default: see {Coercions#extract}
  • :validate (Object) — default: see {Validators#extract}

Yields:



497
498
499
# File 'lib/cmdx/task.rb', line 497

def required(*names, **options, &)
  register(:input, *names, **options, required: true, &)
end

.retriers ⇒ Retriers

Returns cloned from superclass/configuration on first call.

Returns:

  • (Retriers) —

    cloned from superclass/configuration on first call



295
296
297
298
299
300
301
302
# File 'lib/cmdx/task.rb', line 295

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 and no options returns the current (possibly inherited) Retry. Pass false to disable retries on this class.

Parameters:

  • exceptions (Array<Class, false>) —

    exception classes; false alone disables retries on this class

  • options (Hash{Symbol => Object}) —

Options Hash (**options):

  • :limit (Integer) — default: see {Retry#initialize}
  • :delay (Float) — default: see {Retry#initialize}
  • :max_delay (Float) — default: see {Retry#initialize}
  • :jitter (Symbol, Proc, #call) — default: see {Retry#initialize}
  • :if (Symbol, Proc, #call) —

    gate (task, error, attempt) for retries

  • :unless (Symbol, Proc, #call) —

    gate (task, error, attempt) for retries

Yields:

  • (attempt, delay) —

    optional custom jitter block

Returns:



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/cmdx/task.rb', line 37

def retry_on(*exceptions, **options, &)
  @retry_on ||=
    if superclass.respond_to?(:retry_on)
      superclass.retry_on
    else
      Retry.disabled
    end

  @retry_on =
    if exceptions == [false]
      Retry.disabled
    else
      @retry_on.build(exceptions, options, &)
    end
end

.settings(options = EMPTY_HASH) ⇒ Settings

Reads or extends this class's Settings. Inherits from the superclass.

Parameters:

  • options (Hash{Symbol => Object}) (defaults to: EMPTY_HASH) —

    merged onto the current settings

Options Hash (options):

  • :logger (Logger) — default: see {Settings#initialize}
  • :log_formatter (#call) — default: see {Settings#initialize}
  • :log_level (Integer) — default: see {Settings#initialize}
  • :backtrace_cleaner (#call) — default: see {Settings#initialize}
  • :log_exclusions (Array<Symbol>) — default: see {Settings#initialize}
  • :tags (Array<Symbol, String>) — default: see {Settings#initialize}
  • :strict_context (Boolean) — default: see {Settings#initialize}
  • :correlation_id (#call) —

    callable returning a String id resolved once by Runtime when the root chain is acquired; surfaces as result.xid (see Settings#correlation_id)

Returns:



205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/cmdx/task.rb', line 205

def settings(options = EMPTY_HASH)
  @settings ||=
    if superclass.respond_to?(:settings)
      superclass.settings.build(options)
    else
      Settings.new(options)
    end

  return @settings if options.empty?

  @settings = @settings.build(options)
end

.telemetry ⇒ Telemetry

Returns cloned from superclass/configuration on first call.

Returns:

  • (Telemetry) —

    cloned from superclass/configuration on first call



245
246
247
248
249
250
251
252
# File 'lib/cmdx/task.rb', line 245

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".

Returns:

  • (String) —

    "Workflow" when the class includes Workflow, else "Task"



535
536
537
# File 'lib/cmdx/task.rb', line 535

def type
  @type ||= include?(Workflow) ? "Workflow" : "Task"
end

.validators ⇒ Validators

Returns cloned from superclass/configuration on first call.

Returns:

  • (Validators) —

    cloned from superclass/configuration on first call



265
266
267
268
269
270
271
272
# File 'lib/cmdx/task.rb', line 265

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.

Parameters:

  • strict (Boolean) (defaults to: false) —

    when true, re-raises Fault/exceptions on failure; when false, swallows them and returns the Result

Yield Parameters:

Returns:

  • (Result, Object) —

    the yielded block's value when a block is given, otherwise the Result

Raises:

  • (Fault, StandardError) —

    only when strict: true and the task fails



620
621
622
623
# File 'lib/cmdx/task.rb', line 620

def execute(strict: false)
  result = Runtime.execute(self, strict:)
  block_given? ? yield(result) : result
end

#fail!(reason = nil, **sigdata) ⇒ void

This method returns an undefined value.

Halts #work and marks the Result as failed. Captures the current caller frames for the Fault backtrace and merges any sigdata onto #metadata before the signal is thrown.

Parameters:

  • reason (String, nil) (defaults to: nil) —

    human-readable explanation surfaced on the Result/Fault

  • sigdata (Hash{Symbol => Object}) —

    extra metadata merged into #metadata

Raises:



689
690
691
692
693
694
695
696
697
698
699
# File 'lib/cmdx/task.rb', line 689

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.

Returns:

  • (Logger) —

    a logger tailored to this task's settings



627
628
629
# File 'lib/cmdx/task.rb', line 627

def logger
  @logger ||= LoggerProxy.logger(self)
end

#skip!(reason = nil, **sigdata) ⇒ void

This method returns an undefined value.

Halts #work and marks the Result as skipped. Any sigdata is merged onto #metadata before the signal is thrown.

Parameters:

  • reason (String, nil) (defaults to: nil) —

    human-readable explanation surfaced on the Result

  • sigdata (Hash{Symbol => Object}) —

    extra metadata merged into #metadata

Raises:



669
670
671
672
673
674
675
676
677
678
679
# File 'lib/cmdx/task.rb', line 669

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.

Parameters:

  • reason (String, nil) (defaults to: nil) —

    human-readable explanation surfaced on the Result

  • sigdata (Hash{Symbol => Object}) —

    extra metadata merged into #metadata

Raises:



650
651
652
653
654
655
656
657
658
659
660
# File 'lib/cmdx/task.rb', line 650

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?

Echoes another Result or Signal's failure outcome from this task. A no-op when other is not failed, letting callers conditionally bubble up nested failures without branching. Captures caller frames for the Fault backtrace and merges any sigdata onto #metadata before the signal is thrown.

Parameters:

  • other (Result, Signal) —

    upstream outcome to mirror

  • sigdata (Hash{Symbol => Object}) —

    extra metadata merged into #metadata

Returns:

  • (void, nil) —

    returns nil when other is not failed; otherwise throws Signal::TAG

Raises:



711
712
713
714
715
716
717
718
719
720
721
722
723
# File 'lib/cmdx/task.rb', line 711

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 is abstract.

This method returns an undefined value.

The task's core logic. Subclasses must override.

Raises:



636
637
638
639
640
641
# File 'lib/cmdx/task.rb', line 636

def work
  raise ImplementationError, <<~MSG.chomp
    #{self.class} must implement #work.
    See https://drexed.github.io/cmdx/basics/setup/#structure
  MSG
end