Class: Foobara::StateMachine

Inherits:
Object
  • Object
show all
Includes:
Callbacks, Sugar, TransitionLog, Validations
Defined in:
foobara-0.0.130/projects/state_machine/src/state_machine.rb,
foobara-0.0.130/projects/state_machine/src/sugar.rb,
foobara-0.0.130/projects/state_machine/src/callbacks.rb,
foobara-0.0.130/projects/state_machine/src/log_entry.rb,
foobara-0.0.130/projects/state_machine/src/validations.rb,
foobara-0.0.130/projects/state_machine/src/transition_log.rb,
foobara-0.0.130/projects/state_machine/lib/foobara/state_machine.rb

Overview

TODO: allow quick creation of a statemachine either through better options to #initialize or a .for method.

Defined Under Namespace

Modules: Callbacks, Sugar, TransitionLog, Validations Classes: InvalidTransition, LogEntry

Class Attribute Summary collapse

Instance Attribute Summary collapse

Attributes included from TransitionLog

#log

Attributes included from Callbacks

#callback_registry

Class Method Summary collapse

Instance Method Summary collapse

Methods included from TransitionLog

#log_transition

Methods included from Concern

foobara_class_methods_module_for, foobara_concern?, included

Methods included from Callbacks

#register_transition_callback

Constructor Details

#initialize(*args, owner: nil, target_attribute: nil, **options) ⇒ StateMachine

owner is optional. It can help with certain callbacks. It’s also required if planning to use the target_attribute feature



50
51
52
53
54
55
56
57
58
59
60
# File 'foobara-0.0.130/projects/state_machine/src/state_machine.rb', line 50

def initialize(*args, owner: nil, target_attribute: nil, **options)
  self.owner = owner

  super

  if target_attribute
    self.target_attribute = target_attribute
  else
    self.current_state = self.class.initial_state
  end
end

Class Attribute Details

.initial_stateObject

Returns the value of attribute initial_state.



17
18
19
# File 'foobara-0.0.130/projects/state_machine/src/state_machine.rb', line 17

def initial_state
  @initial_state
end

.non_terminal_statesObject

Returns the value of attribute non_terminal_states.



17
18
19
# File 'foobara-0.0.130/projects/state_machine/src/state_machine.rb', line 17

def non_terminal_states
  @non_terminal_states
end

.raw_transition_mapObject

Returns the value of attribute raw_transition_map.



17
18
19
# File 'foobara-0.0.130/projects/state_machine/src/state_machine.rb', line 17

def raw_transition_map
  @raw_transition_map
end

.stateObject

Returns the value of attribute state.



17
18
19
# File 'foobara-0.0.130/projects/state_machine/src/state_machine.rb', line 17

def state
  @state
end

.statesObject

Returns the value of attribute states.



17
18
19
# File 'foobara-0.0.130/projects/state_machine/src/state_machine.rb', line 17

def states
  @states
end

.terminal_statesObject

Returns the value of attribute terminal_states.



17
18
19
# File 'foobara-0.0.130/projects/state_machine/src/state_machine.rb', line 17

def terminal_states
  @terminal_states
end

.transitionObject

Returns the value of attribute transition.



17
18
19
# File 'foobara-0.0.130/projects/state_machine/src/state_machine.rb', line 17

def transition
  @transition
end

.transition_mapObject

Returns the value of attribute transition_map.



17
18
19
# File 'foobara-0.0.130/projects/state_machine/src/state_machine.rb', line 17

def transition_map
  @transition_map
end

.transitionsObject

Returns the value of attribute transitions.



17
18
19
# File 'foobara-0.0.130/projects/state_machine/src/state_machine.rb', line 17

def transitions
  @transitions
end

Instance Attribute Details

#ownerObject

Returns the value of attribute owner.



46
47
48
# File 'foobara-0.0.130/projects/state_machine/src/state_machine.rb', line 46

def owner
  @owner
end

#target_attributeObject

Returns the value of attribute target_attribute.



46
47
48
# File 'foobara-0.0.130/projects/state_machine/src/state_machine.rb', line 46

def target_attribute
  @target_attribute
end

Class Method Details

.for(transition_map) ⇒ Object



38
39
40
41
42
43
# File 'foobara-0.0.130/projects/state_machine/src/state_machine.rb', line 38

def for(transition_map)
  klass = Class.new(self)

  klass.set_transition_map(transition_map)
  klass
end

.set_transition_map(transition_map, initial_state: nil, states: nil, terminal_states: nil, transitions: nil) ⇒ Object



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'foobara-0.0.130/projects/state_machine/src/state_machine.rb', line 20

def set_transition_map(transition_map, initial_state: nil, states: nil, terminal_states: nil, transitions: nil)
  self.raw_transition_map = transition_map

  self.initial_state = initial_state
  self.states = states
  self.terminal_states = terminal_states
  self.transitions = transitions

  desugarize_transition_map
  determine_states_and_transitions

  create_enums
  create_state_predicate_methods
  create_transition_methods
  create_can_methods
  create_register_callback_methods
end

Instance Method Details

#allowed_transitionsObject



103
104
105
106
107
108
109
# File 'foobara-0.0.130/projects/state_machine/src/state_machine.rb', line 103

def allowed_transitions
  if in_terminal_state?
    []
  else
    self.class.transition_map[current_state].keys
  end
end

#can?(transition) ⇒ Boolean

Returns:

  • (Boolean)


111
112
113
# File 'foobara-0.0.130/projects/state_machine/src/state_machine.rb', line 111

def can?(transition)
  self.class.transition_map[current_state].key?(transition)
end

#current_stateObject



62
63
64
65
66
67
68
# File 'foobara-0.0.130/projects/state_machine/src/state_machine.rb', line 62

def current_state
  if target_attribute
    owner.send(target_attribute) || self.class.initial_state
  else
    @current_state
  end
end

#current_state=(state) ⇒ Object



70
71
72
73
74
75
76
# File 'foobara-0.0.130/projects/state_machine/src/state_machine.rb', line 70

def current_state=(state)
  if target_attribute
    owner.send("#{target_attribute}=", state)
  else
    @current_state = state
  end
end

#in_terminal_state?Boolean

Returns:

  • (Boolean)


120
121
122
# File 'foobara-0.0.130/projects/state_machine/src/state_machine.rb', line 120

def in_terminal_state?
  self.class.terminal_states.include?(current_state)
end

#perform_transition!(transition, &block) ⇒ Object



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'foobara-0.0.130/projects/state_machine/src/state_machine.rb', line 78

def perform_transition!(transition, &block)
  from = current_state

  transition_map = self.class.transition_map

  if in_terminal_state?
    raise InvalidTransition,
          "#{current_state} is a terminal state so no transitions from here are allowed."
  end

  unless transition_map[current_state].key?(transition)
    raise InvalidTransition,
          "Cannot perform #{transition} from #{current_state}. Expected one of #{allowed_transitions}."
  end

  to = transition_map[current_state][transition]

  conditions = { from:, transition:, to: }

  callback_registry.runner(**conditions).callback_data(state_machine: self, **conditions).run do
    block.call if block_given?
    update_current_state(**conditions)
  end
end

#update_current_state(**conditions) ⇒ Object



115
116
117
118
# File 'foobara-0.0.130/projects/state_machine/src/state_machine.rb', line 115

def update_current_state(**conditions)
  self.current_state = conditions[:to]
  log_transition(**conditions)
end