Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 24bcf58

Browse files
authored
Merge pull request #330 from hoijnet/issues/261-support-for-unique-vars
feat(woql): Add vars_unique() for generating unique variable names fi...
2 parents fd3d3ee + 61fed00 commit 24bcf58

File tree

3 files changed

+154
-3
lines changed

3 files changed

+154
-3
lines changed

‎lib/query/woqlDoc.js‎

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ function convert(obj) {
4040
'@type': 'Value',
4141
variable: obj.name,
4242
};
43+
// eslint-disable-next-line no-use-before-define
44+
} if (obj instanceof VarUnique) {
45+
return {
46+
'@type': 'Value',
47+
variable: obj.name,
48+
};
4349
} if (typeof (obj) === 'object' && !Array.isArray(obj)) {
4450
const pairs = [];
4551
// eslint-disable-next-line no-restricted-syntax
@@ -80,6 +86,30 @@ function Var(name) {
8086
};
8187
}
8288

89+
/**
90+
* Global counter for generating unique variable names
91+
*/
92+
let uniqueVarCounter = 0;
93+
94+
/**
95+
* Creates a unique variable by appending an incrementing counter to the name.
96+
* This ensures variables are unique across all instantiations, even with the same input name.
97+
* @param {string} name - Base name for the variable
98+
* @returns {VarUnique} - A unique variable object
99+
*/
100+
function VarUnique(name) {
101+
uniqueVarCounter += 1;
102+
this.name = `${name}_${uniqueVarCounter}`;
103+
this.baseName = name;
104+
this.counter = uniqueVarCounter;
105+
this.json = function () {
106+
return {
107+
'@type': 'Value',
108+
variable: this.name,
109+
};
110+
};
111+
}
112+
83113
/**
84114
* @param {object} name
85115
* @returns {object}
@@ -105,4 +135,6 @@ function Vars(...args) {
105135
return varObj;
106136
}
107137

108-
module.exports = { Vars, Var, Doc };
138+
module.exports = {
139+
Vars, Var, VarUnique, Doc,
140+
};

‎lib/woql.js‎

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
// I HAVE TO REVIEW THE Inheritance and the prototype chain
55
const WOQLQuery = require('./query/woqlBuilder');
66
const WOQLLibrary = require('./query/woqlLibrary');
7-
const { Vars, Var, Doc } = require('./query/woqlDoc');
7+
const {
8+
Vars, Var, VarUnique, Doc,
9+
} = require('./query/woqlDoc');
810
// eslint-disable-next-line no-unused-vars
911
const typedef = require('./typedef');
1012
// eslint-disable-next-line no-unused-vars
@@ -1352,6 +1354,33 @@ WOQL.vars = function (...varNames) {
13521354
return varNames.map((item) => new Var(item));
13531355
};
13541356

1357+
/**
1358+
* Generates unique javascript variables for use as WOQL variables within a query.
1359+
* Each call to vars_unique() creates variables with unique names by appending an
1360+
* incrementing counter, ensuring that variables are fully unique across all scopes.
1361+
* This is particularly useful with select() to firewall local variables from other
1362+
* scopes, even when requesting the same variable name multiple times.
1363+
*
1364+
* @param {...string} varNames - Base names for the variables
1365+
* @returns {array<VarUnique>} an array of unique javascript variables which can be
1366+
* dereferenced using the array destructuring operation
1367+
* @example
1368+
* const [a, b, c] = WOQL.vars_unique("a", "b", "c")
1369+
* // Creates variables like "a_1", "b_2", "c_3"
1370+
* const [a2, b2] = WOQL.vars_unique("a", "b")
1371+
* // Creates variables like "a_4", "b_5" - guaranteed unique even with same input names
1372+
*
1373+
* @example
1374+
* // Using with select() to create isolated scopes
1375+
* const [localVar] = WOQL.vars_unique("x")
1376+
* WOQL.select(localVar, WOQL.triple(localVar, "rdf:type", "Person"))
1377+
* // localVar is "x_1" and won't conflict with any other "x" variables
1378+
*/
1379+
1380+
WOQL.vars_unique = function (...varNames) {
1381+
return varNames.map((item) => new VarUnique(item));
1382+
};
1383+
13551384
/**
13561385
* Produces an encoded form of a document that can be used by a WOQL operation
13571386
* such as `WOQL.insert_document`.

‎test/woql.spec.js‎

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const { expect } = require('chai');
22

33
const WOQL = require('../lib/woql');
4-
const { Var, Vars } = require('../lib/query/woqlDoc');
4+
const { Var, VarUnique,Vars } = require('../lib/query/woqlDoc');
55

66
const idGenJson = require('./woqlJson/woqlIdgenJson');
77
const woqlStarJson = require('./woqlJson/woqlStarJson');
@@ -404,6 +404,96 @@ describe('woql queries', () => {
404404
expect(varsArr[0]).to.be.instanceof(Var);
405405
});
406406

407+
it('check the vars_unique method creates VarUnique instances', () => {
408+
const varsArr = WOQL.vars_unique('A', 'B', 'C');
409+
410+
expect(varsArr[0]).to.be.instanceof(VarUnique);
411+
expect(varsArr[1]).to.be.instanceof(VarUnique);
412+
expect(varsArr[2]).to.be.instanceof(VarUnique);
413+
});
414+
415+
it('check vars_unique creates unique variable names within a single call', () => {
416+
const [a, b, c] = WOQL.vars_unique('A', 'B', 'C');
417+
418+
// Each variable should have a unique name
419+
expect(a.name).to.not.equal(b.name);
420+
expect(b.name).to.not.equal(c.name);
421+
expect(a.name).to.not.equal(c.name);
422+
423+
// Each should contain the base name
424+
expect(a.name).to.include('A');
425+
expect(b.name).to.include('B');
426+
expect(c.name).to.include('C');
427+
});
428+
429+
it('check vars_unique creates unique variable names across multiple calls', () => {
430+
const [a1] = WOQL.vars_unique('X');
431+
const [a2] = WOQL.vars_unique('X');
432+
const [a3] = WOQL.vars_unique('X');
433+
434+
// Variables with the same base name should still be unique
435+
expect(a1.name).to.not.equal(a2.name);
436+
expect(a2.name).to.not.equal(a3.name);
437+
expect(a1.name).to.not.equal(a3.name);
438+
439+
// All should contain the base name 'X'
440+
expect(a1.name).to.include('X');
441+
expect(a2.name).to.include('X');
442+
expect(a3.name).to.include('X');
443+
});
444+
445+
it('check vars_unique appends incrementing counter', () => {
446+
// Get the current counter value by creating a variable
447+
const [v1] = WOQL.vars_unique('test');
448+
const counter1 = v1.counter;
449+
450+
// Next variable should have counter + 1
451+
const [v2] = WOQL.vars_unique('test');
452+
expect(v2.counter).to.equal(counter1 + 1);
453+
454+
// And so on
455+
const [v3] = WOQL.vars_unique('test');
456+
expect(v3.counter).to.equal(counter1 + 2);
457+
});
458+
459+
it('check vars_unique generates correct JSON with unique variable names', () => {
460+
const [a, b] = WOQL.vars_unique('myvar', 'myvar');
461+
462+
const jsonA = a.json();
463+
const jsonB = b.json();
464+
465+
expect(jsonA).to.have.property('@type', 'Value');
466+
expect(jsonA).to.have.property('variable');
467+
expect(jsonB).to.have.property('@type', 'Value');
468+
expect(jsonB).to.have.property('variable');
469+
470+
// Variable names in JSON should be different even with same base name
471+
expect(jsonA.variable).to.not.equal(jsonB.variable);
472+
expect(jsonA.variable).to.include('myvar');
473+
expect(jsonB.variable).to.include('myvar');
474+
});
475+
476+
it('check vars still works exactly as before (no changes)', () => {
477+
const [x, y, z] = WOQL.vars('X', 'Y', 'Z');
478+
479+
// Should create Var instances, not VarUnique
480+
expect(x).to.be.instanceof(Var);
481+
expect(x).to.not.be.instanceof(VarUnique);
482+
483+
// Names should be exactly as provided
484+
expect(x.name).to.equal('X');
485+
expect(y.name).to.equal('Y');
486+
expect(z.name).to.equal('Z');
487+
488+
// Should not have counter property
489+
expect(x).to.not.have.property('counter');
490+
491+
// Multiple calls with same names should produce identical variable names
492+
const [x2] = WOQL.vars('X');
493+
expect(x2.name).to.equal('X');
494+
expect(x2.name).to.equal(x.name);
495+
});
496+
407497
it('check type_of(Var,Var)', () => {
408498
const TypeOf = WOQL.type_of('v:X', 'v:Y').json()
409499
expect(TypeOf).to.deep.eql({

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /