Skip to content

Commit d3fa7ca

Browse files
Jan ZombikJan Zombik
authored andcommitted
feat: add OnExitFrom configuration method (closes #75)
1 parent 2a5b0eb commit d3fa7ca

File tree

4 files changed

+71
-0
lines changed

4 files changed

+71
-0
lines changed

config.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,16 @@ func (sc *StateConfiguration) OnExit(action ActionFunc) *StateConfiguration {
160160
return sc
161161
}
162162

163+
// OnExitWith specifies an action that will execute when transitioning from the configured state with a specific trigger.
164+
func (sc *StateConfiguration) OnExitWith(trigger Trigger, action ActionFunc) *StateConfiguration {
165+
sc.sr.ExitActions = append(sc.sr.ExitActions, actionBehaviour{
166+
Action: action,
167+
Description: newinvocationInfo(action),
168+
Trigger: &trigger,
169+
})
170+
return sc
171+
}
172+
163173
// SubstateOf sets the superstate that the configured state is a substate of.
164174
// Substates inherit the allowed transitions of their superstate.
165175
// When entering directly into a substate from outside of the superstate,

example_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ func Example() {
6666

6767
phoneCall.Configure(stateOnHold).
6868
SubstateOf(stateConnected).
69+
OnExitWith(triggerPhoneHurledAgainstWall, func(ctx context.Context, args ...any) error {
70+
onWasted()
71+
return nil
72+
}).
6973
Permit(triggerTakenOffHold, stateConnected).
7074
Permit(triggerPhoneHurledAgainstWall, statePhoneDestroyed)
7175

@@ -90,6 +94,7 @@ func Example() {
9094
// Microphone muted!
9195
// Microphone unmuted!
9296
// Volume set to 11!
97+
// Wasted!
9398
// [Timer:] Call ended at 11:30am
9499
// State is PhoneDestroyed
95100

@@ -111,6 +116,10 @@ func onDialed(callee string) {
111116
fmt.Printf("[Phone Call] placed for : [%s]\n", callee)
112117
}
113118

119+
func onWasted() {
120+
fmt.Println("Wasted!")
121+
}
122+
114123
func startCallTimer(_ context.Context, _ ...any) error {
115124
fmt.Println("[Timer:] Call started at 11:00am")
116125
return nil

graph_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,10 @@ func phoneCall() *stateless.StateMachine {
112112

113113
phoneCall.Configure(stateOnHold).
114114
SubstateOf(stateConnected).
115+
OnExitWith(triggerPhoneHurledAgainstWall, func(ctx context.Context, args ...any) error {
116+
onWasted()
117+
return nil
118+
}).
115119
Permit(triggerTakenOffHold, stateConnected).
116120
Permit(triggerPhoneHurledAgainstWall, statePhoneDestroyed)
117121

statemachine_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,31 @@ func TestStateMachine_Fire_ParametersSuppliedToFireArePassedToEntryAction(t *tes
368368
}
369369
}
370370

371+
func TestStateMachine_Fire_ParametersSuppliedToFireArePassedToExitAction(t *testing.T) {
372+
sm := NewStateMachine(stateB)
373+
sm.SetTriggerParameters(triggerX, reflect.TypeOf(""), reflect.TypeOf(0))
374+
sm.Configure(stateB).Permit(triggerX, stateC)
375+
376+
var (
377+
entryArg1 string
378+
entryArg2 int
379+
)
380+
sm.Configure(stateB).OnExitWith(triggerX, func(_ context.Context, args ...any) error {
381+
entryArg1 = args[0].(string)
382+
entryArg2 = args[1].(int)
383+
return nil
384+
})
385+
suppliedArg1, suppliedArg2 := "something", 2
386+
sm.Fire(triggerX, suppliedArg1, suppliedArg2)
387+
388+
if entryArg1 != suppliedArg1 {
389+
t.Errorf("entryArg1 = %v, want %v", entryArg1, suppliedArg1)
390+
}
391+
if entryArg2 != suppliedArg2 {
392+
t.Errorf("entryArg2 = %v, want %v", entryArg2, suppliedArg2)
393+
}
394+
}
395+
371396
func TestStateMachine_OnUnhandledTrigger_TheProvidedHandlerIsCalledWithStateAndTrigger(t *testing.T) {
372397
sm := NewStateMachine(stateB)
373398
var (
@@ -570,6 +595,29 @@ func TestStateMachine_Fire_IgnoreVsPermitReentryFrom(t *testing.T) {
570595
}
571596
}
572597

598+
func TestStateMachine_Fire_IgnoreVsPermitReentryExitWith(t *testing.T) {
599+
sm := NewStateMachine(stateA)
600+
var calls int
601+
sm.Configure(stateA).
602+
OnExitWith(triggerX, func(_ context.Context, _ ...any) error {
603+
calls += 1
604+
return nil
605+
}).
606+
OnExitWith(triggerY, func(_ context.Context, _ ...any) error {
607+
calls += 1
608+
return nil
609+
}).
610+
PermitReentry(triggerX).
611+
Ignore(triggerY)
612+
613+
sm.Fire(triggerX)
614+
sm.Fire(triggerY)
615+
616+
if calls != 1 {
617+
t.Errorf("calls = %d, want %d", calls, 1)
618+
}
619+
}
620+
573621
func TestStateMachine_Fire_IfSelfTransitionPermited_ActionsFire_InSubstate(t *testing.T) {
574622
sm := NewStateMachine(stateA)
575623
var onEntryStateBfired, onExitStateBfired, onExitStateAfired bool

0 commit comments

Comments
 (0)