Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

Functions to wrap other functions and fields/methods and to change/enhance their behavior, functionality or usage

License

NotificationsYou must be signed in to change notification settings

gamtiq/wrapme

Repository files navigation

NPM version

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 (bywrap) 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 (usebefore orlisten option),after (useafter option) and/or insidehandler (userun() orrunApply()).
  • Totally control calling of original function/method or field's operation insidehandler: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 fromhandler.
  • Save necessary data betweenhandler 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();

See more examples below.

Table of contents

Installation

Node

npm install wrapme

AMD, <script>

Usedist/wrapme.umd.development.js ordist/wrapme.umd.production.min.js (minified version).

Usage

ECMAScript 6+

import{intercept,wrap}from'wrapme';

Node

constwrapme=require('wrapme');const{ intercept, wrap}=wrapme;

AMD

define(['path/to/dist/wrapme.umd.production.min.js'],function(wrapme){constintercept=wrapme.intercept;constwrap=wrapme.wrap;});

<script>

<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

wrap(target, field, handler?, settings?): Function

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.

intercept(target, field, handler?, settings?): 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.


[8]ページ先頭

©2009-2025 Movatter.jp