add patch/unpatch utility methods

This commit is contained in:
Géry Debongnie
2019-03-26 14:30:27 +01:00
parent 766d2f2301
commit 2758269139
2 changed files with 107 additions and 1 deletions
+51
View File
@@ -125,3 +125,54 @@ export function shallowEqual(objA, objB) {
}
return true;
}
export function patch(C: any, patchName: string, patch: any) {
const proto = C.prototype;
if (!proto.__patches) {
proto.__patches = {
origMethods: {},
patches: {},
current: []
};
}
if (proto.__patches.patches[patchName]) {
throw new Error(`Patch [${patchName}] already exists`);
}
proto.__patches.patches[patchName] = patch;
applyPatch(proto, patch);
proto.__patches.current.push(patchName);
function applyPatch(proto, patch) {
Object.keys(patch).forEach(function(methodName) {
const method = patch[methodName];
if (typeof method === "function") {
const original = proto[methodName];
if (!(methodName in proto.__patches.origMethods)) {
proto.__patches.origMethods[methodName] = original;
}
proto[methodName] = function(...args) {
this._super = original;
return method.call(this, ...args);
};
}
});
}
}
export function unpatch(C: any, patchName: string) {
const proto = C.prototype;
const patchInfo = proto.__patches;
delete proto.__patches;
// reset to original
for (let k in patchInfo.origMethods) {
proto[k] = patchInfo.origMethods[k];
}
// apply other patches
for (let name of patchInfo.current) {
if (name !== patchName) {
patch(C, name, patchInfo.patches[name]);
}
}
}
+56 -1
View File
@@ -5,7 +5,9 @@ import {
memoize,
debounce,
findInTree,
shallowEqual
shallowEqual,
patch,
unpatch
} from "../src/utils";
describe("escape", () => {
@@ -103,3 +105,56 @@ describe("shallowEqual", () => {
expect(shallowEqual({ a: 1 }, ["a"])).toBe(false);
});
});
describe("patch/unpatch", () => {
test("can monkey patch a class", () => {
class Test {
n = 1;
doSomething(): string {
return "hey";
}
}
patch(Test, "some_custo", {
doSomething(): string {
this.n = this.n + 1;
return this._super();
}
});
const t = new Test();
expect(t.n).toBe(1);
expect(t.doSomething()).toBe("hey");
expect(t.n).toBe(2);
});
test("cannot patch a class twice with same patch name", () => {
class Test {}
patch(Test, "some_custo", {});
expect(() => {
patch(Test, "some_custo", {});
}).toThrow();
});
test("can unpatch a class", () => {
class Test {
doSomething(): number {
return 1;
}
}
patch(Test, "some_custo", {
doSomething(): number {
return this._super() + 2;
}
});
const t = new Test();
expect(t.doSomething()).toBe(3);
unpatch(Test, "some_custo");
expect(t.doSomething()).toBe(1);
});
});