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
122
# 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
  @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



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)



225
226
227
# File 'lib/cmdx/result.rb', line 225

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



362
363
364
365
366
# File 'lib/cmdx/result.rb', line 362

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



376
377
378
379
380
# File 'lib/cmdx/result.rb', line 376

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



175
176
177
178
179
180
181
# File 'lib/cmdx/result.rb', line 175

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



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

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



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

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



141
142
143
# File 'lib/cmdx/result.rb', line 141

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)



151
152
153
# File 'lib/cmdx/result.rb', line 151

def executed?
  complete? || interrupted?
end

#executing!Object

Examples:

result.executing! # Transitions from initialized to executing

Raises:

  • (RuntimeError)

    When attempting to transition from invalid state



161
162
163
164
165
166
167
# File 'lib/cmdx/result.rb', line 161

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



287
288
289
290
291
292
293
294
295
296
297
298
299
# File 'lib/cmdx/result.rb', line 287

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)



214
215
216
# File 'lib/cmdx/result.rb', line 214

def good?
  !failed?
end

#halt!Object

Examples:

result.halt! # Raises appropriate fault based on status

Raises:



308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
# File 'lib/cmdx/result.rb', line 308

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



451
452
453
# File 'lib/cmdx/result.rb', line 451

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



189
190
191
192
193
194
195
# File 'lib/cmdx/result.rb', line 189

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



240
241
242
243
244
245
# File 'lib/cmdx/result.rb', line 240

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

  yield(self) if states_or_statuses.any? { |s| 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



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

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

#rolled_back!void

This method returns an undefined value.

Examples:

result.rolled_back!
result.rolled_back? # => true


430
431
432
# File 'lib/cmdx/result.rb', line 430

def rolled_back!
  @rolled_back = true
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



440
441
442
# File 'lib/cmdx/result.rb', line 440

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



260
261
262
263
264
265
266
267
268
269
270
271
272
# File 'lib/cmdx/result.rb', line 260

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



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

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



405
406
407
408
409
# File 'lib/cmdx/result.rb', line 405

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



343
344
345
346
347
348
349
350
351
352
353
# File 'lib/cmdx/result.rb', line 343

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



419
420
421
# File 'lib/cmdx/result.rb', line 419

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



472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
# File 'lib/cmdx/result.rb', line 472

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

Returns:

  • (String)

    String representation of the result



498
499
500
501
502
503
504
505
# File 'lib/cmdx/result.rb', line 498

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