From 46095b7a967e75ee20a87cac018c540ace1f8447 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Sat, 5 Oct 2019 09:00:23 +0200 Subject: [PATCH] [ADD] playground: add monkey patching example closes #312 --- tools/playground/samples.js | 134 ++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) diff --git a/tools/playground/samples.js b/tools/playground/samples.js index cb041cdb..7a01e3d2 100644 --- a/tools/playground/samples.js +++ b/tools/playground/samples.js @@ -1625,6 +1625,135 @@ const WMS_CSS = `body { font-size: 20px; }`; +const MONKEYPATCH = ` +// In this example, we show a possible way components can be monkey patched. +// This involves the functions patch/unpatch, which modifies the prototype of a +// class (not only an owl component). Sadly, the patch code cannot make use of +// the syntactic sugar that JS engines provides to classes, so it needs to use +// the "this._super()" syntax instead of the standard "super.someMethod()". + +//-------------------------------------------------------------------------- +// Patching Code +//-------------------------------------------------------------------------- +const patchMap = new WeakMap(); + +function patch(C, patchName, patch) { + let metadata = patchMap.get(C.prototype); + if (!metadata) { + metadata = { + origMethods: {}, + patches: {}, + current: [] + }; + patchMap.set(C.prototype, metadata); + } + const proto = C.prototype; + if (metadata.patches[patchName]) { + throw new Error(\`Patch [\${patchName}] already exists\`); + } + metadata.patches[patchName] = patch; + applyPatch(proto, patch); + 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; + } + proto[methodName] = function(...args) { + this._super = original; + return method.call(this, ...args); + }; + } + }); + } +} + +// we define here an unpatch function. This is mostly useful if we want to +// remove a patch. For example, for testing purposes +function unpatch(C, patchName) { + const proto = C.prototype; + let metadata = patchMap.get(proto); + if (!metdata) { + return; + } + patchMap.delete(proto); + + // reset to original + for (let k in metadata.origMethods) { + proto[k] = metadata.origMethods[k]; + } + + // apply other patches + for (let name of metadata.current) { + if (name !== patchName) { + patch(C, name, metadata.patches[name]); + } + } +} + + +//-------------------------------------------------------------------------- +// Components +//-------------------------------------------------------------------------- +const { Component, useState } = owl; + +class Counter extends Component { + state = useState({ value: 0 }); + + increment() { + this.state.value++; + } +} + +// Main root component +class App extends Component { + static components = { Counter }; +} + +//-------------------------------------------------------------------------- +// Patching code +//-------------------------------------------------------------------------- + +// In an Odoo application, this code would be located in another odoo module, +// to customize some behaviour in an existing class/component +patch(Counter, "double_value", { + increment() { + this._super(); + this.state.value = 2 * this.state.value; + } +}); + +//-------------------------------------------------------------------------- +// Application setup +//-------------------------------------------------------------------------- +const qweb = new owl.QWeb(TEMPLATES); +const app = new App({ qweb }); +app.mount(document.body); +`; + +const MONKEYPATCH_XML = ` + + + +
+ +
+
+`; + +const MONKEYPATCH_CSS = ` +button { + font-size: 20px; + width: 100px; + height: 50px; +}`; + export const SAMPLES = [ { description: "Components", @@ -1684,5 +1813,10 @@ export const SAMPLES = [ code: ASYNC_COMPONENTS, xml: ASYNC_COMPONENTS_XML, css: ASYNC_COMPONENTS_CSS + }, { + description: "Monkey patching components", + code: MONKEYPATCH, + xml: MONKEYPATCH_XML, + css: MONKEYPATCH_CSS } ];