1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
|
odoo.define("web.patchMixin", function () {
"use strict";
/**
* This module defines and exports the 'patchMixin' function. This function
* returns a 'monkey-patchable' version of the ES6 Class given in arguments.
*
* const patchMixin = require('web.patchMixin');
* class MyClass {
* print() {
* console.log('MyClass');
* }
* }
* const MyPatchedClass = patchMixin(MyClass);
*
*
* A patchable class has a 'patch' function, allowing to define a patch:
*
* MyPatchedClass.patch("module_name.key", T =>
* class extends T {
* print() {
* console.log('MyPatchedClass');
* super.print();
* }
* }
* );
*
* const myPatchedClass = new MyPatchedClass();
* myPatchedClass.print(); // displays "MyPatchedClass" and "MyClass"
*
*
* The 'unpatch' function can be used to remove a patch, given its key:
*
* MyPatchedClass.unpatch("module_name.key");
*/
function patchMixin(OriginalClass) {
let unpatchList = [];
class PatchableClass extends OriginalClass {}
PatchableClass.patch = function (name, patch) {
if (unpatchList.find(x => x.name === name)) {
throw new Error(`Class ${OriginalClass.name} already has a patch ${name}`);
}
if (!Object.prototype.hasOwnProperty.call(this, 'patch')) {
throw new Error(`Class ${this.name} is not patchable`);
}
const SubClass = patch(Object.getPrototypeOf(this));
unpatchList.push({
name: name,
elem: this,
prototype: this.prototype,
origProto: Object.getPrototypeOf(this),
origPrototype: Object.getPrototypeOf(this.prototype),
patch: patch,
});
Object.setPrototypeOf(this, SubClass);
Object.setPrototypeOf(this.prototype, SubClass.prototype);
};
PatchableClass.unpatch = function (name) {
if (!unpatchList.find(x => x.name === name)) {
throw new Error(`Class ${OriginalClass.name} does not have any patch ${name}`);
}
const toUnpatch = unpatchList.reverse();
unpatchList = [];
for (let unpatch of toUnpatch) {
Object.setPrototypeOf(unpatch.elem, unpatch.origProto);
Object.setPrototypeOf(unpatch.prototype, unpatch.origPrototype);
}
for (let u of toUnpatch.reverse()) {
if (u.name !== name) {
PatchableClass.patch(u.name, u.patch);
}
}
};
return PatchableClass;
}
return patchMixin;
});
|