Class: ScripTTY::Util::FSM

Inherits:
Object
  • Object
show all
Defined in:
lib/scriptty/util/fsm.rb,
lib/scriptty/util/fsm/definition_parser.rb

Overview

:nodoc:

Defined Under Namespace

Classes: DefinitionParser, NoMatch

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}, &block) ⇒ FSM

Initialize a FSM

The following options are supported:

:definition

FSM definition, as a string

:callback

See the documentation for the callback attribute. A block may be given to the new method instead of being passed as an option.

:callback_method

See the documentation for the callback_method attribute.



84
85
86
87
88
89
90
91
# File 'lib/scriptty/util/fsm.rb', line 84

def initialize(options={}, &block)
  @redirect = nil
  @input_sequence = []
  @callback = options[:callback] || block
  @callback_method = (options[:callback_method] || :call).to_sym
  load_definition(options[:definition])
  reset!
end

Instance Attribute Details

#callbackObject

Object that will receive named events.

When processing reaches a named event, the FSM will invoke the method specified by the callback_method attribute (by default, “call”), passing it the name of the event and the FSM object.



52
53
54
# File 'lib/scriptty/util/fsm.rb', line 52

def callback
  @callback
end

#callback_methodObject

The name of the method to invoke on the callback object. (default: :call)



55
56
57
# File 'lib/scriptty/util/fsm.rb', line 55

def callback_method
  @callback_method
end

#inputObject (readonly)

The current (or previous) input.



33
34
35
# File 'lib/scriptty/util/fsm.rb', line 33

def input
  @input
end

#input_sequenceObject (readonly)

An array of inputs received since the initial state.

This allows a callback to get the contents of a complete escape sequence.



39
40
41
# File 'lib/scriptty/util/fsm.rb', line 39

def input_sequence
  @input_sequence
end

#next_stateObject (readonly)

The next state



45
46
47
# File 'lib/scriptty/util/fsm.rb', line 45

def next_state
  @next_state
end

#redirectObject

When not nil, all inputs are redirected, bypassing normal processing (but input_sequence is still updated).

If redirect is a symbol, then the specified method is called on the callback object (this implies that the callback object can’t be an ordinary Proc in this case). Otherwise, the “call” method on the redirect object is invoked.

The redirect function will be passed a reference to the FSM, which it can use to access methods such as input, input_sequence, reset!, etc.

If the redirect function returns true, the process method returns immediately. If the redirect function returns false, the redirection is removed and the current input is processed normally.



71
72
73
# File 'lib/scriptty/util/fsm.rb', line 71

def redirect
  @redirect
end

#stateObject (readonly)

The current state



42
43
44
# File 'lib/scriptty/util/fsm.rb', line 42

def state
  @state
end

Instance Method Details

#fire_event(event) ⇒ Object

Invoke the callback for the specified event.



107
108
109
# File 'lib/scriptty/util/fsm.rb', line 107

def fire_event(event)
  @callback.__send__(@callback_method, event.to_sym, self) if @callback
end

#initial_state?Boolean

Return true if we are at the initial state (i.e. @state == 1 and !@redirect)

Returns:

  • (Boolean)


102
103
104
# File 'lib/scriptty/util/fsm.rb', line 102

def initial_state?
  @state == 1 && !@redirect
end

#process(input) ⇒ Object

Process the specified input.

If there is no matching entry in the state transition table, ScripTTY::Util::FSM::NoMatch is raised.

Raises:



115
116
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
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/scriptty/util/fsm.rb', line 115

def process(input)
  # Switch to @next_state
  @state = @next_state

  # Reset @input_sequence if we are at the initial state.  Otherwise,
  # append the current input to @input_sequence.
  if initial_state?
    @input_sequence = [input]
  else
    @input_sequence << input
  end

  # Set @input and call the redirect object (if necessary)
  @input = input
  if @redirect
    if @redirect.is_a?(Symbol)
      result = @callback.send(@redirect, self)
    else
      result = @redirect.call(self)
    end
    return true if result
    @redirect = nil
  end

  # The redirect function might invoke the reset! method, so fix
  # @input_sequence for that case.
  @input_sequence = [input] if @state == 1

  # Look up for a state transition for the specified input
  t = @state_transitions[@state][input]
  t ||= @state_transitions[@state][:other]
  raise NoMatch.new("No matching transition for input_sequence=#{input_sequence.inspect} (state=#{state.inspect})", input_sequence, state) unless t

  # Set next_state and invoke the callback, if any is specified for this state transition.
  @next_state = t[:next_state]
  fire_event(t[:event]) if t[:event]

  # Return true
  true
end

#reset!Object

Set state and next_state to 1, and clear the redirect.



94
95
96
97
98
99
# File 'lib/scriptty/util/fsm.rb', line 94

def reset!
  @state = 1
  @next_state = 1
  @redirect = nil
  nil
end