1 | var _ = require('underscore') |
---|
2 | ; |
---|
3 | |
---|
4 | var when; |
---|
5 | |
---|
6 | // type Callback = status:Number -> result:Any -> Either HTTPResult Any |
---|
7 | // type Handler = { f:Callback, next:HTTPResult } |
---|
8 | |
---|
9 | // <init> :: status:Number? -> result:Any? -> HTTPResult |
---|
10 | var counter = 0; |
---|
11 | function HTTPResult(status,result) { |
---|
12 | this.id = counter++; |
---|
13 | this.status = 0; |
---|
14 | this.result = null; |
---|
15 | this._handlers = []; |
---|
16 | if ( status ) { |
---|
17 | this.set(status,result); |
---|
18 | } |
---|
19 | } |
---|
20 | |
---|
21 | // isSet :: Bool |
---|
22 | HTTPResult.prototype.isSet = function() { |
---|
23 | return this.status !== 0; |
---|
24 | }; |
---|
25 | |
---|
26 | // set :: status:Number -> result:Any? -> () |
---|
27 | HTTPResult.prototype.set = function(status,result) { |
---|
28 | if ( this.isSet() ) { |
---|
29 | throw new Error("Result was already set."); |
---|
30 | } |
---|
31 | if ( !_.isNumber(status) ) { |
---|
32 | throw new Error("Status must be a number."); |
---|
33 | } |
---|
34 | this.status = status; |
---|
35 | this.result = result; |
---|
36 | _.each(this._handlers,this._fire.bind(this)); |
---|
37 | }; |
---|
38 | |
---|
39 | // _fire :: Handler -> () |
---|
40 | HTTPResult.prototype._fire = function(handler) { |
---|
41 | try { |
---|
42 | var ret = handler.f(this.status,this.result); |
---|
43 | if ( ret instanceof HTTPResult ) { |
---|
44 | ret.handle(handler.next.set.bind(handler.next)); |
---|
45 | } else { |
---|
46 | handler.next.set(this.status,ret); |
---|
47 | } |
---|
48 | } catch(ex) { |
---|
49 | console.log('HTTPResult','exception',ex.toString(),ex.stack); |
---|
50 | handler.next.set(-1,ex); |
---|
51 | } |
---|
52 | }; |
---|
53 | |
---|
54 | /* handle :: Either Callback |
---|
55 | * {status:Number*: (result -> Either HTTPResult Any), |
---|
56 | * 'default'?: Callback} |
---|
57 | * -> HTTPResult |
---|
58 | */ |
---|
59 | HTTPResult.prototype.handle = function(fOrObj) { |
---|
60 | var f = _.isFunction(fOrObj) ? |
---|
61 | fOrObj : |
---|
62 | function(status,result) { |
---|
63 | if ( status in fOrObj ) { |
---|
64 | return fOrObj[status](result); |
---|
65 | } else if ( status >= 200 && status < 300 && |
---|
66 | 'success' in fOrObj ) { |
---|
67 | return fOrObj.success(status,result); |
---|
68 | } else if ( !(status >= 200 && status < 300) && |
---|
69 | 'failure' in fOrObj ) { |
---|
70 | return fOrObj.failure(status,result); |
---|
71 | } else if ( 'default' in fOrObj ) { |
---|
72 | return fOrObj['default'](status,result); |
---|
73 | } else { |
---|
74 | return result; |
---|
75 | } |
---|
76 | }; |
---|
77 | var next = new HTTPResult(); |
---|
78 | var handler = { |
---|
79 | f: f, |
---|
80 | next: next |
---|
81 | }; |
---|
82 | if ( this.isSet() ) { |
---|
83 | this._fire(handler); |
---|
84 | } else { |
---|
85 | this._handlers.push(handler); |
---|
86 | } |
---|
87 | return next; |
---|
88 | }; |
---|
89 | |
---|
90 | // then :: onSuccess:(result:Any -> status:Number)? -> |
---|
91 | // onError:(result:Any -> status:Number)? -> HTTPResult |
---|
92 | HTTPResult.prototype.then = function(onSuccess,onError) { |
---|
93 | var f = function(status,result) { |
---|
94 | if ( status >= 200 && status < 300 && onSuccess ) { |
---|
95 | if ( onSuccess ) { |
---|
96 | result = onSuccess(result,status); |
---|
97 | } |
---|
98 | } else { |
---|
99 | if ( onError ) { |
---|
100 | result = onError(result,status); |
---|
101 | } |
---|
102 | } |
---|
103 | return result; |
---|
104 | }; |
---|
105 | return this.handle(f); |
---|
106 | }; |
---|
107 | |
---|
108 | HTTPResult.prototype.asCallback = function(status) { |
---|
109 | status = status || 200; |
---|
110 | return function(ex,result) { |
---|
111 | if ( ex ) { |
---|
112 | this.set(-1,ex); |
---|
113 | } else { |
---|
114 | this.set(status,result); |
---|
115 | } |
---|
116 | }.bind(this); |
---|
117 | }; |
---|
118 | |
---|
119 | // when :: Either HTTPResult Any -> HTTPResult |
---|
120 | when = HTTPResult.when = function(status,valueOrResult) { |
---|
121 | status = status || 200; |
---|
122 | if ( valueOrResult instanceof HTTPResult ) { |
---|
123 | return valueOrResult; |
---|
124 | } else { |
---|
125 | return new HTTPResult(status,valueOrResult); |
---|
126 | } |
---|
127 | }; |
---|
128 | |
---|
129 | // ok :: result:Any? -> HTTPResult |
---|
130 | HTTPResult.ok = function(result) { |
---|
131 | return new HTTPResult(200,result); |
---|
132 | }; |
---|
133 | |
---|
134 | // fail :: error:Any? -> HTTPResult |
---|
135 | HTTPResult.fail = function(error) { |
---|
136 | return new HTTPResult(-1,error); |
---|
137 | }; |
---|
138 | |
---|
139 | module.exports = HTTPResult; |
---|