Wednesday, June 9, 2010

String Literals in Fab.js (for the sake of JSON.parse)

‹prev | My Chain | next›

Up tonight, a bit of hacking on fab.js proper. Last night I encountered this stack trace:
cstrom@whitefall:~/repos/my_fab_game$ ./game.js
/home/cstrom/repos/my_fab_game/game.js:38
say: obj.body.say.substr(0,100)
^
TypeError: Cannot call method 'substr' of undefined
at listener (/home/cstrom/repos/my_fab_game/game.js:38:35)
at listener (/home/cstrom/.node_libraries/fab/apps/fab.map.js:11:19)
at IncomingMessage.<anonymous> (/home/cstrom/.node_libraries/fab/apps/fab.nodejs.js:29:36)
at IncomingMessage.emit (events:25:26)
at HTTPParser.onBody (http:98:23)
at Stream.ondata (http:745:22)
at IOWatcher.callback (net:373:31)
at node.js:204:9
I solved the problem by reworking my (fab) app, but I am thinking that maybe I need to resolve this in fab.js itself. After a bit of legwork, I decide the best point of attack in that stack is fab.nodejs.js:
     if ( inbound ) {
request
.addListener( "end", inbound )
.addListener( "data", function( body ) {
if ( inbound ) inbound = inbound({ body: body });
})
}
Inside fab.nodejs.js, I add some debugging code to see what the body is:
//...
if ( inbound ) {
request
.addListener( "end", inbound )
.addListener( "data", function( body ) {
puts("[fab.node.js] data body isa " + typeof(body));
puts("[fab.node.js] data body toString(): " + body.toString());
for (var prop in body) {
puts("[fab.node.js] " + prop + ": " + body[prop]);
}

if ( inbound ) inbound = inbound({ body: body.toString ? body.toString() : body });
})
}
//...
After sending some dummy chat data to my (fab) game, I find this debug output:
cstrom@whitefall:~/repos/my_fab_game$ ./game.js
[fab.node.js] data body isa object
[fab.node.js] data body toString(): {"id":"foo", "say":"hi"}
[fab.node.js] 0: 123
[fab.node.js] 1: 34
[fab.node.js] 2: 105
[fab.node.js] 3: 100
...
As expected (after last night's investigation), the body is not a string literal, but rather is a String object. The later does not work well with things like JSON.parse, so I need to come up with a way of ensuring that body a string literal.

I do investigate up the stack a bit, but am frustrated to find that it should already be a string literal. From http.js in node.js:
      parser.incoming.emit('data', b.slice(start, start+len));
The slice method ought to produce a string literal:
cstrom@whitefall:~/repos/my_fab_game$ rlwrap node-repl
Welcome to the Node.js REPL.
Enter ECMAScript at the prompt.
Tip 1: Use 'rlwrap node-repl' for a better interface
Tip 2: Type Control-D to exit.
Type '.help' for options.
node> str = new String("foo")
{ '0': 'f', '1': 'o', '2': 'o' }
node> typeof str
'object'
node> str.slice(1,4)
'oo'
node> typeof str.slice(1,4)
'string'
But in practice, sadly, that is not what is emitted by the onBody callback. Ah, well. I can resolve the issue similarly to last night: by calling toString() on the body in fab.nodejs.js:
    if ( inbound ) {
request
.addListener( "end", inbound )
.addListener( "data", function( body ) {
if ( inbound ) inbound = inbound({ body: body.toString ? body.toString() : body });
})
}
Now I can be sure that any data POSTed to any of my (fab) apps will be string literals and thus JSON.parse friendly. That's a good stopping point for tonight.

Day #129

No comments:

Post a Comment