diff options
| author | Joseph Caburnay <jcb@odoo.com> | 2020-03-06 08:11:36 +0000 |
|---|---|---|
| committer | Joseph Caburnay <jcb@odoo.com> | 2020-03-06 09:04:20 +0000 |
| commit | d6b854306cae245343a7acb9edc4178d245a49fe (patch) | |
| tree | c339d3778c291cea01339f36d9db1c1ad7ed4a43 /addons | |
| parent | ed1a39c61826d740e2f34f2d2d9072603021503b (diff) | |
[IMP] web: allow patching getters
Previous implementation of patch (web.utils) only allows patching
methods. This commit allows patching getters as well.
closes odoo/odoo#47047
Signed-off-by: Géry Debongnie (ged) <ged@openerp.com>
Diffstat (limited to 'addons')
| -rw-r--r-- | addons/web/static/src/js/core/utils.js | 52 | ||||
| -rw-r--r-- | addons/web/static/tests/core/util_tests.js | 80 |
2 files changed, 124 insertions, 8 deletions
diff --git a/addons/web/static/src/js/core/utils.js b/addons/web/static/src/js/core/utils.js index 243f3f51cd0..9bc1a287512 100644 --- a/addons/web/static/src/js/core/utils.js +++ b/addons/web/static/src/js/core/utils.js @@ -520,6 +520,10 @@ var utils = { * Patch a class and return a function that remove the patch * when called. * + * In the patch object, super methods and getters are accessible thru: + * - this._super() - for method + * - this._super - for getter + * * @param {Class} C Class to patch * @param {string} patchName * @param {Object} patch @@ -531,7 +535,8 @@ var utils = { metadata = { origMethods: {}, patches: {}, - current: [] + current: [], + originalDescriptors: {}, }; patchMap.set(C.prototype, metadata); } @@ -544,20 +549,41 @@ var utils = { metadata.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 metadata.origMethods)) { - metadata.origMethods[methodName] = original; + Object.keys(patch).forEach(function(property) { + const protoDescriptor = Object.getOwnPropertyDescriptor(proto, property); + const patchDescriptor = Object.getOwnPropertyDescriptor(patch, property); + if (!protoDescriptor && patchDescriptor) { + if (!(property in metadata.originalDescriptors)) { + metadata.originalDescriptors[property] = undefined; + } + Object.defineProperty(proto, property, patchDescriptor); + return; + } + if (protoDescriptor.value) { + const method = patch[property]; + if (typeof method !== 'function') return; + const original = proto[property]; + if (!(property in metadata.origMethods)) { + metadata.origMethods[property] = original; } - proto[methodName] = function (...args) { + proto[property] = function(...args) { const previousSuper = this._super; this._super = original; const res = method.call(this, ...args); this._super = previousSuper; return res; }; + } else if (protoDescriptor.get) { + if (!(property in metadata.originalDescriptors)) { + metadata.originalDescriptors[property] = protoDescriptor; + } + const patchDescriptor = Object.getOwnPropertyDescriptor(patch, property); + Object.defineProperty(proto, property, { + get() { + this._super = protoDescriptor.get.apply(this); + return patchDescriptor.get.apply(this); + }, + }); } }); } @@ -735,6 +761,16 @@ var utils = { proto[k] = metadata.origMethods[k]; } + // reset the descriptors + for (let k in metadata.originalDescriptors) { + const descriptor = metadata.originalDescriptors[k]; + if (descriptor === undefined) { + delete proto[k]; + } else { + Object.defineProperty(proto, k, descriptor); + } + } + // apply other patches for (let name of metadata.current) { if (name !== patchName) { diff --git a/addons/web/static/tests/core/util_tests.js b/addons/web/static/tests/core/util_tests.js index 91a9f6b271b..f33a76d87c1 100644 --- a/addons/web/static/tests/core/util_tests.js +++ b/addons/web/static/tests/core/util_tests.js @@ -101,6 +101,86 @@ QUnit.module('core', {}, function () { assert.strictEqual(human_number(-1.012e+43, 2, 2), '-1.01e+43'); }); + QUnit.test('patch a class with getter', function (assert) { + assert.expect(13); + + const { patch } = utils; + + class TestClass { + get val() { + return 'aaa'; + } + } + + const testInstance = new TestClass(); + assert.strictEqual(testInstance.val, 'aaa'); + + // Extend `val` + + const removePatch1 = patch(TestClass, 'patch1', { + get val() { + return this._super + 'bbb'; + }, + }); + assert.strictEqual(testInstance.val, 'aaabbb'); + + const removePatch2 = patch(TestClass, 'patch2', { + get val() { + return this._super + 'ccc'; + }, + }); + assert.strictEqual(testInstance.val, 'aaabbbccc'); + + removePatch1(); + assert.strictEqual(testInstance.val, 'aaaccc'); + + removePatch2(); + assert.strictEqual(testInstance.val, 'aaa'); + + // Add getters + + const removeMorePatch1 = patch(TestClass, 'more-patch-1', { + get realVal() { + return `${this.wrapper.start}${this.val}${this.wrapper.end}` + }, + get wrapper() { + return { + start: '(', + end: ')', + } + } + }) + assert.strictEqual(testInstance.realVal, '(aaa)'); + + const removeMorePatch2 = patch(TestClass, 'more-patch-2', { + get wrapper() { + return { + start: `[${this._super.start}`, + end: `${this._super.end}]`, + } + } + }) + assert.strictEqual(testInstance.realVal, '[(aaa)]'); + + const removeMorePatch3 = patch(TestClass, 'more-patch-3', { + get realVal() { + return `${this._super} - augmented`; + } + }) + assert.strictEqual(testInstance.realVal, '[(aaa)] - augmented'); + + removeMorePatch2(); + assert.strictEqual(testInstance.realVal, '(aaa) - augmented'); + + removeMorePatch3(); + assert.strictEqual(testInstance.realVal, '(aaa)'); + + removeMorePatch1(); + assert.strictEqual(testInstance.realVal, undefined); + assert.strictEqual(testInstance.wrapper, undefined); + assert.strictEqual(testInstance.val, 'aaa'); + }); + }); }); |
