Tonight, I hope to DRY up the move/bounce/chat handlers in the fab.js part of my (fab) game. The code in question:
// TODO: These broadcast resources are not DRYEach of these unary (non-middleware) fab apps updates the player status in the local store then broadcasts it to the other players in the room. The exact details of the status attribute being stored / broadcast varies from resource to resource, but the overall structure of the unary apps is nearly identical (as the initial comment notes).
( /move/ )
( function() {
var out = this;
return function listener( obj ) {
if ( !obj ) out();
else if ( obj.body ) {
broadcast(comet_walk_player(obj.body));
update_player_status(JSON.parse(""+obj.body));
}
return listener;
};
} )
( /chat/ )
( function() {
var out = this;
return function listener( obj ) {
if ( !obj ) out();
else if ( obj.body ) {
var msg = JSON.parse(obj.body.toString());
msg.body = msg.say.substr(0,100);
broadcast(comet_player_say(JSON.stringify(msg)));
}
return listener;
};
} )
( /bounce/ )
( function() {
var out = this;
return function listener( obj ) {
if ( !obj ) out();
else if ( obj.body ) {
try {
update_player_status(JSON.parse(""+obj.body));
broadcast(comet_bounce_player(obj.body));
} catch (e) {
puts("[bounce_handler] error type: " + e.type +
", message: " + e.message);
}
finally {
out();
}
}
return listener;
};
} )
The only difference in structure between the three is the
try-catch-finally
chain in the bounce
handler. I added that last night and would like to add it to the other two, minus the duplication, of course. First up, I factor the
try-catch-finally
version out into a library app. Until I can think of a better name, I call it unary_try
:var puts = require( "sys" ).puts;I ought to be able to reduce the
function unary_try(fn) {
var out = this;
return function listener( obj ) {
if ( !obj ) out();
else if ( obj.body ) {
try {
if ( obj.body ) {
fn.call(obj, obj);
}
} catch (e) {
puts("[unary_try] error type: " + e.type +
", message: " + e.message);
}
finally {
out();
}
}
return listener;
};
}
exports.app = unary_try;
bounce
handler to this:( /bounce/ )Sending that anonymous function to the
( unary_try( function () {
update_player_status(JSON.parse(""+this.body));
broadcast(comet_bounce_player(this.body));
} ) )
unary_try
app establishes a callback that is invoked when the downstream (i.e. web browser) POSTs data. Using the call
method and sending the POST contents as the first object binds the this
variable to the POST body inside the anonymous function. In this case, it is a cheap way to keep the anonymous functions as small as possible.When I run the game, however, it exits immediately. This is an indication that I have forgotten a function wrapper somewhere and, indeed, I have done so here. Specifically, the
unary_try
app is not a fab app—it takes the callback as its only argument, but it has to return a unary app to fit properly in the chain:function unary_try(fn) {Ah, much better, now the game starts up and collision / bounce messages are broadcast. Before applying this to the other two resources, I switch the
return function() {
var out = this;
return function listener( obj ) {
if ( !obj ) out();
else if ( obj.body ) {
try {
if ( obj.body ) {
fn.call(obj, obj);
}
} catch (e) {
puts("[unary_try] error type: " + e.type +
", message: " + e.message);
}
finally {
out();
}
}
return listener;
};
};
}
fn
context to the POSTs body rather than the entire request—it is the only thing that I care about in each of the scenarios, so I might as well be as specific as possible://...With that, I can DRY up my other two broadcast resource nicely:
try {
if ( obj && obj.body ) {
fn.call(obj.body, obj.body);
}
}
//...
( /move/ )Much better! I do not have any tests written against this code and I think that I might be able to simplify it a bit. I think I will pick up there tomorrow.
( unary_try( function () {
update_player_status(JSON.parse(""+this));
broadcast(comet_walk_player(this));
} ) )
( /chat/ )
( unary_try( function () {
var msg = JSON.parse(this.toString());
msg.body = msg.say.substr(0,100);
broadcast(comet_player_say(JSON.stringify(msg)));
} ) )
( /bounce/ )
( unary_try( function () {
update_player_status(JSON.parse(""+this));
broadcast(comet_bounce_player(this));
} ) )
Day #139
No comments:
Post a Comment