Skip to content

Instantly share code, notes, and snippets.

@chamberlainpi
Last active February 28, 2020 14:45
Show Gist options
  • Save chamberlainpi/6ea7de99b47619545727ed6f574d7017 to your computer and use it in GitHub Desktop.
Save chamberlainpi/6ea7de99b47619545727ed6f574d7017 to your computer and use it in GitHub Desktop.
/**
@author Pierre Chamberlain
@description This is a demo of using the 'proxyFieldsOf(...)' method.
*/
const { proxyFieldsOf } = require('./proxy-fields-of');
const trace = console.log.bind(console);
const SOME_NAMESPACE = {
someObject: {
hi(name) {
trace(`Hello "${name}"!`);
return 'foobar';
}
}
};
const PROXY_NAMESPACE = proxyFieldsOf(SOME_NAMESPACE, {
fullpath: 'SOME_NAMESPACE',
recursive: true,
getter({ target, prop, fullpath }) {
trace(`*PROXY* getter: ${prop}`);
},
before({ target, args, method, fullpath }) {
trace(`*PROXY* calling: ${fullpath}.${method}( ... [${args.length} args])`);
},
after({ target, args, method, fullpath, result }) {
trace(`*PROXY* called: ${fullpath}.${method}( ... [${args.length} args]) with result of: ` + result);
}
});
PROXY_NAMESPACE.someObject.hi('Bob');
/* =========== Outputs: ============
*PROXY* getter: someObject
*PROXY* calling: SOME_NAMESPACE.someObject.hi( ... [1 args])
Hello "Bob"!
*PROXY* called: SOME_NAMESPACE.someObject.hi( ... [1 args]) with result of: foobar
=================================== */
/**
@author Pierre Chamberlain
@description This is the 'proxyFieldsOf(...)' method, used for inspecting when an object's fields
are being accessed (getter) and when functions are being invoked (before & after its execution).
It can also recursively inspect all of the target's child-objects (and their grand-children, etc.)
FIXED: Getters were only called once (the first time), should be every time they're accessed now.
*/
const SELF = module.exports = {
proxyFieldsOf(target, options) {
const proxyCache = {};
if (typeof options === 'function') options = { after: options };
return new Proxy(proxyCache, {
get(obj, prop) {
if (prop in proxyCache) {
var proxy = proxyCache[prop];
return proxy._isGetter ? proxy.callGetter() : proxy;
}
const valueOrFunc = target[prop];
const valueType = typeof valueOrFunc;
if (valueType !== 'function') {
const getterProxy = { _isGetter: true, callGetter: null };
//For deep objects (more elaborate APIs), it might make sense to recursively proxy it's child objects:
if (valueType === 'object' && options.recursive) {
options.fullpath = options.fullpath ? options.fullpath + '.' + prop : prop;
getterProxy.childProxy = SELF.proxyFieldsOf(valueOrFunc, options);
}
getterProxy.callGetter = function () {
options.getter && options.getter({ target, prop, fullpath: options.fullpath, result: valueOrFunc });
return getterProxy.childProxy != null ? getterProxy.childProxy : valueOrFunc;
};
proxyCache[prop] = getterProxy;
return getterProxy.callGetter();
}
const methodProxy = function (...args) {
const argsNew = { target, args, method: prop, fullpath: options.fullpath, result: null };
options.before && options.before(argsNew);
argsNew.result = valueOrFunc.apply(target, args);
options.after && options.after(argsNew);
return argsNew.result;
}
proxyCache[prop] = methodProxy;
return methodProxy;
}
});
},
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment