summaryrefslogtreecommitdiff
path: root/addons/web/static/src
diff options
context:
space:
mode:
authorMichael Mattiello (mcm) <mcm@odoo.com>2020-03-16 08:28:17 +0000
committerMichael Mattiello (mcm) <mcm@odoo.com>2020-04-16 07:19:25 +0000
commitdcf9a4c38da5d50a6e597d3d68c3d46fdef61df5 (patch)
treed17339ec7d9a543deb5937052415fe768d1298d7 /addons/web/static/src
parent5cc42022fc159e71ccd2dde122ddde80dd66e23e (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.js80
-rw-r--r--addons/web/static/src/js/core/utils.js7
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