Class: ExcADG::StateMachine

Inherits:
Object
  • Object
show all
Defined in:
lib/excadg/state_machine.rb

Overview

carry states and transitions for individual vertices

Defined Under Namespace

Classes: NotAllTransitionsBound, WrongState, WrongTransition

Constant Summary collapse

GRAPH =

sets in stone possible state transitions

RGL::DirectedAdjacencyGraph.new

Instance Method Summary collapse

Constructor Details

#initialize(name:) ⇒ StateMachine

add states graph to the current object



23
24
25
26
27
28
29
# File 'lib/excadg/state_machine.rb', line 23

def initialize name:
  @state = :new
  @state_edge_bindings = {}
  @state_transition_data = {}

  @name = name
end

Instance Method Details

#assert_state_transition_boundsObject



83
84
85
# File 'lib/excadg/state_machine.rb', line 83

def assert_state_transition_bounds
  raise NotAllTransitionsBound, GRAPH.edges - @state_edge_bindings.keys unless GRAPH.edges.eql? @state_edge_bindings.keys
end

#bind_action(source, target, &block) ⇒ Object

bind action to one of the state graph’s edges



32
33
34
35
36
37
38
39
40
# File 'lib/excadg/state_machine.rb', line 32

def bind_action source, target, &block
  [source, target].each { |state|
    raise WrongState, "unknown state #{state}" unless GRAPH.has_vertex? state
  }
  raise WrongTransition.new source, target unless GRAPH.has_edge? source, target

  edge = GRAPH.edges.find { |e| e.source == source && e.target = target }
  @state_edge_bindings[edge] = block
end

#state_dataObject

makes state data for the current vertex



88
89
90
# File 'lib/excadg/state_machine.rb', line 88

def state_data
  VStateData::Full.new name: @name, data: @state_transition_data[@state], state: @state
end

#stepObject

transition to next state @return: state data (result) / nil if it’s a final step

Raises:



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/excadg/state_machine.rb', line 44

def step
  Log.debug 'taking another step'
  assert_state_transition_bounds

  target_candidates = GRAPH.each_adjacent @state
  Log.debug "possible candidates: #{target_candidates.size}"
  return nil if target_candidates.none?
  raise WrongState, "state #{@state} has more than one adjacent states" unless target_candidates.one?

  target = target_candidates.first
  Log.debug "found a candidate: #{target}"
  edge = GRAPH.edges.find { |e| e.source == @state && e.target = target }

  with_fault_processing {
    Log.debug "calling payload for #{edge}"
    @state_transition_data[target] = @state_edge_bindings[edge].call
    @state = target
    Log.debug "moved to #{@state}"
  }
  @state_transition_data[@state]
end

#with_fault_processingObject

invoke the block provided with state update on failures



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/excadg/state_machine.rb', line 67

def with_fault_processing
  yield if block_given?
rescue StandardError => e
  Log.error "step failed with '#{e}' / #{e.backtrace}"
  @state_transition_data[:failed] = e
  @state = :failed
ensure
  begin
    Broker.ask Request::Update.new data: state_data
  rescue StandardError => e
    @state_transition_data[:failed] = e
    @state = :failed
    Broker.ask Request::Update.new data: state_data
  end
end