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 =
[
  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 =
[
  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



112
113
114
115
116
117
118
119
120
121
# File 'lib/cmdx/result.rb', line 112

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



96
97
98
# File 'lib/cmdx/result.rb', line 96

def cause
  @cause
end

#metadataHash{Symbol => Object} (readonly)

Returns additional metadata about the result.

Examples:

result. # => { duration: 1.5, retries: 2 }

Returns:

  • (Hash{Symbol => Object})

    Metadata hash



76
77
78
# File 'lib/cmdx/result.rb', line 76

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



86
87
88
# File 'lib/cmdx/result.rb', line 86

def reason
  @reason
end

#stateString (readonly)

Returns the current execution state of the result.

Examples:

result.state # => "complete"

Returns:

  • (String)

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



56
57
58
# File 'lib/cmdx/result.rb', line 56

def state
  @state
end

#statusString (readonly)

Returns the execution status of the result.

Examples:

result.status # => "success"

Returns:

  • (String)

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



66
67
68
# File 'lib/cmdx/result.rb', line 66

def status
  @status
end

#taskCMDx::Task (readonly)

Returns the task instance associated with this result.

Examples:

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

Returns:



46
47
48
# File 'lib/cmdx/result.rb', line 46

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)



298
299
300
# File 'lib/cmdx/result.rb', line 298

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



434
435
436
437
438
# File 'lib/cmdx/result.rb', line 434

def caused_failure
  return unless failed?

  chain.results.reverse.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



448
449
450
451
452
# File 'lib/cmdx/result.rb', line 448

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



211
212
213
214
215
216
217
# File 'lib/cmdx/result.rb', line 211

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



564
565
566
# File 'lib/cmdx/result.rb', line 564

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



579
580
581
582
583
584
585
586
587
588
589
590
591
# File 'lib/cmdx/result.rb', line 579

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



160
161
162
# File 'lib/cmdx/result.rb', line 160

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)



170
171
172
# File 'lib/cmdx/result.rb', line 170

def executed?
  complete? || interrupted?
end

#executing!Object

Examples:

result.executing! # Transitions from initialized to executing

Raises:

  • (RuntimeError)

    When attempting to transition from invalid state



197
198
199
200
201
202
203
# File 'lib/cmdx/result.rb', line 197

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



359
360
361
362
363
364
365
366
367
368
369
370
371
# File 'lib/cmdx/result.rb', line 359

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.faults.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)



270
271
272
# File 'lib/cmdx/result.rb', line 270

def good?
  !failed?
end

#halt!Object

Examples:

result.halt! # Raises appropriate fault based on status

Raises:



380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
# File 'lib/cmdx/result.rb', line 380

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

#handle_bad {|self| ... } ⇒ self

Returns self for method chaining

Examples:

result.handle_bad { |r| puts "Task had issues: #{r.reason}" }

Yields:

  • (self)

    Executes the block if task execution was unsuccessful

Returns:

  • (self)

    Returns self for method chaining

Raises:

  • (ArgumentError)

    When no block is provided



312
313
314
315
316
317
# File 'lib/cmdx/result.rb', line 312

def handle_bad(&)
  raise ArgumentError, "block required" unless block_given?

  yield(self) if bad?
  self
end

#handle_executed {|self| ... } ⇒ self

Returns self for method chaining

Examples:

result.handle_executed { |r| puts "Task finished: #{r.outcome}" }

Yields:

  • (self)

    Executes the block if task has been executed

Returns:

  • (self)

    Returns self for method chaining

Raises:

  • (ArgumentError)

    When no block is provided



184
185
186
187
188
189
# File 'lib/cmdx/result.rb', line 184

def handle_executed(&)
  raise ArgumentError, "block required" unless block_given?

  yield(self) if executed?
  self
end

#handle_good {|self| ... } ⇒ self

Returns self for method chaining

Examples:

result.handle_good { |r| puts "Task completed successfully" }

Yields:

  • (self)

    Executes the block if task execution was successful

Returns:

  • (self)

    Returns self for method chaining

Raises:

  • (ArgumentError)

    When no block is provided



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

def handle_good(&)
  raise ArgumentError, "block required" unless block_given?

  yield(self) if good?
  self
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



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

def index
  chain.index(self)
end

#interrupt!Object

Examples:

result.interrupt! # Transitions from executing to interrupted

Raises:

  • (RuntimeError)

    When attempting to transition from invalid state



225
226
227
228
229
230
231
# File 'lib/cmdx/result.rb', line 225

def interrupt!
  return if interrupted?

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

  @state = INTERRUPTED
end

#outcomeString

Returns The outcome of the task execution.

Examples:

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

Returns:

  • (String)

    The outcome of the task execution



512
513
514
# File 'lib/cmdx/result.rb', line 512

def outcome
  initialized? || thrown_failure? ? state : status
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



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

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.faults.unspecified")
  @cause = cause
  @metadata = 

  halt! if halt
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



461
462
463
464
465
466
467
# File 'lib/cmdx/result.rb', line 461

def threw_failure
  return unless failed?

  current = index
  results = chain.results.select(&:failed?)
  results.find { |r| r.index > current } || results.last
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



477
478
479
480
481
# File 'lib/cmdx/result.rb', line 477

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



415
416
417
418
419
420
421
422
423
424
425
# File 'lib/cmdx/result.rb', line 415

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



491
492
493
# File 'lib/cmdx/result.rb', line 491

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", metadata: {}}

Returns:

  • (Hash)

    Hash representation of the result



523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
# File 'lib/cmdx/result.rb', line 523

def to_h
  task.to_h.merge!(
    state:,
    status:,
    outcome:,
    metadata:
  ).tap do |hash|
    if interrupted?
      hash[:reason] = reason
      hash[:cause] = cause
    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"

Returns:

  • (String)

    String representation of the result



548
549
550
551
552
553
554
555
# File 'lib/cmdx/result.rb', line 548

def to_s
  Utils::Format.to_str(to_h) do |key, value|
    case key
    when /failure/ then "#{key}=<[#{value[:index]}] #{value[:class]}: #{value[:id]}>"
    else "#{key}=#{value.inspect}"
    end
  end
end