I ended up yesterday's session with a test that looks like:
'send 1000+ bytes to get Chrome\'s attention': function(chunks) {That works just fine, but I am not fond of accumulators and index tracking variables.
var byte_count = 0;
for (var i=0; i < chunks.length; i++) {
var chunk = chunks[i];
if (chunk && chunk.body && typeof(chunk.body) == "string") {
byte_count = byte_count + chunk.body.length;
}
}
assert.isTrue(byte_count > 1000);
},
To get cool, ruby-like iterators I install underscore.js. Happily, I can install via npm:
cstrom@whitefall:~/repos/my_fab_game$ npm install underscoreThe current version of underscore.js is 1.04, but I will not quibble given the convenience. Since I can install it via
The "ini" module will be removed in future versions of Node, please extract it into your own code.
npm configfile /home/cstrom/.npmrc
npm sudo false
npm cli [ 'install', 'underscore' ]
npm install pkg underscore
npm fetch data underscore
npm GET underscore
npm install pkg underscore
npm install pkg underscore@1.0.3
...
npm activate underscore 1.0.3
npm readJson /home/cstrom/.node_libraries/.npm/underscore/active/package/package.json
npm readJson /home/cstrom/.node_libraries/.npm/underscore/1.0.3/package/package.json
npm build Success: underscore-1.0.3
npm ok
npm
, I ought to be able to require it the usual way:var _ = require('underscore');Then, I can convert from accumulators/trackers to:
'send 1000+ bytes to get Chrome\'s attention': function(chunks) {I wrap the chunks of HTTP responses inside an underscore object, then filter out only those chunks with a body (non-headers), map each to the size of the response, then reduce each to a simple sum. Easy-peasey except... it does not work:
var byte_count = _(chunks).
filter(function(chunk) {
return chunk && (typeof(chunk.body) == "string");
}).
map(function(chunk) {
return chunk.body.length;
}).
reduce(function(memo, length) {
return memo + length;
});
assert.isTrue(byte_count > 1000);
},
cstrom@whitefall:~/repos/my_fab_game$ vows --specAh, looking through the underscore code a bit, it looks as though it takes care of exporting the underscore to the global namespace itself, so I change my require to simply read:
♢ init_comet
with a player
✓ sets a session cookie
✓ sends the opening HTML doc
✗ send 1000+ bytes to get Chrome's attention
TypeError: object is not a function
at Object.CALL_NON_FUNCTION (native)
at Object.<anonymous> (/home/cstrom/repos/my_fab_game/test/init_comet.js:47:26)
at runTest (/home/cstrom/.node_libraries/.npm/vows/0.4.5/package/lib/vows.js:99:26)
at EventEmitter.<anonymous> (/home/cstrom/.node_libraries/.npm/vows/0.4.5/package/lib/vows.js:72:9)
at EventEmitter.emit (events:42:20)
at /home/cstrom/.node_libraries/.npm/vows/0.4.5/package/lib/vows/context.js:24:44
at EventEmitter._tickCallback (node.js:48:25)
at node.js:204:9
require('underscore');And then my tests pass again:
cstrom@whitefall:~/repos/my_fab_game$ vows --specAs I was reading through the underscore.js source code, I could not help noticing many comments along the lines of:
♢ init_comet
with a player
✓ sets a session cookie
✓ sends the opening HTML doc
✓ send 1000+ bytes to get Chrome's attention
✓ sends the player
without a player
✓ terminates the downstream connection
with an invalid player object
✓ terminates the downstream connection
♢ player_from_querystring
with a query string
✓ is player
✓ has unique ID
✓ has X coordinate
✓ has Y coordinate
without explicit X-Y coordinates
✓ has X coordinate
✓ has Y coordinate
POSTing data
✓ is null response
✓ OK » 13 honored (0.122s)
// Delegates to JavaScript 1.6's native filter if available.In fact, each of the three methods that I used,
filter
, map
, and reduce
have comments like that. I am using v8, which means that I ought to have the latest and greatest ECMAscript standards, so why use underscore.js at all?'send 1000+ bytes to get Chrome\'s attention': function(chunks) {Sure enough, that works just fine. I would note that this solution is not any shorter than the accumulator / index variable version. It would also clean things up a bit if Javascript did not require the
var byte_count = chunks.
filter(function(chunk) {
return chunk && (typeof(chunk.body) == "string");
}).
map(function(chunk) {
return chunk.body.length;
}).
reduce(function(memo, length) {
return memo + length;
});
assert.isTrue(byte_count > 1000);
},
function
or return
statements. Nevertheless, I am much more comfortable with this version. Each iterator is self-contained which prevents them from being at the mercy of out-of-scope variables. Such variables are not a problem now, but in the future, they suck for maintainability.Ah, well. Good to know that I can get it into node.js / fab.js / vows if I need it. Even better to know that I do not need to add an extra dependency just yet.
Day #158
Just to let you know: you were never using underscore's version of map or reduce.
ReplyDeleteUnderscore only returns a wrapped object if you call chain() first. Then you'd have to call value() at the end to get the original one back. Normally it would just return an array or value, which has no underscore methods.