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



132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/cmdx/result.rb', line 132

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



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, code: 200, message: "Success" }

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

#retriesInteger

Returns the number of retries attempted.

Examples:

result.retries # => 2

Returns:

  • (Integer)

    The number of retries attempted



106
107
108
# File 'lib/cmdx/result.rb', line 106

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



116
117
118
# File 'lib/cmdx/result.rb', line 116

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”



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)



246
247
248
# File 'lib/cmdx/result.rb', line 246

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



383
384
385
386
387
# File 'lib/cmdx/result.rb', line 383

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



397
398
399
400
401
# File 'lib/cmdx/result.rb', line 397

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



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

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



534
535
536
# File 'lib/cmdx/result.rb', line 534

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



549
550
551
552
553
554
555
556
557
558
559
560
561
# File 'lib/cmdx/result.rb', line 549

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



162
163
164
# File 'lib/cmdx/result.rb', line 162

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)



172
173
174
# File 'lib/cmdx/result.rb', line 172

def executed?
  complete? || interrupted?
end

#executing!Object

Examples:

result.executing! # Transitions from initialized to executing

Raises:

  • (RuntimeError)

    When attempting to transition from invalid state



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

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



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

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)



235
236
237
# File 'lib/cmdx/result.rb', line 235

def good?
  !failed?
end

#halt!Object

Examples:

result.halt! # Raises appropriate fault based on status

Raises:



329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
# File 'lib/cmdx/result.rb', line 329

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



471
472
473
# File 'lib/cmdx/result.rb', line 471

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



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

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



261
262
263
264
265
266
# File 'lib/cmdx/result.rb', line 261

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



481
482
483
# File 'lib/cmdx/result.rb', line 481

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



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

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



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

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



281
282
283
284
285
286
287
288
289
290
291
292
293
# File 'lib/cmdx/result.rb', line 281

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



410
411
412
413
414
415
416
# File 'lib/cmdx/result.rb', line 410

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



426
427
428
429
430
# File 'lib/cmdx/result.rb', line 426

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



364
365
366
367
368
369
370
371
372
373
374
# File 'lib/cmdx/result.rb', line 364

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



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

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



492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
# File 'lib/cmdx/result.rb', line 492

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



518
519
520
521
522
523
524
525
# File 'lib/cmdx/result.rb', line 518

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