Batman.js is no longer in production at Shopify and is not actively maintained.

This website is left for reference (and for old times' sake).

batman.js

Batman.StateMachine implements the finite-state machine model. In batman.js, it is used to track a record's lifecycle. To use Batman.StateMachine, extend it and define a transition table with @transitions. For example:

test 'StateMachine defines valid transitions', ->
  class BatsuitStateMachine extends Batman.StateMachine
    @transitions
      removeSuit:
        from: ['wearingPants', 'wearingShoes']
        to: 'unsuited'
      putOnPants:
        from: ['unsuited']
        to: 'wearingPants'
      putOnShoes:
        from: ['wearingPants']
        to: 'wearingShoes'

  ok BatsuitStateMachine::putOnShoes, "Transition names are prototype functions"

  batsuit = new BatsuitStateMachine('unsuited')
  equal batsuit.get('state'), 'unsuited', "It starts with the provided state"
  equal batsuit.get('isUnsuited'), true, "State names are accessors with 'is'"
  equal batsuit.get('isWearingShoes'), false, "State names are accessors with 'is'"

  equal batsuit.putOnPants(), true, "Successful transitions return true"

  batsuit = new BatsuitStateMachine('unsuited')
  equal batsuit.putOnShoes(), false, "Don't put on your shoes before putting on your pants!"
  • constructor(startState : String) : StateMachine

    Returns a new StateMachine in state startState.

  • @transitions(transitionTable : object)

    Defines the transition table for the StateMachine. Each key-value pair of transitionTable defines an input event for the StateMachine. It should have the form:

    class StateMachineSubclass extends Batman.StateMachine
      @transitions
        inputEvent:
          from: ["startingState", "otherStartingState"] # any number of valid starting states
          to: "endingState"

    For each input event in transitionTable, prototype function inputEvent is created. Given StateMachine instance stateMachineInstance, calling stateMachineInstance.inputEvent() tries to transition from stateMachineInstance's current state to the to state defined in transitionTable. The transition is successful if stateMachineInstance.state is a member of inputEvent's from states in transitionTable. It returns true if the transition was successful, otherwise it returns false. For example:

    stateMachineInstance = new StateMachineSubclass('startingState')
    stateMachineInstance.get('state') # => 'startingState'
    stateMachineInstance.inputEvent() # => true
    stateMachineInstance.get('state') # => 'endingState'
    stateMachineInstance.inputEvent() # => false, because 'endingState' not in ['startingState', 'otherStartingState']

    StateMachine doesn't automatically throw StateMachine.InvalidTransitionErrors.

    For each state defined in from and to, an "is#{capitalize(state)}" accessor is available on the StateMachine which returns true, false, or undefined. For example:

    stateMachineInstance.get('state')              # => 'endingState'
    stateMachineInstance.get('isEndingState')      # => true
    stateMachineInstance.get('isStartingState')    # => false
    stateMachineInstance.get('isUnspecifiedState') # => undefined
  • transitionTable : object

    Returns a representation of the StateMachine's transition table. For example, an instance of BatsuitStateMachine above would return:

    removeSuit:
      wearingPants: "unsuited"
      wearingShoes: "unsuited"
    putOnPants:
      unsuited: "wearingPants"
    putOnShoes:
      wearingPants: "wearingShoes"
  • state

    Returns the current state of the StateMachine.

Help us improve our documentation!

Contributions to this page are welcome on Github. If you find a problem but you cannot fix it, please open an issue.

Discussion regarding batman.js documentation is also welcome on our mailing list.