Wednesday, July 14, 2010

Spike a New (Fab) App

‹prev | My Chain | next›

Before moving on with my (fab) game, I have a quick look at my fab.js apps dealing with the player store. Hoepfully, I might be able to clean them up or, better yet, get some test coverage for them.

Much of the player store code is wrapped in my if_body (fab) app or in the comet_view resource which establishes the comet session and adds players to the game:
  ( /chat/ )
( if_body(
function () {
var msg = JSON.parse(this.toString());
msg.body = msg.say.substr(0,100);
broadcast(comet_player_say(JSON.stringify(msg)));
} ) )

( /bounce/ )
( if_body(
function () {
update_player_status(JSON.parse(""+this));
broadcast(comet_bounce_player(this));
} ) )

( /^\/comet_view/ )
( broadcast_new )
( store_player )
( init_comet )
( player_from_querystring )
The if_body (fab) app fires the supplied function if the downstream (i.e. browser) supplied a body, which it would do by POSTing data. What I notice while looking through the store_player and init_comet apps is that I do a lot of if-response-body-then-do:
function init_comet (app) {
return function () {
var out = this;

return app.call( function listener(obj) {
if (obj && obj.body && obj.body.uniq_id) {
do_comet.call(out, obj);
}
else if (obj && obj.status) {
out(obj)();
}

return listener;
});
};
}
It could be especially helpful to factor that out of the player store code. Doing so would allow me to separate the player store entirely from the (fab) apps:
function store_player (app) {
return function () {
var out = this;

return app.call( function listener(obj) {
if (obj && obj.body && obj.body.id) {
var new_id = obj.body.id;
if (!players[new_id]) {
puts("[store_player] adding: " + new_id);
// player store code here
}
else if (players[new_id].uniq_id == obj.body.uniq_id) {
puts("[store_player] refreshing session: " + new_id);
// player store code here
}
else {
out();
}

}
out(obj);

return listener;
});
};
}
I do not think that I need a ternary (fab) app. What I need is a (fab) app that takes a callback to fire if there is a downstream response. Fab.js already includes fab.tap that does something similar, except that it does not include the downstream response when invoking the callback. The fab.tap app takes the callback as an argument, then returns a binary app:
exports.app = function( fn ) {
return function( app ) {
return function() {
fn();
return app.call( this );
}
}
}
To get the upstream response, it is not sufficient to app.call() the upstream app with the downstream listener (always set to this by fab.js). Instead, I have to app.call with a listener for the upstream response:
function if_response(fn) {
return function(app) {
return function() {
var out = this;

return app.call(function listener(obj) {
if (obj && obj.body) {
fn.call(out, obj.body);
}
out(obj);

return listener;
}
);
};
};
}
With that, I can refactor the store_player function as a side-effect only function. This allows me to rewrite the comet call stack as:
  ( /^\/comet_view/ )
( broadcast_new )
( if_response (store_player) )

( init_comet )
( player_from_querystring )
I am not sure how I feel about that syntax, so I will stop there for today. If I decide that I like it after sleeping on it, I will delete this exploratory code and BDD the real thing. Otherwise I will move onto to other matters.


Day #164

No comments:

Post a Comment