Entry Actions

Step 4: Entry Actions

Entering a state usually does something: paying charges the card, shipping sends an email. Attach entry actions: callbacks that run when the machine arrives in a state. fire already invokes them; you register them.

  • on_enter(state, callback): register a callback (called with the order) to run whenever the order enters state; multiple callbacks on the same state run in registration order
Hints
Starter code
class Order:
    TRANSITIONS = {
        "cart": {"checkout": "pending"},
        "pending": {"pay": "paid", "cancel": "cancelled"},
        "paid": {"ship": "shipped", "refund": "cancelled"},
        "shipped": {"deliver": "delivered"},
        "delivered": {},
        "cancelled": {},
    }

    def __init__(self):
        self.state = "cart"
        self.history = ["cart"]
        self._enter_callbacks = {}       # state -> list of callbacks

    def fire(self, event):
        allowed = self.TRANSITIONS.get(self.state, {})
        if event not in allowed:
            return False
        self.state = allowed[event]
        self.history.append(self.state)
        for callback in self._enter_callbacks.get(self.state, []):
            callback(self)
        return True

    def on_enter(self, state, callback):
        """Register a callback to run when the order enters the given state."""
        # TODO: self._enter_callbacks.setdefault(state, []).append(callback)
        pass

    def can(self, event):
        return event in self.TRANSITIONS.get(self.state, {})

    def available_events(self):
        return list(self.TRANSITIONS.get(self.state, {}).keys())

    def is_terminal(self):
        return len(self.TRANSITIONS.get(self.state, {})) == 0
Write it on a laptop

The editor and the test runner need a wide screen. Open this page on a desktop browser and your progress will be waiting.