Class: Ask::Graph::Context

Inherits:
Object
  • Object
show all
Defined in:
lib/ask/graph/context.rb

Overview

Shared state that flows through every step of a graph execution.

Stores input, output, and intermediate data produced by steps. Accessed by step classes via context.data_key or context[:data_key]. Thread-safe by design — each context is scoped to one graph run.

Examples:

class MyStep
  def call(context)
    context.transcript = "Hello"
    context[:classification] = "booking"
  end
end

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(graph, input = nil) ⇒ Context

Returns a new instance of Context.



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/ask/graph/context.rb', line 26

def initialize(graph, input = nil)
  @graph = graph
  @store = {}
  @each_index = nil
  @each_items = nil
  @item = nil
  @mutex = Mutex.new
  @resume_input = nil
  if input.is_a?(Hash)
    @store[:input] = input
    @store.merge!(input)
  elsif input
    @store[:input] = input
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args, &block) ⇒ Object

Access a stored value by method name.



46
47
48
49
50
51
52
53
54
55
# File 'lib/ask/graph/context.rb', line 46

def method_missing(name, *args, &block)
  if name.to_s.end_with?("=")
    key = name.to_s.chomp("=").to_sym
    @mutex.synchronize { @store[key] = args.first }
  elsif args.empty? && !block
    @mutex.synchronize { @store[name] }
  else
    super
  end
end

Instance Attribute Details

#graphAsk::Graph (readonly)

Returns the graph this context belongs to.

Returns:

  • (Ask::Graph)

    the graph this context belongs to



21
22
23
# File 'lib/ask/graph/context.rb', line 21

def graph
  @graph
end

#itemObject? (readonly)

Returns the current item when iterating via #each.

Returns:

  • (Object, nil)

    the current item when iterating via #each



24
25
26
# File 'lib/ask/graph/context.rb', line 24

def item
  @item
end

#resume_inputObject?

Returns input provided on resume for approval steps.

Returns:

  • (Object, nil)

    input provided on resume for approval steps



43
44
45
# File 'lib/ask/graph/context.rb', line 43

def resume_input
  @resume_input
end

Instance Method Details

#[](key) ⇒ Object?

Access a stored value by key.

Parameters:

  • key (Symbol)

    the key

Returns:

  • (Object, nil)


65
66
67
# File 'lib/ask/graph/context.rb', line 65

def [](key)
  @mutex.synchronize { @store[key.to_sym] }
end

#[]=(key, value) ⇒ Object

Store a value by key.

Parameters:

  • key (Symbol)

    the key

  • value (Object)

    the value



72
73
74
# File 'lib/ask/graph/context.rb', line 72

def []=(key, value)
  @mutex.synchronize { @store[key.to_sym] = value }
end

#each(items) {|item| ... } ⇒ Object

Iterate over items with per-item checkpointing.

Each iteration is checkpointed so that if the process crashes, it resumes from the last completed item rather than starting over.

Parameters:

  • items (Array)

    the items to iterate over

Yields:

  • (item)

    called once per item

Yield Parameters:

  • item (Object)

    the current item



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/ask/graph/context.rb', line 84

def each(items, &block)
  @each_index = graph.runner.resume_index_for(items) || 0
  @each_items = items

  items.each_with_index do |item, idx|
    break if idx < @each_index

    @item = item
    block.call(item)
    graph.runner.checkpoint_each!(idx)
  end
ensure
  @each_index = nil
  @each_items = nil
  @item = nil
end

#export_dataHash

Unlike #to_h, this returns the actual Ruby objects without serialization. Useful for passing state to sub-graphs.

Returns:

  • (Hash)

    raw store data with original object references.



113
114
115
# File 'lib/ask/graph/context.rb', line 113

def export_data
  @mutex.synchronize { @store.dup }
end

#import(other) ⇒ Object

Merge data from another context (or hash) into this one. Existing keys in this context are overwritten.

Parameters:

  • other (Context, Hash)

    source of data to merge in



121
122
123
124
# File 'lib/ask/graph/context.rb', line 121

def import(other)
  data = other.is_a?(Context) ? other.export_data : other
  @mutex.synchronize { @store.merge!(data) }
end

#respond_to_missing?(name, include_private = false) ⇒ Boolean

Returns:

  • (Boolean)


57
58
59
60
# File 'lib/ask/graph/context.rb', line 57

def respond_to_missing?(name, include_private = false)
  clean = name.to_s.sub(/=$/, "").to_sym
  @store.key?(clean) || super
end

#run(graph_class) ⇒ Ask::Graph::Context

Run an Ask::Graph as a sub-graph, merging its context back.

Exports the current context data, creates the sub-graph with it, runs it, and merges any results back into this context.

Examples:

# Inside a step PORO
class CalculateShipping
  def call(context)
    context.run(Shipping::Workflow)
  end
end

Parameters:

  • graph_class (Class < Ask::Graph] the graph to run)

    raph_class [Class < Ask::Graph] the graph to run

Returns:



140
141
142
143
144
145
# File 'lib/ask/graph/context.rb', line 140

def run(graph_class)
  sub = graph_class.new(export_data)
  sub_ctx = sub.call
  import(sub_ctx)
  sub_ctx
end

#to_hHash

All keys and values are converted to JSON-safe types (string keys, symbols converted to strings, arrays recursed).

Returns:

  • (Hash)

    serializable snapshot of the context state.



104
105
106
107
108
# File 'lib/ask/graph/context.rb', line 104

def to_h
  @mutex.synchronize do
    deep_json_safe(@store)
  end
end