diff options
| author | Michael Mattiello (mcm) <mcm@odoo.com> | 2020-03-16 08:28:17 +0000 |
|---|---|---|
| committer | Michael Mattiello (mcm) <mcm@odoo.com> | 2020-04-16 07:19:25 +0000 |
| commit | dcf9a4c38da5d50a6e597d3d68c3d46fdef61df5 (patch) | |
| tree | d17339ec7d9a543deb5937052415fe768d1298d7 /addons/web/static/src | |
| parent | 5cc42022fc159e71ccd2dde122ddde80dd66e23e (diff) | |
[IMP] web: add es6 class patcher
This commit implements a new way to monkey-patch ES6
classes (for instance, Owl Components).
Usage
-----
odoo.module('module.BaseClass', function (require) {
const patchMixin = require('web.patchMixin');
// Define your class as usual
class BaseClass {
constructor() {
this.foo = 'foo';
}
bar() {
console.log(this.foo);
}
}
// but export it like this to have a "patchable" version
return patchMixin(BaseClass);
});
odoo.module('module.Patch', function (require) {
// imports the "patchable" version
const BaseClass = require('module.BaseClass');
BaseClass.patch('key', T =>
class extends T {
constructor() {
super();
this.foo = 'patched foo';
}
bar() {
super.bar();
console.log('patched bar()');
}
}
);
});
odoo.module('module.instance', function (require) {
// imports the "patchable" version
const BaseClass = require('module.BaseClass');
const obj = new BaseClass();
obj.bar();
// if module.patch is loaded before this module the patch
// will be apply and calling obj.bar() will log:
// patched foo
// patched bar()
// You can unpatch a patchable class
BaseClass.unpatch('key');
});
Diffstat (limited to 'addons/web/static/src')
| -rw-r--r-- | addons/web/static/src/js/core/patch_mixin.js | 80 | ||||
| -rw-r--r-- | addons/web/static/src/js/core/utils.js | 7 |
2 files changed, 86 insertions, 1 deletions
diff --git a/addons/web/static/src/js/core/patch_mixin.js b/addons/web/static/src/js/core/patch_mixin.js new file mode 100644 index 00000000000..99f06f56588 --- /dev/null +++ b/addons/web/static/src/js/core/patch_mixin.js @@ -0,0 +1,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; +}); diff --git a/addons/web/static/src/js/core/utils.js b/addons/web/static/src/js/core/utils.js index f1d4b00c6a9..e8924747616 100644 --- a/addons/web/static/src/js/core/utils.js +++ b/addons/web/static/src/js/core/utils.js @@ -527,7 +527,12 @@ var utils = { /** * Patch a class and return a function that remove the patch * when called. - * + * + * This function is the last resort solution for monkey-patching an + * ES6 Class, for people that do not control the code defining the Class + * to patch (e.g. partners), and when that Class isn't patchable already + * (i.e. when it doesn't have a 'patch' function, defined by the 'web.PatchMixin'). + * * @param {Class} C Class to patch * @param {string} patchName * @param {Object} patch |
