Over the past few days, I have worked through the library of fab.js apps that support my (fab) game. I have gotten testing coverage in place with vows.js and cleaned up some of my code while I was at it (always good sign for a testing framework).
But I have made code changes. Ideally everything is still working, but well... it is not. When I first enter the room everything stops. Specifically, the comet session drops immediately.
Sure enough, when I check the
comet_view
resource via curl
, it drops right back to the command line prompt rather than blocking for more output:cstrom@whitefall:~/repos/my_fab_game$ curl http://localhost:4011/comet_view?player=foo\&x=250\&y=350In fab.js, HTTP connections are terminated by calling a downstream listener with no arguments. So I work through my
<html><body>
// comet initialization
<script type="text/javascript">window.parent.player_list.new_player({"id":"foo","x":250,"y":350,"uniq_id":"ef41a5af47ae84ba00fe36e602fbef85"})</script>
<script type="text/javascript">window.parent.player_list.new_player({"id":"foo","x":250,"y":350,"uniq_id":"ef41a5af47ae84ba00fe36e602fbef85"})</script>
cstrom@whitefall:~/repos/my_fab_game$
comet_view
stack looking for things like out()
:( /^\/comet_view/ )Both
( broadcast_new )
( store_player )
( init_comet )
( player_from_querystring )
store_player
and init_comet
have downstream calls with no arguments. To figure out which one is the cause, I add some print STDERR calls to each:function init_comet (app) {Indeed, that does get called:
return function () {
var out = this;
return app.call( function listener(obj) {
if (obj && obj.body && obj.body.uniq_id) {
// take the upstream body response and send
// it back down downstream
}
else {
puts("[init_comet] terminating");
out();
}
return listener;
});
};
}
cstrom@whitefall:~/repos/my_fab_game$ ./game.jsDang. How is that possible? Well, it must be coming from downstream because it is in the listener that is sent to the upstream app.
[store_player] adding: foo
[add_player] broadcasting about: foo
...
[init_comet] terminating
There is only one app that is upstream of
init_comet
, player_from_querystring
:function player_from_querystring() {Ah, there is an
var out = this;
return function(head) {
if (head && head.url && head.url.search) {
var uniq_id;
// calculate the uniq_id
var search = head.url.search.substring(1);
var q = require('querystring').parse(search);
var app = out({ body: {id: q.player, x: q.x || 0, y: q.y || 0, uniq_id: uniq_id} });
if ( app ) app();
}
else {
out();
}
};
}
out()
call in there. But, print STDERR proves that it is not being called, nor should it—that code is only reached when query strings are not supplied. There is a query string:cstrom@whitefall:~/repos/my_fab_game$ curl http://localhost:4011/comet_view?player=foo\&x=250\&y=350So where is the trouble coming from?
Aw nuts! There is more than one
out()
call in there! The other is disguised as app()
:function player_from_querystring() {Dang! That is another
var out = this;
return function(head) {
if (head && head.url && head.url.search) {
var uniq_id;
// calculate the uniq_id
var search = head.url.search.substring(1);
var q = require('querystring').parse(search);
var app = out({ body: {id: q.player, x: q.x || 0, y: q.y || 0, uniq_id: uniq_id} });
if ( app ) app();
}
else {
out();
}
};
}
out()
call. In the init_comet
middleware / binary app, the last thing that the listener sent to player_from_querystring
does is return itself:function init_comet (app) {That listener then gets assigned to the
return function () {
var out = this;
return app.call( function listener(obj) {
// Lots of cool comet code
return listener;
});
};
}
app
local variable, which is then invoked with no arguments, terminating the HTTP connection.There is nothing wrong with any of that code. Both the middleware app and the upstream app behave in a very typical fab.js way. In fact, I only added that
app()
call because it is a typical fab.js thing to do. But I clearly did not add that to make a test pass. My only defense is that I did that before I had any comfort level with vows.js or any Javascript testing. Aw well, lesson learned. Hopefully. And a lesson learned is a good place to stop for the night.
Day #161
All I can say is :D
ReplyDeleteHehe. I couldn't help but think of you when I tracked that one down.
ReplyDeleteDon't look at Firetower then... no tests at all, purely exploratory coding :-P
ReplyDelete