[525] | 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; |
---|
[487] | 11 | function HTTPResult(status,result) { |
---|
[525] | 12 | this.id = counter++; |
---|
[487] | 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 | |
---|
[525] | 26 | // set :: status:Number -> result:Any? -> () |
---|
[487] | 27 | HTTPResult.prototype.set = function(status,result) { |
---|
[525] | 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 | } |
---|
[487] | 34 | this.status = status; |
---|
| 35 | this.result = result; |
---|
[525] | 36 | _.each(this._handlers,this._fire.bind(this)); |
---|
[487] | 37 | }; |
---|
| 38 | |
---|
[525] | 39 | // _fire :: Handler -> () |
---|
| 40 | HTTPResult.prototype._fire = function(handler) { |
---|
[527] | 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); |
---|
[487] | 51 | } |
---|
| 52 | }; |
---|
| 53 | |
---|
[525] | 54 | /* handle :: Either Callback |
---|
| 55 | * {status:Number*: (result -> Either HTTPResult Any), |
---|
| 56 | * 'default'?: Callback} |
---|
[487] | 57 | * -> HTTPResult |
---|
| 58 | */ |
---|
| 59 | HTTPResult.prototype.handle = function(fOrObj) { |
---|
[525] | 60 | var f = _.isFunction(fOrObj) ? |
---|
[487] | 61 | fOrObj : |
---|
| 62 | function(status,result) { |
---|
| 63 | if ( status in fOrObj ) { |
---|
| 64 | return fOrObj[status](result); |
---|
[531] | 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); |
---|
[487] | 71 | } else if ( 'default' in fOrObj ) { |
---|
| 72 | return fOrObj['default'](status,result); |
---|
| 73 | } else { |
---|
| 74 | return result; |
---|
| 75 | } |
---|
| 76 | }; |
---|
[525] | 77 | var next = new HTTPResult(); |
---|
| 78 | var handler = { |
---|
| 79 | f: f, |
---|
| 80 | next: next |
---|
| 81 | }; |
---|
[487] | 82 | if ( this.isSet() ) { |
---|
[525] | 83 | this._fire(handler); |
---|
[487] | 84 | } else { |
---|
[525] | 85 | this._handlers.push(handler); |
---|
[487] | 86 | } |
---|
[525] | 87 | return next; |
---|
[487] | 88 | }; |
---|
| 89 | |
---|
[525] | 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 | |
---|
[487] | 139 | module.exports = HTTPResult; |
---|