UNPKG

4.55 kBtext/coffeescriptView Raw
1# States = require("node-state")
2States = require("../")
3# You can use error-doc to create beautiful error declares and checks.
4Errors = require("error-doc").create()
5 .define("CommunicationFailure")
6 .define("BarginFailure")
7 .define("ProgrammerError")
8 .generate()
9# You DON'T have to define a `Action` when using node-state.
10# I just write them here for easier understanding.
11
12
13class BuyCarProcedure extends States
14 # How to buy a car
15 # 1. goto shop
16 # 2. bargin with shop manager (again and again)
17 # 3. pay
18 constructor:()->
19 super()
20 # `@give("startSignal")` should be called
21 # to start the statemachine. If we are not
22 # wait for `"startSignal"`, giving that one will
23 # do nothing.
24 @waitFor "startSignal",()=>
25 # State will be changed
26 # @atGotoShop will be called
27 @setState "gotoShop"
28 atGotoShop:(sole)->
29 @waitFor "shopName",(name)=>
30 @data.shopName = name
31 @walkToShop name,(err)=>
32 # Always do a integrity check for async action.
33 # `sole` is given as state method parameters.
34 if not @checkSole sole
35 return
36 # go panic on error
37 if err
38 @error err
39 return
40 @setState "thinkOfAStartPrice"
41 atThinkOfAStartPrice:()->
42 @waitFor "startPrice",(price)=>
43 @data.myPrice = price
44 @setState "bargin"
45 atBargin:(sole)->
46 if not @data.myPrice
47 @setState "thinkOfAStartPrice"
48 return
49 @bargin @data.myPrice,(err,result)=>
50 if not @checkSole sole
51 return
52 if err
53 @error err
54 return
55 if not result
56 @data.myPrice += 100
57 @setState "bargin"
58 else
59 @setState "pay"
60 atPay:(sole)->
61 @waitFor "money",(money)=>
62 @pay money,(err)=>
63 if not @checkSole sole
64 return
65 if err
66 @error err
67 return
68 @setState "paid"
69 atPaid:()->
70 @emit "paid"
71 # Actions behaves just like a normal function.
72 # They don't and shouldn't change state machine state, and
73 # should better not change `@data`.
74 walkToShop:(name,callback)->
75 console.log "I walk to #{name} to buy a car"
76 setTimeout callback,10
77 bargin:(price,callback)->
78 accept = 1000
79 err = null
80 console.log "I bid at price #{price}"
81 if Math.random() > 0.85
82 err = new Errors.CommunicationFailure("the manager say something I don't understand")
83 else if Math.random() > 0.8
84 err = new Errors.BarginFailure("the manager don't want to bargin with me any more")
85 callback err,price > accept
86 pay:(money,callback)->
87 console.log "#{money} is given away."
88 callback null
89
90
91# All error handlings
92p = new BuyCarProcedure()
93# you can see all the state jump and actions
94p.debug({name:"BuyCar"})
95
96
97p.on "wait/shopName",()=>
98 p.give("shopName","the car shop near my house")
99p.on "wait/startPrice",()=>
100 p.give("startPrice",500)
101p.on "wait/money",()=>
102 p.give("money",p.data.myPrice + "$")
103
104# Suppose we are in a parent statemachine who
105# is responsible for the error handling.
106# If the error is simple enough and don't not require any third party
107# information to handle it, we can also consider that sort of
108# error a valid state.
109# But here we handle them outside the statemachine to expalin a standard
110# panic recover strategy here.
111
112parentStateMachineData = {}
113d = parentStateMachineData
114p.on "panic",(err,state)=>
115 if err instanceof Errors.CommunicationFailure and state is "bargin"
116 # it's OK just bargin again!
117 p.recover()
118 p.setState "bargin"
119 else if err instanceof Errors.BarginFailure and state is "bargin"
120 d.failCount ?= 0
121 d.failCount++
122 if d.failCount < 3
123 # we just try again
124 p.recover()
125 p.setState "bargin"
126 else
127 # a error that is impossible to handle
128 console.log "I can't bargin with this asshole any more!"
129 process.exit(0)
130 else
131 throw new Errors.ProgrammerError("I don't expect this kind of situation",{via:err,state:state})
132p.on "paid",()->
133 console.log "paid!"
134# finally start the stae machine
135p.give("startSignal")