Module: Phronomy::WorkflowContext

Defined in:
lib/phronomy/workflow_context.rb

Overview

Module for defining workflow context (the data that travels through a workflow). Include in a class and use the field DSL to declare context fields.

In StateChart terminology this is the "extended state" or "context" — data associated with the current execution that does not affect transitions directly, as opposed to the current phase (which is the machine's state).

Field update policies: :replace (default) -- overwrites with the new value :append -- appends to an Array :merge -- shallow-merges into a Hash (top-level keys are merged; nested objects are replaced)

Examples:

class ScanContext
  include Phronomy::WorkflowContext
  field :messages, type: :append, default: -> { [] }
  field :query,    type: :replace
  field :metadata, type: :merge,   default: -> { {} }
end

Defined Under Namespace

Modules: ClassMethods

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#thread_idObject (readonly)

Internal workflow metadata accessors (not user-defined fields). These are preserved through merge but excluded from to_h.



55
56
57
# File 'lib/phronomy/workflow_context.rb', line 55

def thread_id
  @thread_id
end

Class Method Details

.included(base) ⇒ Object



24
25
26
27
# File 'lib/phronomy/workflow_context.rb', line 24

def self.included(base)
  base.extend(ClassMethods)
  base.instance_variable_set(:@fields, {})
end

Instance Method Details

#halted?Boolean

Returns true if the workflow is paused mid-execution (not yet completed).

Returns:

  • (Boolean)


71
72
73
# File 'lib/phronomy/workflow_context.rb', line 71

def halted?
  phase != :__end__
end

#initialize(**attrs) ⇒ Object

Raises:

  • (ArgumentError)


85
86
87
88
89
90
91
92
93
94
95
# File 'lib/phronomy/workflow_context.rb', line 85

def initialize(**attrs)
  unknown = attrs.keys - self.class.fields.keys
  raise ArgumentError, "Unknown WorkflowContext field(s): #{unknown.inspect}" unless unknown.empty?

  self.class.fields.each do |name, config|
    default = config[:default].is_a?(Proc) ? config[:default].call : config[:default]
    send(:"#{name}=", attrs.fetch(name, default))
  end
  @thread_id = nil
  @phase = :__end__
end

#merge(updates) ⇒ self.class

Returns a new context instance with the specified field updates applied. Updated fields follow the field's declared +:type+ semantics (:replace, :append, or :merge). Unchanged fields are deep-copied on a best-effort basis — objects that do not support +#dup+ (e.g. integers, frozen objects) are carried over by reference. Internal workflow metadata (thread_id, phase) is preserved.

Parameters:

  • updates (Hash)

    { field_name => new_value }

Returns:

  • (self.class)

    new context instance

Raises:

  • (ArgumentError)

    if updates contains keys that are not declared fields



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/phronomy/workflow_context.rb', line 106

def merge(updates)
  unknown = updates.keys - self.class.fields.keys
  raise ArgumentError, "Unknown WorkflowContext field(s): #{unknown.inspect}" unless unknown.empty?

  new_attrs = {}
  self.class.fields.each_key do |name|
    field_config = self.class.fields[name]
    new_attrs[name] = if updates.key?(name)
      case field_config[:type]
      when :append
        Array(send(name)) + Array(updates[name])
      when :merge
        (send(name) || {}).merge(updates[name])
      else
        updates[name]
      end
    else
      deep_dup_value(send(name))
    end
  end
  new_context = self.class.new(**new_attrs)
  new_context.(
    thread_id: @thread_id,
    phase: @phase
  )
  new_context
end

#phaseSymbol

Returns the current execution phase of the workflow. Encoding: :end — workflow completed (or not yet started) :awaiting_ — halted at a wait_state(:awaiting_) declaration : — resuming at (workflow paused before its execution)

Returns:

  • (Symbol)


64
65
66
# File 'lib/phronomy/workflow_context.rb', line 64

def phase
  @phase || :__end__
end

#set_graph_metadata(thread_id: nil, phase: nil) ⇒ Object

Sets internal workflow metadata. Returns self.

Parameters:

  • thread_id (String, nil) (defaults to: nil)
  • phase (Symbol, nil) (defaults to: nil)


79
80
81
82
83
# File 'lib/phronomy/workflow_context.rb', line 79

def (thread_id: nil, phase: nil)
  @thread_id = thread_id unless thread_id.nil?
  @phase = phase unless phase.nil?
  self
end

#to_hHash

Converts user-defined fields to a Hash (excludes internal workflow metadata).

Returns:

  • (Hash)


137
138
139
140
141
# File 'lib/phronomy/workflow_context.rb', line 137

def to_h
  self.class.fields.keys.each_with_object({}) do |name, h|
    h[name] = send(name)
  end
end