Created
March 3, 2016 13:57
-
-
Save unscriptable/0d5f5a157f3dd9a06021 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
/*@flow*/ | |
// TODO: parameterize by argument type when flow supports it: | |
// type Func<A,R> = (...args:A) => R | |
// type Adviser<F:Func<A,R>> = (x:F) => F | |
// export type TraceAdvice<A,R> = (args:A, result:R) => mixed | |
type Func<R> = () => R | |
type Adviser<F:Function> = (x:F) => F | |
export type TraceAdvice<R> | |
= (args:Array<mixed>, result:R) => mixed | |
& (args:Array<mixed>, result:void, error:Error) => mixed | |
export type BeforeAdvice = (args:Array<mixed>) => mixed | |
export type AfterAdvice<R> = (result:R) => mixed | |
export type OnAdviceError = (e:Error) => mixed | |
// Execute a side-effect before a function. | |
export const before | |
: (advice:BeforeAdvice, onError:OnAdviceError) => Adviser | |
= (advice, onError = logErrorToConsole) => { | |
const safeAdvice = _captureError(onError, advice) | |
return _before(safeAdvice) | |
} | |
// Execute a side-effect after a function. | |
export const after | |
: (advice:AfterAdvice, onError:OnAdviceError) => Adviser | |
= (advice, onError = logErrorToConsole) => { | |
const safeAdvice = _captureError(onError, advice) | |
return _after(safeAdvice) | |
} | |
// Call a function to record the arguments and result of a function. | |
// If the function throws, the Error is sent as the third parameter. | |
export const trace | |
: (advice:TraceAdvice, onError:OnAdviceError) => Adviser | |
= (advice, onError = logErrorToConsole) => { | |
const safeAdvice = _captureError(onError, advice) | |
return _trace(safeAdvice) | |
} | |
// A Collection of advise functions. | |
export type Advice = { | |
before (advice:BeforeAdvice, onError:OnAdviceError): Adviser, | |
after (advice:AfterAdvice, onError:OnAdviceError): Adviser, | |
trace (advice:TraceAdvice, onError:OnAdviceError): Adviser | |
} | |
// Creates a collection of similarly-defaulted advice functions. | |
const advice | |
: (defaultError:OnAdviceError) => Advice | |
= (defaultError) => { | |
return { | |
before: (advice, onError = defaultError) => _before(advice, onError), | |
after: (advice, onError = defaultError) => _after(advice, onError), | |
trace: (advice, onError = defaultError) => _trace(advice, onError) | |
} | |
} | |
export default advice | |
export const logErrorToConsole | |
: OnAdviceError | |
= (e) => { console.log(e) } | |
export const squelchError | |
: OnAdviceError | |
= (e) => {} | |
// --------------- Exported for testing --------------- | |
export const _before | |
: (advice:BeforeAdvice) => Adviser | |
= (advice) => (f) => (...args) => { | |
advice(...args) | |
return f(...args) | |
} | |
export const _after | |
: (advice:AfterAdvice) => Adviser | |
= (advice) => (f) => (...args) => { | |
const result = f(...args) | |
advice(result) | |
return result | |
} | |
export const _trace | |
: (record:TraceAdvice) => Adviser | |
= (record) => (f) => (...args) => { | |
let result | |
try { | |
result = f(...args) | |
} | |
catch (err) { | |
record(args, undefined, err) | |
throw err | |
} | |
record(args, result) | |
return result | |
} | |
// TODO: Increase the type safety of this function | |
export const _captureError | |
: (onError:OnAdviceError, advice:Function) => Function | |
= (onError, advice) => (...args) => { | |
try { | |
advice(...args) | |
} | |
catch (err) { | |
onError(err) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment