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 | var ret = handler.f(this.status,this.result); |
---|
42 | if ( ret instanceof HTTPResult ) { |
---|
43 | ret.handle(handler.next.set.bind(handler.next)); |
---|
44 | } else { |
---|
45 | handler.next.set(this.status,ret); |
---|
46 | } |
---|
47 | }; |
---|
48 | |
---|
49 | /* handle :: Either Callback |
---|
50 | * {status:Number*: (result -> Either HTTPResult Any), |
---|
51 | * 'default'?: Callback} |
---|
52 | * -> HTTPResult |
---|
53 | */ |
---|
54 | HTTPResult.prototype.handle = function(fOrObj) { |
---|
55 | var f = _.isFunction(fOrObj) ? |
---|
56 | fOrObj : |
---|
57 | function(status,result) { |
---|
58 | if ( status in fOrObj ) { |
---|
59 | return fOrObj[status](result); |
---|
60 | } else if ( 'default' in fOrObj ) { |
---|
61 | return fOrObj['default'](status,result); |
---|
62 | } else { |
---|
63 | return result; |
---|
64 | } |
---|
65 | }; |
---|
66 | var next = new HTTPResult(); |
---|
67 | var handler = { |
---|
68 | f: f, |
---|
69 | next: next |
---|
70 | }; |
---|
71 | if ( this.isSet() ) { |
---|
72 | this._fire(handler); |
---|
73 | } else { |
---|
74 | this._handlers.push(handler); |
---|
75 | } |
---|
76 | return next; |
---|
77 | }; |
---|
78 | |
---|
79 | // then :: onSuccess:(result:Any -> status:Number)? -> |
---|
80 | // onError:(result:Any -> status:Number)? -> HTTPResult |
---|
81 | HTTPResult.prototype.then = function(onSuccess,onError) { |
---|
82 | var f = function(status,result) { |
---|
83 | if ( status >= 200 && status < 300 && onSuccess ) { |
---|
84 | if ( onSuccess ) { |
---|
85 | result = onSuccess(result,status); |
---|
86 | } |
---|
87 | } else { |
---|
88 | if ( onError ) { |
---|
89 | result = onError(result,status); |
---|
90 | } |
---|
91 | } |
---|
92 | return result; |
---|
93 | }; |
---|
94 | return this.handle(f); |
---|
95 | }; |
---|
96 | |
---|
97 | HTTPResult.prototype.asCallback = function(status) { |
---|
98 | status = status || 200; |
---|
99 | return function(ex,result) { |
---|
100 | if ( ex ) { |
---|
101 | this.set(-1,ex); |
---|
102 | } else { |
---|
103 | this.set(status,result); |
---|
104 | } |
---|
105 | }.bind(this); |
---|
106 | }; |
---|
107 | |
---|
108 | // when :: Either HTTPResult Any -> HTTPResult |
---|
109 | when = HTTPResult.when = function(status,valueOrResult) { |
---|
110 | status = status || 200; |
---|
111 | if ( valueOrResult instanceof HTTPResult ) { |
---|
112 | return valueOrResult; |
---|
113 | } else { |
---|
114 | return new HTTPResult(status,valueOrResult); |
---|
115 | } |
---|
116 | }; |
---|
117 | |
---|
118 | // ok :: result:Any? -> HTTPResult |
---|
119 | HTTPResult.ok = function(result) { |
---|
120 | return new HTTPResult(200,result); |
---|
121 | }; |
---|
122 | |
---|
123 | // fail :: error:Any? -> HTTPResult |
---|
124 | HTTPResult.fail = function(error) { |
---|
125 | return new HTTPResult(-1,error); |
---|
126 | }; |
---|
127 | |
---|
128 | module.exports = HTTPResult; |
---|