javascriptnode.jsbddvows

I'm trying to wrap my head around variable scoping in Node.js and Vows


As an exercise in TDD in Node.js, I'm trying to implement a very simple "database" that stores things as flat files. Here's the very beginning of the DB module code:

var fs = require( 'fs' );

exports.create = function( path, readycallback ) {
    new FlatFileDB( path, readycallback );
};

function FlatFileDB( path, readycallback ) {
    this.path = path;
    readycallback( null, this );
}

FlatFileDB.prototype.getPath = function() {
    return this.path;
};

The DB creation is asynchronous, and in my test case, I have a check to see if calling the create() function actually results in two distinct objects:

var vows = require('vows'),
    assert = require('assert'),
    fs = require( 'fs' ),
    flatfileDB = require( '../lib/flatfileDB' );

var twoDBs = {};

vows.describe( 'flatfileDB' ).addBatch( {
    'creating the database': {
        topic: flatfileDB,
        'calling create': {
            topic: function( flatfileDB ) {
                flatfileDB.create( './createTest', this.callback );
            },
            'results in an object with the path passed in': function( err, db ) {
                assert.equal( db.getPath(), './createTest' );
            }
        },
        'calling create more than once': {
            topic: function( flatfileDB ) {
                flatfileDB.create( './db1', function( err, newdb ) {
                    twoDBs.db1 = newdb;
                    flatfileDB.create( './db2', function( err, newdb ) {
                        twoDBs.db2 = newdb;
                        this.callback( null, twoDBs );
                    } );
                });
            },
            'results in two objects with different paths.': function( err, dbs ) {
                console.log( 'twoDBs.  db1: ' + twoDBs.db1 + ', db2: ' + twoDBs.db2 );
                console.log( 'dbs: ' + JSON.stringify( dbs ) );
                assert.notEqual( twoDBs.db1.getPath(), twoDBs.db2.getPath() );
            }
        }
    },
}).run();

The output of those two console.log lines surprises me, though:

twoDBs.  db1: [object Object], db2: [object Object]
dbs: {}

since I'm passing twoDBs to the test callback, I had expected dbs and twoDBs to be the some object, but they don't appear to be. Can anyone help me out with what's going on here?


Solution

  • dbs scope is inside of that callback. You wont be able to access because invoke of this.callback() isn't scoped in that callback. You need to you pass it along with this.callback(null, dbs, twoDBs) or you create a var in topic and assign dbs to it in the callback. This isn't so much an issue with Vows; It has much more to do with JavaScript's lexical scoping and callbacks.

    What I do in these cases is pass along needed vars by add parameters to this.callback().

    Edit: What's above isn't right, but the fix came out in the comments below:

    If you put var that = this; in the topic and change it to that.callback( null, twoDBs); what happens?