Wednesday, September 22, 2010

Javascript Separation of Concerns with Functions

‹prev | My Chain | next›

Up tonight, a bit of code clean-up. The main game.js file in my (fab) game has grown to 350+ lines of code as it now maintains a CouchDB, establishes a faye server (and some server side listeners), and responds to various resources necessary to drive the game. It has been difficult working with the code, so I start moving some of it to the lib directory.

I can write my dirt-simple Logger in lib/logger.js as:
var puts = require( "sys" ).puts;

Logger = {
level: 1,

debug: function(msg) { if (this.level<1) puts("[DEBUG] " + msg); },
info: function(msg) { if (this.level<2) puts("[INFO] " + msg); },
warn: function(msg) { if (this.level<3) puts("[WARN] " + msg); },
errror: function(msg) { if (this.level<4) puts("[ERROR] " + msg); }
};

module.exports = Logger;
The module.exports statement at the end is a commonjs mechanism for describing what will be imported into the main game.js code when it requires the module:
var Logger = require("./lib/logger");

Logger.info("Starting up...");
Easy enough.

I factor out the player store code as well:
var couchdb = require('node-couchdb/lib/couchdb'),
client = couchdb.createClient(5984, 'localhost'),
db = client.db('my-fab-game');

// Player local store
var players = (require("./lib/players")).init(db);
The code is starting to look better and the extraction process is going smoothly until I hit the
  // attach the extension ensuring player messages come from the same
// client that originally added player to the room
faye_server.addExtension(require("./lib/faye_server_auth"));
The problem with this faye extension is that it accesses the player store, which is now isolated in its own module.

Blech. It was almost nicer when everything was just in one big namespace and everything had access to everything. But, deep down, I know that will lead to poor separation of concerns. So I stop my pining for the bad old days and seek a mechanism to allow the message authorization to lookup players.

I could require the players module inside the message authorization module. I opt against that because the player code does much more (maybe too much more) than just lookup players in CouchDB—it also establishes faye subscriptions to update the data store in response to various published messages.

Instead, I pass a function (this is javascript, after all) into the message authorization init() method that can be used to look up players:
  // attach the extension ensuring player messages come from the same
// client that originally added player to the room
var auth = require("./lib/faye_server_auth").init(function () {
players.get.apply(players, arguments);
});

faye_server.addExtension(auth);
I have to be careful to return the object itself in init() because I need that object when calling faye_server.addExtension():
  init: function(player_get) {
this.player_get = player_get;
return this;
}
I can then use the player_get callback inside the message authorization module thusly:
    this.player_get(message.data.id, function(player) {
// Do stuff with the player
});
With that, I have my game.js source down to 180 lines—100 of which is HTML (fab) apps. I am not completely convinced that passing a function in by init() is inherently better than requiring the data store in the message authorization module. Still, if the message authorization module had direct access to the player store, the main code body would still have to pass in the database name. Unsure, I will leave it as-is, but make a note to reconsider in the future.

That is a nice stopping point for the night. Up tomorrow, I think that I am ready to merge back into master and return to some raphaël.js fun.


Day #234

No comments:

Post a Comment