Class: CMDx::Result

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/cmdx/result.rb

Overview

Represents the execution result of a CMDx task, tracking state transitions, status changes, and providing methods for handling different outcomes.

The Result class manages the lifecycle of task execution from initialization through completion or interruption, offering a fluent interface for status checking and conditional handling.

Constant Summary collapse

STATES =

Rbs:

[
  INITIALIZED = "initialized",  # Initial state before execution
  EXECUTING = "executing",      # Currently executing task logic
  COMPLETE = "complete",        # Successfully completed execution
  INTERRUPTED = "interrupted"   # Execution was halted due to failure
].freeze
STATUSES =

Rbs:

[
  SUCCESS = "success",  # Task completed successfully
  SKIPPED = "skipped",  # Task was skipped intentionally
  FAILED = "failed"     # Task failed due to error or validation
].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(task) ⇒ CMDx::Result

Returns A new result instance for the task.

Examples:

result = CMDx::Result.new(my_task)
result.state # => "initialized"

Parameters:

  • task (CMDx::Task)

    The task instance this result represents

Raises:

  • (TypeError)

    When task is not a CMDx::Task instance

Rbs:

  • (Task) -> void



136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/cmdx/result.rb', line 136

def initialize(task)
  raise TypeError, "must be a CMDx::Task" unless task.is_a?(CMDx::Task)

  @task = task
  @state = INITIALIZED
  @status = SUCCESS
  @metadata = {}
  @reason = nil
  @cause = nil
  @retries = 0
  @rolled_back = false
end

Instance Attribute Details

#causeException? (readonly)

Returns the exception that caused the interruption.

Examples:

result.cause # => #<StandardError: Connection timeout>

Returns:

  • (Exception, nil)

    The causing exception, or nil if not interrupted

Rbs:

  • @cause: (Exception | nil)



100
101
102
# File 'lib/cmdx/result.rb', line 100

def cause
  @cause
end

#metadataHash{Symbol => Object} (readonly)

Returns additional metadata about the result.

Examples:

result. # => { duration: 1.5, code: 200, message: "Success" }

Returns:

  • (Hash{Symbol => Object})

    Metadata hash

Rbs:

  • @metadata: Hash[Symbol, untyped]



80
81
82
# File 'lib/cmdx/result.rb', line 80

def 
  @metadata
end

#reasonString? (readonly)

Returns the reason for interruption (skip or failure).

Examples:

result.reason # => "Validation failed"

Returns:

  • (String, nil)

    The reason message, or nil if not interrupted

Rbs:

  • @reason: (String | nil)



90
91
92
# File 'lib/cmdx/result.rb', line 90

def reason
  @reason
end

#retriesInteger

Returns the number of retries attempted.

Examples:

result.retries # => 2

Returns:

  • (Integer)

    The number of retries attempted

Rbs:

  • @retries: Integer



110
111
112
# File 'lib/cmdx/result.rb', line 110

def retries
  @retries
end

#rolled_backBoolean

Returns whether the result has been rolled back.

Examples:

result.rolled_back? # => true

Returns:

  • (Boolean)

    Whether the result has been rolled back

Rbs:

  • @rolled_back: bool



120
121
122
# File 'lib/cmdx/result.rb', line 120

def rolled_back
  @rolled_back
end

#stateString (readonly)

Returns the current execution state of the result.

Examples:

result.state # => "complete"

Returns:

  • (String)

    One of: “initialized”, “executing”, “complete”, “interrupted”

Rbs:

  • @state: String



60
61
62
# File 'lib/cmdx/result.rb', line 60

def state
  @state
end

#statusString (readonly)

Returns the execution status of the result.

Examples:

result.status # => "success"

Returns:

  • (String)

    One of: “success”, “skipped”, “failed”

Rbs:

  • @status: String



70
71
72
# File 'lib/cmdx/result.rb', line 70

def status
  @status
end

#taskCMDx::Task (readonly)

Returns the task instance associated with this result.

Examples:

result.task.id # => "users/create"

Returns:

Rbs:

  • @task: Task



50
51
52
# File 'lib/cmdx/result.rb', line 50

def task
  @task
end

Instance Method Details

#bad?Boolean

Returns Whether the task execution was unsuccessful (not success).

Examples:

result.bad? # => true if !success?

Returns:

  • (Boolean)

    Whether the task execution was unsuccessful (not success)

Rbs:

  • () -> bool



250
251
252
# File 'lib/cmdx/result.rb', line 250

def bad?
  !success?
end

#caused_failureCMDx::Result?

Returns The result that caused this failure, or nil.

Examples:

cause = result.caused_failure
puts "Caused by: #{cause.task.id}" if cause

Returns:

  • (CMDx::Result, nil)

    The result that caused this failure, or nil

Rbs:

  • () -> Result?



408
409
410
411
412
# File 'lib/cmdx/result.rb', line 408

def caused_failure
  return unless failed?

  chain.results.reverse_each.find(&:failed?)
end

#caused_failure?Boolean

Returns Whether this result caused the failure.

Examples:

if result.caused_failure?
  puts "This task caused the failure"
end

Returns:

  • (Boolean)

    Whether this result caused the failure

Rbs:

  • () -> bool



422
423
424
425
426
# File 'lib/cmdx/result.rb', line 422

def caused_failure?
  return false unless failed?

  caused_failure == self
end

#complete!Object

Examples:

result.complete! # Transitions from executing to complete

Raises:

  • (RuntimeError)

    When attempting to transition from invalid state

Rbs:

  • () -> void



200
201
202
203
204
205
206
# File 'lib/cmdx/result.rb', line 200

def complete!
  return if complete?

  raise "can only transition to #{COMPLETE} from #{EXECUTING}" unless executing?

  @state = COMPLETE
end

#deconstructArray

Returns Array containing state, status, reason, cause, and metadata.

Examples:

state, status = result.deconstruct
puts "State: #{state}, Status: #{status}"

Returns:

  • (Array)

    Array containing state, status, reason, cause, and metadata

Rbs:



571
572
573
# File 'lib/cmdx/result.rb', line 571

def deconstruct(*)
  [state, status, reason, cause, ]
end

#deconstruct_keysHash

Returns Hash with key-value pairs for pattern matching.

Examples:

case result.deconstruct_keys
in {state: "complete", good: true}
  puts "Task completed successfully"
in {bad: true}
  puts "Task had issues"
end

Returns:

  • (Hash)

    Hash with key-value pairs for pattern matching

Rbs:

  • (*untyped) -> Hash[Symbol, untyped]



586
587
588
589
590
591
592
593
594
595
596
597
598
# File 'lib/cmdx/result.rb', line 586

def deconstruct_keys(*)
  {
    state: state,
    status: status,
    reason: reason,
    cause: cause,
    metadata: ,
    outcome: outcome,
    executed: executed?,
    good: good?,
    bad: bad?
  }
end

#executed!self

Returns self for method chaining

Examples:

result.executed! # Transitions to complete or interrupted

Returns:

  • (self)

    Returns self for method chaining

Rbs:

  • () -> self



166
167
168
# File 'lib/cmdx/result.rb', line 166

def executed!
  success? ? complete! : interrupt!
end

#executed?Boolean

Returns Whether the task has been executed (complete or interrupted).

Examples:

result.executed? # => true if complete? || interrupted?

Returns:

  • (Boolean)

    Whether the task has been executed (complete or interrupted)

Rbs:

  • () -> bool



176
177
178
# File 'lib/cmdx/result.rb', line 176

def executed?
  complete? || interrupted?
end

#executing!Object

Examples:

result.executing! # Transitions from initialized to executing

Raises:

  • (RuntimeError)

    When attempting to transition from invalid state

Rbs:

  • () -> void



186
187
188
189
190
191
192
# File 'lib/cmdx/result.rb', line 186

def executing!
  return if executing?

  raise "can only transition to #{EXECUTING} from #{INITIALIZED}" unless initialized?

  @state = EXECUTING
end

#fail!(reason = nil, halt: true, cause: nil, **metadata) ⇒ Object

Examples:

result.fail!("Validation failed", cause: validation_error)
result.fail!("Network timeout", halt: false, timeout: 30)

Parameters:

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

    Reason for task failure

  • halt (Boolean) (defaults to: true)

    Whether to halt execution after failure

  • cause (Exception, nil) (defaults to: nil)

    Exception that caused the failure

  • metadata (Hash)

    Additional metadata about the failure

Options Hash (**metadata):

  • :* (Object)

    Any key-value pairs for additional metadata

Raises:

  • (RuntimeError)

    When attempting to fail from invalid status

Rbs:

  • (?String? reason, halt: bool, cause: Exception?, **untyped metadata) -> void



333
334
335
336
337
338
339
340
341
342
343
344
345
# File 'lib/cmdx/result.rb', line 333

def fail!(reason = nil, halt: true, cause: nil, **)
  return if failed?

  raise "can only transition to #{FAILED} from #{SUCCESS}" unless success?

  @state = INTERRUPTED
  @status = FAILED
  @reason = reason || Locale.t("cmdx.reasons.unspecified")
  @cause = cause
  @metadata = 

  halt! if halt
end

#good?Boolean Also known as: ok?

Returns Whether the task execution was successful (not failed).

Examples:

result.good? # => true if !failed?

Returns:

  • (Boolean)

    Whether the task execution was successful (not failed)

Rbs:

  • () -> bool



239
240
241
# File 'lib/cmdx/result.rb', line 239

def good?
  !failed?
end

#halt!Object

Examples:

result.halt! # Raises appropriate fault based on status

Raises:

Rbs:

  • () -> void



354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
# File 'lib/cmdx/result.rb', line 354

def halt!
  return if success?

  klass = skipped? ? SkipFault : FailFault
  fault = klass.new(self)

  # Strip the first two frames (this method and the delegator)
  frames = caller_locations(3..-1)

  unless frames.empty?
    frames = frames.map(&:to_s)

    if (cleaner = task.class.settings.backtrace_cleaner)
      cleaner.call(frames)
    end

    fault.set_backtrace(frames)
  end

  raise(fault)
end

#indexInteger

Returns Index of this result in the chain.

Examples:

position = result.index
puts "Task #{position + 1} of #{chain.results.count}"

Returns:

  • (Integer)

    Index of this result in the chain

Rbs:

  • () -> Integer



505
506
507
# File 'lib/cmdx/result.rb', line 505

def index
  @chain_index || chain.index(self)
end

#interrupt!Object

Examples:

result.interrupt! # Transitions from executing to interrupted

Raises:

  • (RuntimeError)

    When attempting to transition from invalid state

Rbs:

  • () -> void



214
215
216
217
218
219
220
# File 'lib/cmdx/result.rb', line 214

def interrupt!
  return if interrupted?

  raise "cannot transition to #{INTERRUPTED} from #{COMPLETE}" if complete?

  @state = INTERRUPTED
end

#on(*states_or_statuses) {|self| ... } ⇒ self

Returns self for method chaining

Examples:

result.on(:bad) { |r| puts "Task had issues: #{r.reason}" }
result.on(:success, :complete) { |r| puts "Task completed successfully" }

Yields:

  • (self)

    Executes the block if task status or state matches

Returns:

  • (self)

    Returns self for method chaining

Raises:

  • (ArgumentError)

    When no block is provided

Rbs:

  • () { (Result) -> void } -> self



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

def on(*states_or_statuses, &)
  raise ArgumentError, "block required" unless block_given?

  yield(self) if states_or_statuses.any? { |s| public_send(:"#{s}?") }
  self
end

#outcomeString

Returns The outcome of the task execution.

Examples:

result.outcome # => "success" or "interrupted"

Returns:

  • (String)

    The outcome of the task execution

Rbs:

  • () -> String



515
516
517
# File 'lib/cmdx/result.rb', line 515

def outcome
  initialized? || thrown_failure? ? state : status
end

#retried?Boolean

Returns Whether the result has been retried.

Examples:

result.retried? # => true

Returns:

  • (Boolean)

    Whether the result has been retried

Rbs:

  • () -> bool



484
485
486
# File 'lib/cmdx/result.rb', line 484

def retried?
  retries.positive?
end

#rolled_back?Boolean

Returns Whether the result has been rolled back.

Examples:

result.rolled_back? # => true

Returns:

  • (Boolean)

    Whether the result has been rolled back

Rbs:

  • () -> bool



494
495
496
# File 'lib/cmdx/result.rb', line 494

def rolled_back?
  !!@rolled_back
end

#skip!(reason = nil, halt: true, cause: nil, **metadata) ⇒ Object

Examples:

result.skip!("Dependencies not met", cause: dependency_error)
result.skip!("Already processed", halt: false)

Parameters:

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

    Reason for skipping the task

  • halt (Boolean) (defaults to: true)

    Whether to halt execution after skipping

  • cause (Exception, nil) (defaults to: nil)

    Exception that caused the skip

  • metadata (Hash)

    Additional metadata about the skip

Options Hash (**metadata):

  • :* (Object)

    Any key-value pairs for additional metadata

Raises:

  • (RuntimeError)

    When attempting to skip from invalid status

Rbs:

  • (?String? reason, halt: bool, cause: Exception?, **untyped metadata) -> void



306
307
308
309
310
311
312
313
314
315
316
317
318
# File 'lib/cmdx/result.rb', line 306

def skip!(reason = nil, halt: true, cause: nil, **)
  return if skipped?

  raise "can only transition to #{SKIPPED} from #{SUCCESS}" unless success?

  @state = INTERRUPTED
  @status = SKIPPED
  @reason = reason || Locale.t("cmdx.reasons.unspecified")
  @cause = cause
  @metadata = 

  halt! if halt
end

#success!(reason = nil, **metadata) ⇒ Object

Sets a reason and optional metadata on a successful result without changing its state or status. Useful for annotating why a task succeeded.

Examples:

result.success!("Created 42 records")
result.success!("Imported", rows: 100)

Parameters:

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

    Reason or note for the success

  • metadata (Hash)

    Additional metadata about the success

Options Hash (**metadata):

  • :* (Object)

    Any key-value pairs for additional metadata

Raises:

  • (RuntimeError)

    When status is not success

Rbs:

  • (?String? reason, **untyped metadata) -> void



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

def success!(reason = nil, **)
  raise "can only be used while #{SUCCESS}" unless success?

  @reason = reason
  @metadata = 
end

#threw_failureCMDx::Result?

Returns The result that threw this failure, or nil.

Examples:

thrown = result.threw_failure
puts "Thrown by: #{thrown.task.id}" if thrown

Returns:

  • (CMDx::Result, nil)

    The result that threw this failure, or nil

Rbs:

  • () -> Result?



435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
# File 'lib/cmdx/result.rb', line 435

def threw_failure
  return unless failed?

  current = index
  last_failed = nil

  chain.results.each do |r|
    next unless r.failed?

    return r if r.index > current

    last_failed = r
  end

  last_failed
end

#threw_failure?Boolean

Returns Whether this result threw the failure.

Examples:

if result.threw_failure?
  puts "This task threw the failure"
end

Returns:

  • (Boolean)

    Whether this result threw the failure

Rbs:

  • () -> bool



460
461
462
463
464
# File 'lib/cmdx/result.rb', line 460

def threw_failure?
  return false unless failed?

  threw_failure == self
end

#throw!(result, halt: true, cause: nil, **metadata) ⇒ Object

Examples:

other_result = OtherTask.execute
result.throw!(other_result, cause: upstream_error)

Parameters:

  • result (CMDx::Result)

    Result to throw from current result

  • halt (Boolean) (defaults to: true)

    Whether to halt execution after throwing

  • cause (Exception, nil) (defaults to: nil)

    Exception that caused the throw

  • metadata (Hash)

    Additional metadata to merge

Options Hash (**metadata):

  • :* (Object)

    Any key-value pairs for additional metadata

Raises:

  • (TypeError)

    When result is not a CMDx::Result instance

Rbs:

  • (Result result, halt: bool, cause: Exception?, **untyped metadata) -> void



389
390
391
392
393
394
395
396
397
398
399
# File 'lib/cmdx/result.rb', line 389

def throw!(result, halt: true, cause: nil, **)
  raise TypeError, "must be a CMDx::Result" unless result.is_a?(Result)

  @state = result.state
  @status = result.status
  @reason = result.reason
  @cause = cause || result.cause
  @metadata = result..merge()

  halt! if halt
end

#thrown_failure?Boolean

Returns Whether this result is a thrown failure.

Examples:

if result.thrown_failure?
  puts "This failure was thrown from another task"
end

Returns:

  • (Boolean)

    Whether this result is a thrown failure

Rbs:

  • () -> bool



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

def thrown_failure?
  failed? && !caused_failure?
end

#to_hHash

Returns Hash representation of the result.

Examples:

result.to_h
# => {state: "complete", status: "success", outcome: "success", reason: "Unspecified", metadata: {}}

Returns:

  • (Hash)

    Hash representation of the result

Rbs:

  • () -> Hash[Symbol, untyped]



526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
# File 'lib/cmdx/result.rb', line 526

def to_h
  task.to_h.merge!(
    state:,
    status:,
    outcome:,
    reason:,
    metadata:
  ).tap do |hash|
    if interrupted?
      hash[:cause] = cause
      hash[:rolled_back] = rolled_back?
    end

    if failed?
      STRIP_FAILURE.call(hash, self, :threw_failure)
      STRIP_FAILURE.call(hash, self, :caused_failure)
    end
  end
end

#to_sString

Returns String representation of the result.

Examples:

result.to_s # => "task_id=my_task state=complete status=success"

With failure

result.to_s # => "task_id=my_task state=complete status=failed threw_failure=<[1] MyTask: my_task>"

Returns:

  • (String)

    String representation of the result

Rbs:

  • () -> String



554
555
556
557
558
559
560
561
562
# File 'lib/cmdx/result.rb', line 554

def to_s
  Utils::Format.to_str(to_h) do |key, value|
    if FAILURE_KEY_REGEX.match?(key)
      "#{key}=<[#{value[:index]}] #{value[:class]}: #{value[:id]}>"
    else
      "#{key}=#{value.inspect}"
    end
  end
end