Handle partially parsed objects (w/ querystrings as keys) #27

Open
wants to merge 2 commits into
from
View
@@ -35,15 +35,7 @@ function parse(parts, parent, key, val) {
var part = parts.shift();
// end
if (!part) {
- if (Array.isArray(parent[key])) {
- parent[key].push(val);
- } else if ('object' == typeof parent[key]) {
- parent[key] = val;
- } else if ('undefined' == typeof parent[key]) {
- parent[key] = val;
- } else {
- parent[key] = [parent[key], val];
- }
+ set(parent, key, val);
// array
} else {
var obj = parent[key] = parent[key] || [];
@@ -95,8 +87,8 @@ function merge(parent, key, val){
* Parse the given obj.
*/
-function parseObject(obj){
- var ret = { base: {} };
+function parseObject(obj, base){
+ var ret = { base: base || {} };
Object.keys(obj).forEach(function(name){
merge(ret, name, obj[name]);
});
@@ -232,10 +224,13 @@ function stringifyObject(obj, prefix) {
function set(obj, key, val) {
var v = obj[key];
- if (undefined === v) {
+ if ('undefined' == typeof v) {
tj
tj Dec 18, 2011 Owner

why do we need this? where's v coming from that we need typeof

tj
tj Dec 18, 2011 Owner

oh it's right above haha, we dont need typeof then

kainosnoema
kainosnoema Dec 18, 2011

Just consistency really, and because undefined can be redefined in Javascript, meaning the safest way to check for undefined is with typeof. It's a habit of mine...

EDIT: I knew this, but just tried it... blows my mind:

> typeof undefined
'undefined'
> {}['foo'] === undefined
true
> undefined = 'foo';
'foo'
> typeof undefined
'string'
> {}['foo'] === undefined
false

EDIT 2: Apparently this behavior was eliminated in newer versions of V8? The above was in Node v0.4.8, v0.6.2 doesn't allow it... thank goodness.

tj
tj Dec 18, 2011 Owner

ah gotcha, personally I avoid defensive programming whenever possible to delegate the issue. plus there are a ton of retarded things you can do in js, people just shouldn't do them haha

obj[key] = val;
} else if (Array.isArray(v)) {
- v.push(val);
+ obj[key] = v.concat(val);
+ } else if (('object' == typeof v) && ('[object Object]' == toString.call(val))) {
+ // partially parsed object
+ parseObject(val, v);
} else {
obj[key] = [v, val];
}
View
@@ -139,6 +139,29 @@ module.exports = {
'test malformed uri': function(){
qs.parse('{%:%}').should.eql({ '{%:%}': '' });
qs.parse('foo=%:%}').should.eql({ 'foo': '%:%}' });
+ },
+
+ 'test partially parsed objects': function(){
+ qs.parse({ 'foo[0]': 'bar', 'foo[1]': 'baz' })
+ .should.eql({ foo: ['bar', 'baz'] });
+
+ qs.parse({ 'foo[items]': [], foo: { items: ['bar'] } })
+ .should.eql({ foo: { items: ['bar'] } })
+
+ qs.parse({ foo: { items: ['bar'] }, 'foo[items]': [] })
+ .should.eql({ foo: { items: ['bar'] } })
+
+ qs.parse({ 'foo[base64]': 'RAWR', 'foo[64base]': 'RAWR' })
+ .should.eql({ foo: { base64: 'RAWR', '64base': 'RAWR' } });
+
+ qs.parse({ 'user[name][first]': ['tj', 'TJ'] })
+ .should.eql({ user: { name: { first: ['tj', 'TJ'] } } });
+
+ qs.parse({ 'user[name]': { first: 'tj' }, 'user': { name: { last: 'holowaychuk' } } })
+ .should.eql({ user: { name: { first: 'tj', last: 'holowaychuk' } } });
+
+ qs.parse({ 'user': { name: { last: 'holowaychuk' } }, 'user[name]': { first: 'tj' } })
+ .should.eql({ user: { name: { last: 'holowaychuk', first: 'tj' } } });
}
// 'test complex': function(){