- Notifications
You must be signed in to change notification settings - Fork1
Functions to wrap other functions and fields/methods and to change/enhance their behavior, functionality or usage
License
gamtiq/wrapme
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Functions to wrap other functions and fields/methods and to change/enhance their behavior, functionality or usage.
Can be used for Aspect-oriented programming.
- 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.
import{intercept}from'wrapme';constapi={sum(...numList){letresult=0;for(letvalueofnumList){result+=value;}returnresult;},// Other methods// ...};// Loggingconstlog=[];functionlogger(callData){log.push({name:callData.field,args:callData.arg,result:callData.result,callNum:callData.number,time:newDate().getTime()});}constunwrap=intercept(api,'sum',logger,{listen:true});api.sum(1,2,3,4);// Returns 10, adds item to logapi.sum(1,-1,2,-2,3);// Returns 3, adds item to log// Restore original methodunwrap();
Installation↑
npm install wrapme
Usedist/wrapme.umd.development.js
ordist/wrapme.umd.production.min.js
(minified version).
Usage↑
import{intercept,wrap}from'wrapme';
constwrapme=require('wrapme');const{ intercept, wrap}=wrapme;
define(['path/to/dist/wrapme.umd.production.min.js'],function(wrapme){constintercept=wrapme.intercept;constwrap=wrapme.wrap;});
<scripttype="text/javascript"src="path/to/dist/wrapme.umd.production.min.js"></script><scripttype="text/javascript">// wrapme is available via wrapme field of window objectconstintercept=wrapme.intercept;constwrap=wrapme.wrap;</script>
Examples↑
import{intercept,wrap}from'wrapme';constapi={value:1,sum(...numList){letresult=0;for(letvalueofnumList){result+=value;}returnresult;},positive(...numList){letresult=[];for(letvalueofnumList){if(value>0){result.push(value);}}returnresult;},factorial(num){letresult=1;while(num>1){result*=num--;}returnresult;},binomCoeff(n,k){const{ factorial}=api;returnfactorial(n)/(factorial(k)*factorial(n-k));}};// Loggingconstlog=[];functionlogger(callData){if(!callData.byUnwrap){callData.settings.log.push({name:callData.field,args:callData.arg,result:callData.result,callNum:callData.number,time:newDate().getTime()});}}constunwrap=intercept(api,['sum','positive','value'],logger,{listen:true, log});api.sum(1,2,3,4);// Returns 10, adds item to logapi.positive(1,2,-3,0,10,-7);// Returns [1, 2, 10], adds item to logapi.value+=api.sum(1,-1,2,-2,3);// Changes value to 4, adds items to log// Restore original fieldsunwrap();api.positive(-1,5,0,api.value,-8);// Returns [5, 4], doesn't add items to logconsole.log("call log:\n",JSON.stringify(log,null,4));/* 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 memoizationfunctionmemoize(callData){const{ save}=callData;constkey=callData.arg.join(' ');return(keyinsave) ?save[key] :(save[key]=callData.run());}intercept(api,['factorial','binomCoeff'],memoize);api.factorial(10);api.factorial(5);api.binomCoeff(10,5);// Uses already calculated factorialsapi.binomCoeff(10,5);// Uses already calculated value// Side effectsfunctionsaveToLocalStorage(callData){if(callData.bySet){const{ save}=callData;if('id'insave){clearTimeout(save.id);}save.id=setTimeout(()=>localStorage.setItem(`wrap:${callData.field}`,typeofcallData.result==='undefined' ?callData.arg0 :callData.result),callData.settings.timeout||0);}}wrap(api,'value',saveToLocalStorage,{listen:true,timeout:50});// Validation, filtering or conversionfunctionfilter(callData){const{ arg, bySet}=callData;constargList=[];for(letitemofarg){constitemType=typeofitem;if((itemType==='number'&&!isNaN(item))||(bySet&&itemType==='string'&&item&&(item=Number(item)))){argList.push(item);}}if(argList.length||!bySet){returncallData.runApply(argList);}}wrap(api,'value',filter);api.value='some data';// value isn't changed, saveToLocalStorage isn't calledapi.value=9;// value is changed, saveToLocalStorage is calledapi.value='-53';// string is converted to number and value is changed, saveToLocalStorage is calledconstsum=wrap(api.sum,filter);constpositive=wrap(api.positive,filter);sum(false,3,NaN,newDate(),8,{},'sum','2');// Returns 11positive(true,-5,NaN,4,newDate(),1,{a:5},0,'positive',-1);// Returns [4, 1]
See additional examples in tests.
API↑
Wraps specified object's field/method or standalone function into new (wrapping) functionthat 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 interceptedand 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 operationshould be called beforehandler
and whether original's result should be returned.settings.set: boolean | Function
(optional) - Whether field's set operation should be interceptedand whether created wrapping function should be used as field's setter(by defaulttrue
for usual (non-functional) field andfalse
for method).
Returns wrapping function whentarget
is a function,or a function that restores original field/method whentarget
is an object.
An object with the following fields will be passed intohandler
:
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.
Whensettings.after
andsettings.listen
arefalse
, result ofhandler
will be returned from wrapping function.
Wraps specified object's field(s)/method(s) or standalone function into new (wrapping) functionthat 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 calledor 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 whentarget
is a function,or a function that restores original field(s)/method(s) whentarget
is an object.
Seedocs
for details.
Related projects↑
Inspiration↑
This library is inspired bymeld.
Contributing↑
In 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.
License↑
Copyright (c) 2020 Denis Sikuler
Licensed under the MIT license.
About
Functions to wrap other functions and fields/methods and to change/enhance their behavior, functionality or usage
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Uh oh!
There was an error while loading.Please reload this page.