From cdc6d5e62250f6b2c8ebbda2a7e347fdbe14e2c6 Mon Sep 17 00:00:00 2001 From: Jelle De Loecker Date: Sat, 27 Apr 2024 20:38:39 +0200 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Use=20methods=20for=20gett?= =?UTF-8?q?ing=20(and=20proxy=20traps=20for=20setting)=20variables=20on=20?= =?UTF-8?q?the=20`Variables`=20class?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + benchmark/05-renderer.js | 22 +++++++++++++++++ lib/core/hawkejs.js | 19 +++++++++++--- lib/core/variables.js | 53 ++++++++++++++++++++++++++++++++++++---- test/00-init.js | 4 +-- 5 files changed, 89 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 774da171..7c823ded 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * Make sure variables are always cast to a `Variables` instance * Add `Variables#get(key)` & `Variables#setFromTemplate(key, value)` method * Don't let `Hawkejs.doNextSync(promise)` swallow errors +* Use methods for getting (and proxy traps for setting) variables on the `Variables` class ## 2.3.19 (2024-04-13) diff --git a/benchmark/05-renderer.js b/benchmark/05-renderer.js index a229263b..00740cba 100644 --- a/benchmark/05-renderer.js +++ b/benchmark/05-renderer.js @@ -42,6 +42,9 @@ const createRawVariables = () => { return {...RAW_VARIABLES}; }; +const PREPARED_VARIABLES = RENDERER.prepareVariables(createRawVariables()); +const LAYERED_VARIABLES = PREPARED_VARIABLES.overlay({e_object: {a: 10}}); + //RENDERER.renderHTML(VARIABLE_TEST_TEMPLATE, createRawVariables()).done((err, result) => console.log(err, result)) //console.log(); @@ -74,3 +77,22 @@ suite('Renderer', function() { }); }); +suite('Variables', () => { + + bench('get(key)', () => { + let nr = PREPARED_VARIABLES.get('a_number'); + nr += PREPARED_VARIABLES.get('e_object').a; + + if (nr !== 15) { + throw new Error('Invalid result: ' + nr); + } + + let overlayed_nr = LAYERED_VARIABLES.get('a_number'); + overlayed_nr += LAYERED_VARIABLES.get('e_object').a; + + if (overlayed_nr !== 24) { + throw new Error('Invalid result: ' + overlayed_nr); + } + }); + +}); diff --git a/lib/core/hawkejs.js b/lib/core/hawkejs.js index afc553e5..781d6f14 100644 --- a/lib/core/hawkejs.js +++ b/lib/core/hawkejs.js @@ -932,7 +932,7 @@ Main.setMethod(function parseTemplateSyntax(source, name, wrap_ejs) { * * @author Jelle De Loecker * @since 1.0.0 - * @version 2.3.18 + * @version 2.4.0 * * @param {string} code * @param {Hawkejs.Scopes} scopes @@ -1018,8 +1018,21 @@ Main.setMethod(function rewriteVariableReferences(code, scopes, level) { if (Hawkejs.hasGlobal(token.value)) { token.value = '(Hawkejs.getGlobal(' + JSON.stringify(token.value) + '))'; } else { - // All the rest are variables that should be changed - token.value = 'vars.' + token.value; + + let is_assignment = false, + name = next?.name; + + if (name) { + if (name == 'increment' || name == 'decrement' || name.indexOf('assign') === 0) { + is_assignment = true; + } + } + + if (is_assignment) { + token.value = 'vars.getProxy().' + token.value; + } else { + token.value = 'vars.get(' + JSON.stringify(token.value) + ')'; + } } } } diff --git a/lib/core/variables.js b/lib/core/variables.js index 90e414d9..d53952fd 100644 --- a/lib/core/variables.js +++ b/lib/core/variables.js @@ -1,5 +1,28 @@ const PARENT = Symbol('parent'), - RENDERER = Symbol('renderer'); + RENDERER = Symbol('renderer'), + TRAPLESS = Symbol('trapless'), + PROXY = Symbol('proxy'); + +/** + * This way we can skip transforming ejs `my_value = 1` + * into complicated calls. + * + * @author Jelle De Loecker + * @since 2.4.0 + * @version 2.4.0 + */ +const TRAPS = { + set(target, name, value) { + + if (typeof name == 'symbol') { + target[name] = value; + } else { + target.setFromTemplate(name, value); + } + + return true; + } +}; /** * Simple class to use for storing variables @@ -13,6 +36,7 @@ const Variables = Fn.inherits('Hawkejs.Base', function Variables(renderer, varia this[renderer.clone_symbol] = null; this[PARENT] = null; this[RENDERER] = renderer; + this[TRAPLESS] = this; if (variables != null) { Object.assign(this, variables); @@ -60,6 +84,24 @@ Variables.setProperty(function length() { return Object.keys(this).length; }); +/** + * Get the proxy of this instance. + * This allows us to set variables without having to use the `set` method. + * + * @author Jelle De Loecker + * @since 2.4.0 + * @version 2.4.0 + */ +Variables.setMethod(function getProxy() { + + if (this[PROXY] == null) { + this[PROXY] = new Proxy(this, TRAPS); + this[PROXY][TRAPLESS] = this; + } + + return this[PROXY]; +}); + /** * Get a specific variable * @@ -137,11 +179,12 @@ Variables.setMethod(function getExistingCloneIfValid(clone_name) { * * @author Jelle De Loecker * @since 2.0.0 - * @version 2.0.0 + * @version 2.4.0 */ Variables.setMethod(function createShim() { - var result = Object.create(this); + let result = Object.create(this[TRAPLESS]); + result[PROXY] = null; return result; }); @@ -226,7 +269,7 @@ Variables.setMethod(function toJSON() { * * @author Jelle De Loecker * @since 2.0.0 - * @version 2.0.0 + * @version 2.4.0 */ Variables.setMethod(function overlay(new_variables) { @@ -242,7 +285,7 @@ Variables.setMethod(function overlay(new_variables) { // This will set this instance as the new parent of the variables instance // (This can overwrite the parent in the cloned instance) - result[PARENT] = this; + result[PARENT] = this[TRAPLESS]; return result; }); \ No newline at end of file diff --git a/test/00-init.js b/test/00-init.js index 96c35809..8c108ffa 100644 --- a/test/00-init.js +++ b/test/00-init.js @@ -116,7 +116,7 @@ describe('Hawkejs', function() { fnc = hawkejs.compile('test this js: <% i = 10;\nprint(i);%>'); body = String(fnc); - if (body.indexOf('print(vars.i)') == -1) { + if (body.indexOf('print(vars.get("i"))') == -1) { throw new Error('The inline JS code was not added to the compiled function'); } @@ -209,7 +209,7 @@ print(zever); assertContains(code, 'let zever = 1; __render.print(zever);'); assertContains(code, '{__render.print(zever);}'); - assertContains(code, '__render.print(vars.zever)'); + assertContains(code, '__render.print(vars.get("zever"))'); hawkejs.try_template_expressions = true; hawkejs.skip_set_err = false;