statemachine

package
v1.2.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 5, 2024 License: Apache-2.0, Apache-2.0 Imports: 5 Imported by: 0

README

statemachine

A generic state machine with simple logging mechanics compatible with any logging system. Based on a state machine presented by Rob Pike in his Lexical Scanning talk.

Documentation can be found here: https://godoc.org/github.com/johnsiilver/golib/statemachine

Documentation

Overview

Package statemachine provides a generalized state machine. It is based on a talk by Rob Pike (though I'm sure any similar implementation by him would be infinitely better).

This statemachine does not use a state type to go from state to state, but instead uses state functions to determine the next state to execute directly.

Example usage:

// myStateObj is a simple object holding our various StateFn's for our state machine. We could have done this
// example with just functions instead of methods, but this is to show you can do the same with objects holding
// attributes you want to maintain through execution.
type myStateObj struct {}

// PrintHello implements StateFn. This will be our starting state.
func (s StateObj) PrintHello() (StateFn, error) {
  fmt.Println("Hello ")
  return s.PrintWorld, nil
}

// PrintWorld implements StateFn.
func (s StateObj) PrintWorld() (StateFn, error) {
  fmt.Println("World")
  return nil, nil
}

func main() {
  so := myStateObj{}

  // Creates a new statemachine executor that will start execution with myStateObj.PrintHello().
  exec := statemachine.New("helloWorld", so.PrintHello)

  // This begins execution and gets our final error state.
  if err := exec.Execute(); err != nil {
    // Do something with the error.
  }
}

You may also be interested in having the Executor give internal run state information back to you. This is maintained inside the Executor and may be accessed by a call to Executor.Nodes() which will print out all the StateFn's called.

If you would like to have a running diagnostic mixed with your other logs, you can do the following:

// Provide your favorite logger for us to use.
log := func(s string, i ...interface{}) {
  glog.Infof(s, i...)  // glog is a logging package, see https://github.com/golang/glog
}

exec := statemachine.New("helloWorld", so.PrintHello, statemachine.LogFacility(log))
exec.Log(true)

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Executor

type Executor interface {
	// Execute executes the statemachine. It stops the first time a StateFn returns an error or returns "nil" for the returned
	// StateFn. If the last StateFn returned an error, StateFn returns an error. Execute() caused the internal state to be cleared
	// and calls the function provided to New() by the Reset option, if provided.
	Execute() error

	// Nodes returns a list of the StateFn's that were executed during the last call to Execute().
	Nodes() []string

	// Log turns on/off detailed logging of the execution state. To use this you must have provided New() with the LogFacility() option.
	Log(b bool)
}

Executor provides methods for executing a state machine. These methods are not thread safe.

func New

func New(name string, start StateFn, opts ...Option) Executor

New is the constructor for Executor. "start" is the StateFn that is first called when Executor.Execute() is called. "name" is used to prepend logging messages as a unique identifier.

type LogFn

type LogFn func(s string, i ...interface{})

LogFn represents some logging function to handle logging when Executor.Logging(true) is set. It should do variable substituion similar to fmt.Sprintf() does.

type MockExecutor

type MockExecutor struct {
	// ReturnVal is the response you wish to receive from Execute().
	ReturnVal error

	// SideEffect causes some function to run at the end of Execute. You can use this to make changes to other objects
	// that would have been affected by the real execution.
	SideEffect func()

	// NodesVal is a list of StateFn's that you wish to indicate were executed.
	NodesVal []string
}

MockExecutor implements Executor. It can be used in tests where you only need to test something happened. It will return "ReturnVal" and will call "SideEffect" before it completes. You should always use MockExecutor instead of your own fake to prevent interface changes during updates from breaking your code.

func (*MockExecutor) Execute

func (m *MockExecutor) Execute() error

Execute implements Executor.Execute().

func (*MockExecutor) Log

func (m *MockExecutor) Log(b bool)

Logging implements Executor.Logging().

func (*MockExecutor) Nodes

func (m *MockExecutor) Nodes() []string

Nodes implements Executor.Nodes().

type Option

type Option func(e *executor)

Option provides an optional argument for New().

func LogFacility

func LogFacility(l LogFn) Option

LogFacility sets up the internal log function for Executor for when Executor.Log(true) is called.

func Reset

func Reset(f func()) Option

Reset provides a function that is called when Executor.Reset() is called. This function should reset any data needed by StateFn's used in the Executor.

type StateFn

type StateFn func() (StateFn, error)

StateFn represents a function that executes at a given state. "s" represents the new state designation.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL