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.



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

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)


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

def halted?
  phase != :__end__
end

#initialize(**attrs) ⇒ Object

Raises:

  • (ArgumentError)


94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/phronomy/workflow_context.rb', line 94

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]
    # Bypass the write guard in initialize — ownership enforcement begins
    # after construction is complete.
    instance_variable_set(:"@#{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



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/phronomy/workflow_context.rb', line 117

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)


73
74
75
# File 'lib/phronomy/workflow_context.rb', line 73

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)


88
89
90
91
92
# File 'lib/phronomy/workflow_context.rb', line 88

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)


148
149
150
151
152
# File 'lib/phronomy/workflow_context.rb', line 148

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