From 86b76eb7003bad5a16505daacd2c6fd0c939f583 Mon Sep 17 00:00:00 2001 From: Robert Kawecki Date: Thu, 12 May 2016 16:32:58 +0200 Subject: [PATCH 1/3] Changed the CommonJS compat wrapper to use amdefine instead of a custom-made shim. --- js-array.js | 3 +-- package.json | 1 + parser.js | 2 +- query.js | 4 +--- util/contains.js | 2 +- util/each.js | 2 +- 6 files changed, 6 insertions(+), 8 deletions(-) diff --git a/js-array.js b/js-array.js index b4b5367..5088431 100644 --- a/js-array.js +++ b/js-array.js @@ -3,8 +3,7 @@ * require("./js-array").query("a=3", {}, [{a:1},{a:3}]) -> [{a:3}] * */ - -({define:typeof define!="undefined"?define:function(deps, factory){module.exports = factory(exports, require("./parser"), require("./query"), require("./util/each"), require("./util/contains"));}}). +if (typeof define !== 'function') { var define = require('amdefine')(module) } define(["exports", "./parser", "./query", "./util/each", "./util/contains"], function(exports, parser, QUERY, each, contains){ //({define:typeof define!="undefined"?define:function(deps, factory){module.exports = factory(exports, require("./parser"));}}). //define(["exports", "./parser"], function(exports, parser){ diff --git a/package.json b/package.json index 58ae9f5..4acb7e9 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "promised-io": "http://github.com/kriszyp/promised-io/zipball/v0.2.1" }, "dependencies": { + "amdefine": "^1.0.0", "promised-io": ">=0.3.0" }, "devDependencies": { diff --git a/parser.js b/parser.js index 4300842..31b0214 100644 --- a/parser.js +++ b/parser.js @@ -2,7 +2,7 @@ * This module provides RQL parsing. For example: * var parsed = require("./parser").parse("b=3&le(c,5)"); */ -({define:typeof define!="undefined"?define:function(deps, factory){module.exports = factory(exports, require("./util/contains"));}}). +if (typeof define !== 'function') { var define = require('amdefine')(module) } define(["exports", "./util/contains"], function(exports, contains){ var operatorMap = { diff --git a/query.js b/query.js index cea7c5a..24a6de9 100644 --- a/query.js +++ b/query.js @@ -9,9 +9,7 @@ * // for each object that matches the query * }); */ -//({define:typeof define!="undefined"?define:function(deps, factory){module.exports = factory(exports, require("./parser"), require("./js-array"));}}). -//define(["exports", "./parser", "./js-array"], function(exports, parser, jsarray){ -({define:typeof define!="undefined"?define:function(deps, factory){module.exports = factory(exports, require("./parser"), require("./util/each"));}}). +if (typeof define !== 'function') { var define = require('amdefine')(module) } define(["exports", "./parser", "./util/each"], function(exports, parser, each){ var parseQuery = parser.parseQuery; diff --git a/util/contains.js b/util/contains.js index f707893..7ddb582 100644 --- a/util/contains.js +++ b/util/contains.js @@ -1,4 +1,4 @@ -({define:typeof define!=='undefined'?define:function(deps, factory){module.exports = factory(exports);}}). +if (typeof define !== 'function') { var define = require('amdefine')(module) } define([], function(){ return contains; diff --git a/util/each.js b/util/each.js index c1a1a75..a279d29 100644 --- a/util/each.js +++ b/util/each.js @@ -1,4 +1,4 @@ -({define:typeof define!=='undefined'?define:function(deps, factory){module.exports = factory(exports);}}). +if (typeof define !== 'function') { var define = require('amdefine')(module) } define([], function(){ return each; From 7db4e49a251f34bd7aa49fe8eb86d6029ca0df64 Mon Sep 17 00:00:00 2001 From: Robert Kawecki Date: Wed, 24 Aug 2016 15:44:25 +0200 Subject: [PATCH 2/3] Add a try/catch in query.encodeValue This change enables converting values containing special characters, such as the percent sign (%), to strings, by always encoding values for which the check if encoding is actually necessary has failed. For values containing URI escape characters, the check throws an error (because they are not decodable), but we still need to encode them. Fixes persvr/rql#75 --- query.js | 46 +++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/query.js b/query.js index 24a6de9..24718de 100644 --- a/query.js +++ b/query.js @@ -79,28 +79,32 @@ function encodeString(s) { exports.encodeValue = function(val) { var encoded; if (val === null) val = 'null'; - if (val !== parser.converters["default"]('' + ( - val.toISOString && val.toISOString() || val.toString() - ))) { - var type = typeof val; - if(val instanceof RegExp){ - // TODO: control whether to we want simpler glob() style - val = val.toString(); - var i = val.lastIndexOf('/'); - type = val.substring(i).indexOf('i') >= 0 ? "re" : "RE"; - val = encodeString(val.substring(1, i)); - encoded = true; - } - if(type === "object"){ - type = "epoch"; - val = val.getTime(); - encoded = true; - } - if(type === "string") { - val = encodeString(val); + try { + if (val !== parser.converters["default"]('' + ( + val.toISOString && val.toISOString() || val.toString() + ))) { + var type = typeof val; + if(val instanceof RegExp){ + // TODO: control whether to we want simpler glob() style + val = val.toString(); + var i = val.lastIndexOf('/'); + type = val.substring(i).indexOf('i') >= 0 ? "re" : "RE"; + val = encodeString(val.substring(1, i)); encoded = true; - } - val = [type, val].join(":"); + } + if(type === "object"){ + type = "epoch"; + val = val.getTime(); + encoded = true; + } + if(type === "string") { + val = encodeString(val); + encoded = true; + } + val = [type, val].join(":"); + } + } catch (conversionError) { + // The conversion necessity check has failed and we have not encoded the value. The next conditional block will take care of it. } if (!encoded && typeof val === "string") val = encodeString(val); return val; From 7986c6d769dfeb19056224f0f087b85c668ba920 Mon Sep 17 00:00:00 2001 From: Robert Kawecki Date: Wed, 21 Dec 2016 11:40:01 +0100 Subject: [PATCH 3/3] Disable coercion of null values to string:null in Query#toString This also includes a refactoring of the query module's `encodeValue` function for clarity and easier modification later, if necessary. Comments included. For persvr/rql#76 --- query.js | 69 ++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 25 deletions(-) diff --git a/query.js b/query.js index 24718de..5ee71fe 100644 --- a/query.js +++ b/query.js @@ -76,37 +76,56 @@ function encodeString(s) { return s; } +/** + * Encode a value of a part's argument for use in an RQL string. + * @param {*} val - The value to encode. + * @returns {string} The encoded value, URI-safe. + */ exports.encodeValue = function(val) { - var encoded; - if (val === null) val = 'null'; + // Remember whether any encoding took place. The function force-encodes + // the value as a plain string at the end as a last resort. + var encoded = false; + + // Try encoding the value. If it throws, go to our failsafe. try { - if (val !== parser.converters["default"]('' + ( - val.toISOString && val.toISOString() || val.toString() - ))) { - var type = typeof val; - if(val instanceof RegExp){ - // TODO: control whether to we want simpler glob() style - val = val.toString(); - var i = val.lastIndexOf('/'); - type = val.substring(i).indexOf('i') >= 0 ? "re" : "RE"; - val = encodeString(val.substring(1, i)); - encoded = true; - } - if(type === "object"){ - type = "epoch"; - val = val.getTime(); - encoded = true; - } - if(type === "string") { - val = encodeString(val); - encoded = true; - } - val = [type, val].join(":"); + // Check whether casting the value to a string and parsing it would + // result in the input value (i.e. if the value can be left prefix-less). + var stringVal = '' + ((val && val.toISOString) ? val.toISOString() : val); + var parsedStringVal = parser.converters["default"](stringVal); + if (val === parsedStringVal) { + return encodeString(String(val)); + } + // Now, only non-trivial cases are left: + var type = typeof val; + if (val instanceof RegExp) { + // TODO: control whether to we want simpler glob() style + val = val.toString(); + var i = val.lastIndexOf('/'); + type = val.substring(i).indexOf('i') >= 0 ? "re" : "RE"; + val = encodeString(val.substring(1, i)); + encoded = true; } + if (type === "object") { + // Assume it's a date - it's the only other object type we support: + type = "epoch"; + val = val.getTime(); + encoded = true; + } + if (type === "string") { + // It's a string, but it doesn't parse to its own exact value. + // This happens when the input is a String('null') for example - + // the auto-converter parses null to type null, so we need a prefix. + val = encodeString(val); + encoded = true; + } + val = [type, val].join(":"); } catch (conversionError) { // The conversion necessity check has failed and we have not encoded the value. The next conditional block will take care of it. } - if (!encoded && typeof val === "string") val = encodeString(val); + if (!encoded && typeof val === "string") { + val = encodeString(val); + } + val = String(val); return val; };