Skip to content

Commit f4dc9de

Browse files
implemented chaining of computed results (#11)
* implemented chaining of computed results * fixed empty string as computed param
1 parent 89df199 commit f4dc9de

File tree

5 files changed

+103
-29
lines changed

5 files changed

+103
-29
lines changed

CHANGELOG.MD

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ All notable changes to the "@qavajs/memory" will be documented in this file.
44

55
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
66

7+
## [1.3.0]
8+
- :rocket: implemented chaining of computed results
9+
- :beetle: fixed empty string as computed param
10+
711
## [1.2.1]
812
- :beetle: fixed context for methods calling
913

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@qavajs/memory",
3-
"version": "1.2.1",
3+
"version": "1.3.0",
44
"description": "memory package for @qavajs framework",
55
"main": "index.js",
66
"scripts": {

src/Memory.js

Lines changed: 54 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
const KEY_REGEXP = /^\$(.+?)(\((.*)\))?$/;
2+
const PARAMS_REGEXP = /^(.+?)(\((.*)\))?$/;
23
const ESCAPED_REGEXP = /^\\\$/;
34
const PARSE_STRING_REGEXP = /({\$.+?})/g;
45
const QUOTES_REPLACE_REGEXP = /^['"]|['"]$/g;
5-
const STRING_TYPE_REGEXP = /^'.+'|".+"$/;
6+
const STRING_TYPE_REGEXP = /^'.*'|".*"$/;
67
const NUMBER_TYPE_REGEXP = /^\d+|\d+\.\d+$/;
78

89
class Memory {
@@ -37,29 +38,20 @@ class Memory {
3738
* @returns {any} - resolved value
3839
*/
3940
getKey(str) {
40-
const keyMatch = str.match(KEY_REGEXP);
41-
const key = keyMatch ? keyMatch[1] : null;
42-
if (key) {
43-
const { value, ctx } = this.getProperty(key);
44-
if (typeof value === 'function' && str.includes('(') && str.includes(')')) {
45-
const params = this.getComputedParams(str);
46-
return value.apply(ctx, params)
41+
const props = this.parseChain(str);
42+
props[0] = props[0].replace(/^\$/, '');
43+
const traverse = props.reduce((prev, prop) => {
44+
const ctx = prev.value;
45+
const key = prop.replace(/\((.*)\)/, '');
46+
let value = ctx[key];
47+
if (value === undefined) throw new Error(`${str}\n'${key}' is not defined`);
48+
if (typeof value === 'function' && prop.includes('(') && prop.includes(')')) {
49+
const params = this.getComputedParams(prop);
50+
value = value.apply(ctx, params)
4751
}
48-
return value
49-
}
50-
}
51-
52-
/**
53-
* Resolve object
54-
* @private
55-
* @param {string} key - key to resolve
56-
* @returns {any} - resolved value
57-
*/
58-
getProperty(key) {
59-
const props = key.replace(/]/g, '').split(/[[.]/).map(prop => prop.replace(QUOTES_REPLACE_REGEXP, ''));
60-
const obj = this[props.shift()];
61-
if (obj === undefined) throw new Error(`${key} is not found in memory`);
62-
return props.reduce((prev, prop) => ({value: prev.value[prop], ctx: prev.value}), {value: obj, ctx: null})
52+
return {value, ctx}
53+
}, {value: this, ctx: null});
54+
return traverse.value
6355
}
6456

6557
/**
@@ -80,7 +72,7 @@ class Memory {
8072
* @returns {Array<any>} - array of params
8173
*/
8274
getComputedParams(str) {
83-
const paramsString = str.match(KEY_REGEXP);
75+
const paramsString = str.match(PARAMS_REGEXP);
8476
if (!(paramsString && paramsString[3])) return []
8577
const params = [];
8678
let singleQuoteClosed = true;
@@ -113,6 +105,44 @@ class Memory {
113105
this[prop] = obj[prop];
114106
}
115107
}
108+
109+
parseChain(chain) {
110+
const tokens = [];
111+
let currentToken = '';
112+
let insideSquareBracket = false;
113+
let insideParenthesis = false;
114+
115+
for (let i = 0; i < chain.length; i++) {
116+
const char = chain.charAt(i);
117+
118+
if (char === '.' && !insideSquareBracket && !insideParenthesis) {
119+
tokens.push(currentToken);
120+
currentToken = '';
121+
} else if (char === '[') {
122+
insideSquareBracket = true;
123+
tokens.push(currentToken);
124+
currentToken = '';
125+
} else if (char === ']' && insideSquareBracket) {
126+
insideSquareBracket = false;
127+
tokens.push(currentToken);
128+
currentToken = '';
129+
} else {
130+
if (char === '(') {
131+
insideParenthesis = true;
132+
} else if (char === ')') {
133+
insideParenthesis = false
134+
}
135+
currentToken += char;
136+
}
137+
}
138+
139+
if (currentToken !== '') {
140+
tokens.push(currentToken);
141+
}
142+
143+
return tokens.filter(token => token).map(token => token.replace(QUOTES_REPLACE_REGEXP, ''));
144+
}
145+
116146
}
117147

118148
module.exports = new Memory();

tests/memory.spec.js

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ test('simple string returns as is', () => {
5252
});
5353

5454
test('throw error if key is not present in memory', () => {
55-
expect(() => memory.getValue('$val')).to.throw('val is not found in memory');
55+
expect(() => memory.getValue('$val')).to.throw('$val\n\'val\' is not defined');
5656
});
5757

5858
test('memory is singleton', () => {
@@ -81,7 +81,7 @@ test('query string with two existing variables', () => {
8181
test('throws for query string with one existing and one non-existent variables', () => {
8282
memory.ind1 = 3;
8383
const queryString = 'Component > #{$ind1} of Collection1 > #{$notExists} of Collection2'
84-
expect(() => memory.getValue(queryString)).to.throw('notExists is not found in memory');
84+
expect(() => memory.getValue(queryString)).to.throw('$notExists\n\'notExists\' is not defined');
8585
});
8686

8787
test('property from object in dot notation', () => {
@@ -202,3 +202,43 @@ test('save context for methods', () => {
202202
})
203203
expect(memory.getValue('$obj.getProp()')).to.equal(42);
204204
});
205+
206+
test('chaining method calls', () => {
207+
memory.setValue('obj', {
208+
method1() {
209+
return {
210+
method2() {
211+
return 'Im method'
212+
},
213+
prop: 42
214+
}
215+
}
216+
})
217+
expect(memory.getValue('$obj.method1().method2()')).to.equal('Im method');
218+
expect(memory.getValue('$obj.method1().prop')).to.equal(42);
219+
});
220+
221+
test('inner computed call', () => {
222+
memory.setValue('comp1', function (val) { return val });
223+
expect(memory.getValue('$comp1($comp1(42))')).to.equal(42);
224+
});
225+
226+
test('inner method call', () => {
227+
memory.setValue('obj1', {
228+
method1(val) {
229+
return val
230+
}
231+
})
232+
memory.setValue('obj2', {
233+
method1(val) {
234+
return val
235+
}
236+
})
237+
expect(memory.getValue('$obj1.method1($obj2.method1(42))')).to.equal(42);
238+
});
239+
240+
test('empty string', () => {
241+
memory.setValue('fn', function (val) { return val })
242+
expect(memory.getValue('$fn("")')).to.equal('');
243+
expect(memory.getValue("$fn('')")).to.equal('');
244+
});

0 commit comments

Comments
 (0)