wrapme
Functions to wrap other functions and fields/methods and to change/enhance their behavior, functionality or usage.
Can be used for Aspect-oriented programming.
Features
- Wrap a single function/field/method (by
wrap
) or several fields and methods at once (byintercept
). - Wrap only field's get operation (
get
option) or set operation (set
option), or both (by default). - Provide special getter and/or setter for wrapped field if it is necessary.
- Call original function/method or field's operation before (use
before
orlisten
option), after (useafter
option) and/or insidehandler
(userun()
orrunApply()
). - Totally control calling of original function/method or field's operation inside
handler
: call depending on condition, filter/validate/convert passed arguments and/or provide another arguments. - Return result of original function/method or field's operation, or any other value from
handler
. - Save necessary data between
handler
calls. - Restore original fields/methods when it is needed.
- Does not have dependencies and can be used in ECMAScript 5+ environment.
- Small size.
; const api = { let result = 0; for let value of numList result += value; return result; } // Other methods // ...; // Logging const log = ; { log;} const unwrap = ; api; // Returns 10, adds item to logapi; // Returns 3, adds item to log // Restore original method;
Table of contents
↑
InstallationNode
npm install wrapme
AMD, <script>
Use dist/wrapme.umd.development.js
or dist/wrapme.umd.production.min.js
(minified version).
↑
UsageECMAScript 6+
;
Node
const wrapme = ;const intercept wrap = wrapme;
AMD
;
<script>
↑
Examples; const api = value: 1 { let result = 0; for let value of numList result += value; return result; } { let result = ; for let value of numList if value > 0 result; return result; } { let result = 1; while num > 1 result *= num--; return result; } { const factorial = api; return / * ; }; // Logging const log = ; { if ! callDatabyUnwrap callDatasettingslog; } const unwrap = ; api; // Returns 10, adds item to logapi; // Returns [1, 2, 10], adds item to logapivalue += api; // Changes value to 4, adds items to log // Restore original fields; api; // Returns [5, 4], doesn't add items to log console;/* log looks like: [ { "name": "sum", "args": [ 1, 2, 3, 4 ], "result": 10, "callNum": 1, "time": 1586602348174 }, { "name": "positive", "args": [ 1, 2, -3, 0, 10, -7 ], "result": [ 1, 2, 10 ], "callNum": 1, "time": 1586602348174 }, { "name": "value", "args": [], "result": 1, "callNum": 1, "time": 1586602348174 }, { "name": "sum", "args": [ 1, -1, 2, -2, 3 ], "result": 3, "callNum": 2, "time": 1586602348174 }, { "name": "value", "args": [ 4 ], "result": 4, "callNum": 2, "time": 1586602348175 } ]*/ // Simple memoization { const save = callData; const key = callDataarg; return key in save ? savekey : savekey = callData;} ; api;api; api; // Uses already calculated factorials api; // Uses already calculated value // Side effects { if callDatabySet const save = callData; if 'id' in save ; saveid = ; } ; // Validation, filtering or conversion { const arg bySet = callData; const argList = ; for let item of arg const itemType = typeof item; if itemType === 'number' && ! || bySet && itemType === 'string' && item && item = Numberitem argList; if argListlength || ! bySet return callData; } ;apivalue = 'some data'; // value isn't changed, saveToLocalStorage isn't calledapivalue = 9; // value is changed, saveToLocalStorage is calledapivalue = '-53'; // string is converted to number and value is changed, saveToLocalStorage is called const sum = ;const positive = ; ; // Returns 11; // Returns [4, 1]
See additional examples in tests.
↑
APIwrap(target, field, handler?, settings?): Function
Wraps specified object's field/method or standalone function into new (wrapping) function that calls passed handler which eventually may run wrapped function or get/set field's value.
Arguments:
target: Function | object
- Function that should be wrapped or an object whose field/method will be wrapped and replaced.field: Function | string
- Name of field/method that should be wrapped or a handler when function is passed fortarget
parameter.handler: Function | object
- A function (interceptor) that should be executed when newly created function is called or get/set operation for the field is applied, or optional settings when function is passed fortarget
parameter.settings: object
- Optional settings that will be available inhandler
.settings.after: boolean
(optional) - Whether original function, method or field's operation should be called afterhandler
.settings.before: boolean
(optional) - Whether original function, method or field's operation should be called beforehandler
.settings.bind: boolean
(optional) - Whether wrapping function should be bound totarget
object.settings.context: object
(optional) - Context (this
) that should be used forhandler
call.settings.data: any
(optional) - Any data that should be available inhandler
.settings.get: boolean | Function
(optional) - Whether field's get operation should be intercepted and whether created wrapping function should be used as field's getter (by defaulttrue
for usual (non-functional) field andfalse
for method).settings.listen: boolean
(optional) - Whether original function, method or field's operation should be called beforehandler
and whether original's result should be returned.settings.set: boolean | Function
(optional) - Whether field's set operation should be intercepted and whether created wrapping function should be used as field's setter (by defaulttrue
for usual (non-functional) field andfalse
for method).
Returns wrapping function when target
is a function,
or a function that restores original field/method when target
is an object.
An object with the following fields will be passed into handler
:
arg: any[]
- Array of arguments that were passed to the wrapping function.arg0: any
- Value ofarg[0]
.byCall: boolean
- Whether wrapping function is called as object's method or as usual function (by a call operation).byGet: boolean
- Whether wrapping function is called to get field's value (by get operation, as field's getter).bySet: boolean
- Whether wrapping function is called to set field's value (by set operation, as field's setter).byUnwrap: boolean
- Whether wrapping function (andhandler
) is called during unwrapping.context: object
- Context (this
) with which wrapping function is called.data: any
- Value ofsettings.data
option.field: string | undefined
- Name of the field or method that was wrapped.fieldWrap: boolean
- Whether field's get and/or set operation was wrapped.funcWrap: boolean
- Whether standalone function (not object's field/method) was wrapped.get: (() => any) | undefined
- Function that returns field's current value if field was wrapped.method: string
- Name of the method or function that was wrapped.methodWrap: boolean
- Whether method was wrapped.number: number
- Number ofhandler
's call (starting from 1).result: any
- Result of original function/method when it is called beforehandler
.run: (...args?) => any
- Method that calls original function/method or field's getter/setter; by default values fromarg
will be used as arguments; but you may pass arguments torun
and they will be used instead of the original arguments.runApply: (any[]?) => any
- Similar torun
but accepts an array of new arguments, e.g.runApply([1, 2, 3])
is equivalent torun(1, 2, 3)
; if the first argument ofrunApply
is not an array it will be wrapped into array (i.e.[arguments[0]]
); only the first argument ofrunApply
is used.save: object
- An object that can be used to preserve some values betweenhandler
calls.set: ((value: any) => any) | undefined
- Function that changes field's current value if field was wrapped.settings: object
- Value ofsettings
parameter; except forsettings.bind
andsettings.context
, it is possible to change any setting to alter following execution; so be careful when you change a field's value ofsettings
object.target: ((...args) => any) | string
- Original function or method that was wrapped, or name of wrapped field.targetObj: object | null
- An object whose field/method was wrapped and replaced.value: any
- Previous value returned by wrapping function.
When settings.after
and settings.listen
are false
, result of handler
will be returned from wrapping function.
intercept(target, field, handler?, settings?): Function
Wraps specified object's field(s)/method(s) or standalone function into new (wrapping) function that calls passed handler which eventually may run wrapped function or get/set field's value.
Arguments:
target: Function | object
- Function that should be wrapped or an object whose field(s)/method(s) will be wrapped and replaced.field: Function | string | string[]
- Name of field/method (or list of field/method names) that should be wrapped or a handler when function is passed fortarget
parameter.handler: Function | object
- A function (interceptor) that should be executed when newly created function is called or get/set operation for the field is applied, or settings when function is passed fortarget
parameter.settings: object
- Optional settings that will be available inhandler
. Seewrap
for details.
Returns wrapping function when target
is a function,
or a function that restores original field(s)/method(s) when target
is an object.
See doc
folder for details.
↑
Related projects↑
InspirationThis library is inspired by meld.
↑
ContributingIn lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code.
↑
LicenseCopyright (c) 2020 Denis Sikuler
Licensed under the MIT license.