Comparing version 0.1.9 to 0.2.0
@@ -32,3 +32,6 @@ // Copyright 2012 The Obvious Corporation. | ||
/** the name "length" */ | ||
var LENGTH = "length"; | ||
/* | ||
@@ -55,5 +58,30 @@ * Helper functions | ||
*/ | ||
function iterate(obj, visitor) { | ||
function iterate(obj, visitor, includeHidden) { | ||
var visitorStack = []; | ||
visitValue(obj); | ||
/** | ||
* Push a new visitor onto the stack. This is used by innerVisitor | ||
* call-throughs. | ||
*/ | ||
function pushVisitor(v) { | ||
visitorStack.push(visitor); | ||
if (v) { | ||
visitor = v; | ||
} | ||
} | ||
/** | ||
* Pop a visitor from the stack. This is used by innerVisitor | ||
* call-throughs. | ||
*/ | ||
function popVisitor(v) { | ||
visitor = visitorStack.pop(); | ||
} | ||
/** | ||
* Top-level visit. This dispatches based on JavaScript typeof | ||
* to a more specialized visitor. | ||
*/ | ||
function visitValue(x) { | ||
@@ -80,4 +108,6 @@ switch (typeof x) { | ||
function innerVisitor() { | ||
function innerVisitor(v) { | ||
pushVisitor(v); | ||
visitObjectBindings(x); | ||
popVisitor(); | ||
} | ||
@@ -99,4 +129,7 @@ } | ||
function innerVisitor() { | ||
function innerVisitor(v) { | ||
var proto = Object.getPrototypeOf(x); | ||
pushVisitor(v); | ||
if (proto === objectPrototype) { | ||
@@ -107,2 +140,4 @@ visitObjectBindings(x); | ||
} | ||
popVisitor(); | ||
} | ||
@@ -118,4 +153,6 @@ } | ||
function innerVisitor() { | ||
function innerVisitor(v) { | ||
pushVisitor(v); | ||
visitObjectBindings(x, undefined, true); | ||
popVisitor(); | ||
} | ||
@@ -137,4 +174,4 @@ } | ||
var keys = Object.keys(x); | ||
var length = keys.length; | ||
var keys = | ||
includeHidden ? Object.getOwnPropertyNames(x) : Object.keys(x); | ||
var at = 0; | ||
@@ -155,9 +192,26 @@ | ||
} | ||
// Slice off all the array index keys, so they don't get | ||
// reiterated in the general binding phase below. (Too bad | ||
// there's no way to avoid getting these in the keys array | ||
// in the first place.) | ||
keys = keys.slice(at); | ||
// Also prevent "length" from showing up in the next phase, | ||
// as it was implied here (and also any client that cares | ||
// can take the array length explicitly). | ||
for (var i = 0; i < keys.length; i++) { | ||
if (keys[i] === LENGTH) { | ||
keys.splice(i, 1); | ||
break; | ||
} | ||
} | ||
} | ||
// Sort the (remaining) keys for consistency. | ||
keys = keys.slice(at).sort(); | ||
length = keys.length; | ||
// Sort the (remaining) keys, for consistency. JavaScript | ||
// makes no guarantee about key order, but we do want to make | ||
// a guarantee. | ||
keys = keys.sort(); | ||
for (at = 0; at < length; at++) { | ||
for (at = 0; at < keys.length; at++) { | ||
var one = keys[at]; | ||
@@ -179,10 +233,14 @@ var descriptor = Object.getOwnPropertyDescriptor(x, one); | ||
function innerVisitor() { | ||
function innerVisitor(v) { | ||
pushVisitor(v); | ||
visitValue(innerValue); | ||
popVisitor(); | ||
} | ||
function dynamicInnerVisitor() { | ||
function dynamicInnerVisitor(v) { | ||
var details = innerValue; | ||
var name = details.name; | ||
pushVisitor(v); | ||
innerValue = details.get; | ||
@@ -197,2 +255,4 @@ if (innerValue) { | ||
} | ||
popVisitor(); | ||
} | ||
@@ -199,0 +259,0 @@ } |
{ | ||
"name": "bidar", | ||
"version": "0.1.9", | ||
"version": "0.2.0", | ||
"keywords": | ||
@@ -5,0 +5,0 @@ ["object", "serialization", "data", "graph"], |
@@ -200,3 +200,3 @@ Bidar: Binary Data Representation. | ||
### iterate(root, visitor) | ||
### iterate(root, visitor, [includeHidden]) | ||
@@ -207,10 +207,18 @@ Starting at the given root object, iterate through all references | ||
Some of the visit methods define an `innerVisitor` parameter. This | ||
is a function to call (with no arguments) in order to continue the | ||
iteration into the element indicated by the original call. For | ||
example, when `visitObject(x, innerVisitor)` is called, the | ||
`innerVisitor` it is passed will iterate over the prototype and | ||
name bindings of `x`. If `innerVisitor` is not called, then `x`'s | ||
contents will not be iterated over. | ||
Some of the visit methods define an `innerVisitor` parameter. This is | ||
a function to call in order to continue the iteration into the element | ||
indicated by the original call. For example, when `visitObject(x, | ||
innerVisitor)` is called, the `innerVisitor` it is passed will iterate | ||
over the prototype and name bindings of `x`. If `innerVisitor` is not | ||
called, then `x`'s contents will not be iterated over. `innerVisitor` | ||
may either be called with no arguments, in which case further | ||
callbacks get made to the original `visitor` passed in to `iterate()`, | ||
or with a single argument to use as a replacement visitor. | ||
The `includeHidden` argument, if specified, is taken to be a boolean | ||
and indicates whether (`true`) or not (`false`) to include hidden | ||
(non-enumerable) object properties in the iteration. It defaults to | ||
`false`. Note that, even when `true`, the `length` property of | ||
arrays will never get included. (It's particularly special.) | ||
Here is a rundown of the various called methods: | ||
@@ -217,0 +225,0 @@ |
@@ -125,9 +125,9 @@ // Copyright 2012 The Obvious Corporation. | ||
*/ | ||
function checkLog(value, expected) { | ||
function checkLog(value, expected, doHidden) { | ||
var iter = createVisitLogger(); | ||
bidar.iterate(value, iter); | ||
bidar.iterate(value, iter, doHidden); | ||
var log = iter.getLog(); | ||
if (log !== expected) { | ||
console.log("actual:\n" + log + "\nexpected:\n" + expected); | ||
console.log("actual:\n" + log + "\n\nexpected:\n" + expected); | ||
assert.equal(log, expected); | ||
@@ -137,3 +137,11 @@ } | ||
/** | ||
* Try iterating the given value, including hidden properties, | ||
* expecting it to log the given result. | ||
*/ | ||
function checkLogHidden(value, expected) { | ||
checkLog(value, expected, true); | ||
} | ||
/* | ||
@@ -286,3 +294,113 @@ * Tests | ||
function testReplacementInnerVisitor() { | ||
var mainVisitor = { | ||
visitArray: function (arr, innerVisitor) { | ||
record += "arr."; | ||
innerVisitor(subVisitor); | ||
}, | ||
visitFunction: function (func, innerVisitor) { | ||
record += "func."; | ||
innerVisitor(subVisitor); | ||
}, | ||
visitObject: function (obj, innerVisitor) { | ||
record += "obj."; | ||
innerVisitor(subVisitor); | ||
} | ||
}; | ||
var subVisitor = { | ||
visitArrayElement: function () { | ||
record += "elem."; | ||
}, | ||
visitObjectBinding: function () { | ||
record += "bind."; | ||
} | ||
} | ||
var target; | ||
var record; | ||
target = { stuff: "bother" }; | ||
record = ""; | ||
bidar.iterate(target, mainVisitor); | ||
assert.equal(record, "obj.bind."); | ||
target = [ 1 ]; | ||
record = ""; | ||
bidar.iterate(target, mainVisitor); | ||
assert.equal(record, "arr.elem."); | ||
target = function () { }; | ||
target.blort = "spaz"; | ||
record = ""; | ||
bidar.iterate(target, mainVisitor); | ||
assert.equal(record, "func.bind."); | ||
// New sub-visitor to test the object binding call-throughs. | ||
subVisitor = { | ||
visitObjectBinding: function (obj, name, props, innerVisitor) { | ||
record += "bind."; | ||
innerVisitor(subsubVisitor); | ||
} | ||
}; | ||
var subsubVisitor = { | ||
visitObjectGetter: function (obj, name, getter, innerVisitor) { | ||
record += "get."; | ||
innerVisitor(mainVisitor); | ||
}, | ||
visitObjectSetter: function (obj, name, setter, innerVisitor) { | ||
record += "set."; | ||
innerVisitor(mainVisitor); | ||
}, | ||
visitNumber: function () { | ||
record += "num."; | ||
} | ||
} | ||
target = { | ||
alpha: 65, | ||
get beta() { return 66; }, | ||
set beta(n) { }, | ||
}; | ||
record = ""; | ||
bidar.iterate(target, mainVisitor); | ||
assert.equal(record, "obj.bind.num.bind.get.func.set.func."); | ||
} | ||
function testHiddenProperties() { | ||
var obj = { | ||
a: 10 | ||
}; | ||
Object.defineProperty(obj, "b", { value: 20 }); | ||
checkLog(obj, | ||
"visitObject()\n" + | ||
" visitObjectBinding(a)\n" + | ||
" visitNumber(10)"); | ||
checkLogHidden(obj, | ||
"visitObject()\n" + | ||
" visitObjectBinding(a)\n" + | ||
" visitNumber(10)\n" + | ||
" visitObjectBinding(b)\n" + | ||
" visitNumber(20)"); | ||
var arr = [ 10 ]; | ||
Object.defineProperty(arr, "zorch", { value: 20 }); | ||
checkLog(arr, | ||
"visitArray()\n" + | ||
" visitArrayElement(0)\n" + | ||
" visitNumber(10)"); | ||
checkLogHidden(arr, | ||
"visitArray()\n" + | ||
" visitArrayElement(0)\n" + | ||
" visitNumber(10)\n" + | ||
" visitObjectBinding(zorch)\n" + | ||
" visitNumber(20)"); | ||
} | ||
/* | ||
@@ -301,2 +419,4 @@ * Exported bindings | ||
testDeepLayers(); | ||
testReplacementInnerVisitor(); | ||
testHiddenProperties(); | ||
} | ||
@@ -303,0 +423,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
87322
2076
353