Skip to content

Commit

Permalink
initial version for review
Browse files Browse the repository at this point in the history
  • Loading branch information
goulash1971 committed Jun 7, 2011
1 parent ec4a54f commit 064e699
Show file tree
Hide file tree
Showing 11 changed files with 405 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
@@ -0,0 +1,4 @@
.DS_Store
material/
node_modules/
npm-debug.log
3 changes: 3 additions & 0 deletions .gitmodules
@@ -0,0 +1,3 @@
[submodule "support/expresso"]
path = support/expresso
url = https://github.com/visionmedia/expresso.git
3 changes: 3 additions & 0 deletions .npmignore
@@ -0,0 +1,3 @@
.git*
material/
npm-debug.log
11 changes: 11 additions & 0 deletions Makefile
@@ -0,0 +1,11 @@
EXPRESSO = support/expresso/bin/expresso -I lib

TESTS = tests/*.test.js

test:
@$(EXPRESSO) $(TESTS) $(TEST_FLAGS)

test-cov:
@$(MAKE) TEST_FLAGS=--cov test

.PHONY: test test-cov
10 changes: 10 additions & 0 deletions index.js
@@ -0,0 +1,10 @@
/*
* Module file for Mongoose-DBRef
*
* Copyright 2011, Stuart Hudson <goulash1971@yahoo.com>
* Released under the terms of the MIT License.
*
* Version 0.0.1
*/

module.exports = require("./lib");
74 changes: 74 additions & 0 deletions lib/index.js
@@ -0,0 +1,74 @@
/**
* lib/index.js - module loader
*
* Copyright 2011, Stuart Hudson <goulash1971@yahoo.com>
* Released under the terms of the MIT License.
*
* Version 0.0.1
*/


/**
* Expose the utilities that are available from this module (these
* are also accessed from the loadTypes function.
*/
exports.utils = utils = require("./utils");


/**
* Loads either the named types or all available types in the
* {@code lib/types} source directory
*
* @param {Mongoose} the active Mongoose instance for installation
* @param {Array} (optional) type filenames
*/
exports.loadTypes = loadTypes = function (mongoose) {
var types = Array.prototype.slice.call(arguments, 1);
if (types.length) {
types.forEach(function (type) {
require("./types/" + type).loadType(mongoose);
});
} else {
var files = require("fs").readdirSync(__dirname + "/types");
files.forEach(function(filename) {
var base = filename.slice(0, filename.length-3);
require("./types/" + base).loadType(mongoose);
});
}
};


/**
* Installs either the named plugins or all available plugins in the
* {@code lib/plugins} source directory
*
* @param {Mongoose} the active Mongoose instance for installation
* @param {Array} (optional) plugin filenames
*/
exports.installPlugins = installPlugins = function (mongoose) {
var plugins = Array.prototype.slice.call(arguments, 1);
if (plugins.length) {
types.forEach(function (plugin) {
require("./plugins/" + plugin).install(mongoose);
});
} else {
var files = require("fs").readdirSync(__dirname + "/plugins");
files.forEach(function(filename) {
var base = filename.slice(0, filename.length-3);
require("./plugins/" + base).install(mongoose);
});
}
}

/**
* Installation function that will load all of the types and install
* all of the plugins that are defined for this mongoose extension.
*
* @param {Mongoose} the active Mongoose instance for installation
* @return the utilities of the extension (see './utils.js')
*/
exports.install = function (mongoose) {
loadTypes (mongoose);
installPlugins(mongoose);
return utils;
}
120 changes: 120 additions & 0 deletions lib/plugins/resolveDBRefs.js
@@ -0,0 +1,120 @@
/**
* lib/plugins/resolveDbRefs.js - DBRef Resolver
*
* Copyright 2011, Stuart Hudson <goulash1971@yahoo.com>
* Released under the terms of the MIT License.
*
* Version 0.0.1
*/
var mongoose = require("mongoose");

/**
* Utility function that constructs a table of names for a given
* path.
*
* @param {String} the path name
* @return {Object} specifying the 'cache', 'getter' and 'setter'
*/
var namesForPath = function (path) {
var root = path.replace(/^[_]+/g,"");
var names = {cache: "$" + root};
root = root[0].toUpperCase() + root.slice(1);
names['getter'] = "get" + root;
names['setter'] = "set" + root;
return names;
}


/**
* Plugin that will create a getter/setter method pair for each path in
* a schema where the schema type is 'DBRef' and the 'resolve' flag true.
*
* The setter method will convert any 'Object' (model) passed into a
* database reference, and the getter method will resolve the stored
* database reference to an object using the connection of the owning
* model instance.
*
* The getter & setter methods use the path name as the root of the
* method signature (i.e. 'name' gives 'getName' & 'setName') but where
* the path is private (i.e. '_' prefix) this is stripped when creating
* the getter & setter (i.e. '_age' gives 'getAge' and 'setAge').
*
* If the 'cache' flag is set to true then resolved value is cached
* in a model property that has the path name prefixed with a '$'
* sign (i.e. 'name' cached in '$name' and '_age' cached in '$age').
*
* Each setter has one argument, the object to be set via reference
* or 'null' and the getter has 2 arguments, a callback function to be
* invoked after the value has been resolved and an optional boolean
* argument, 'force', that will ignore any cached value.
*
* @param {Object} the schema the plugin is being used against
* @param {Object} global options that apply to this plugin
*/
exports.resolveDBRefs = resolveDBRefs = function (schema, options) {
if (!('mongoose' in options))
throw new Error("'mongoose' option not defined");
if (!('DBRef' in options.mongoose.Types))
throw new Error("'DBRef' type not installed");
var dbref = options.mongoose.SchemaTypes.DBRef;
schema.eachPath(
function (path, defn) {
if (defn.options['type'] !== dbref) return;
if (defn.options['resolve'] === true) {
var names = namesForPath(path);
if (defn.options['cached'] === true) {
var cache = names['cache'];
schema.method(names['getter'], function(callback, force) {
var val = this[path];
if ((typeof val === 'undefined') || (val === null)) {
callback(null, null);
} else {
force = (force === true) ||
typeof this[cache] === 'undefined' ||
this[cache] === val;
if (!force) {
callback(null, this[cache]);
} else {
var self = this;
this.db.db.dereference(val, function(err, doc) {
self[cache] = doc; callback(err, doc) });
}
}
});
schema.method(names['setter'], function(value) {
this[cache] = value;
if (value !== null)
this[path] = dbref.prototype.cast(
{"$ref": value.collection.name,
"$id": value._id, "$db": value.db.db.databaseName});
else this[path] = null;
});
} else {
schema.method(names['getter'], function(callback) {
var val = this[path];
if ((typeof val === 'undefined') || (val === null))
callback(null, null);
else this.db.db.dereference(val, callback);
});
schema.method(names['setter'], function(value) {
if (value !== null)
this[path] = dbref.prototype.cast(
{"$ref": value.collection.name,
"$id": value._id, "$db": value.db.db.databaseName});
else this[path] = null;
});
}
}
});
}



/**
* Installer that installs a plugin into the mongoose infrastructure
*
* @param {Mongoose} the active Mongoose instance for installation
*/
exports.install = function (mongoose) {
return mongoose.plugin(resolveDBRefs, {mongoose: mongoose});
}
90 changes: 90 additions & 0 deletions lib/types/dbref.js
@@ -0,0 +1,90 @@
/**
* lib/types/dbref.js - the DBRef type
*
* Copyright 2011, Stuart Hudson <goulash1971@yahoo.com>
* Released under the terms of the MIT License.
*
* Version 0.0.1
*/
var mongoose = require("mongoose");

/**
* Utility function that will cast a single object for a condition
*
* @param {Object} the single object to be handled
*/
function handleSingle (value) {
return this.cast(value);
}

/**
* Utility function that will cast an array for a condition
*
* @param {Array} the array of objects to be handled
*/
function handleArray (value) {
var self = this;
return value.map (function(m) {return self.cast(m);});
}


/**
* Loader that loads the type into the mongoose infrastructure
*
* @param {Mongoose} the active Mongoose instance for installation
*/
exports.loadType = function (mongoose) {
// The types that are used for schema and models
var SchemaType = mongoose.SchemaType;
var SchemaTypes = mongoose.SchemaTypes;

// The native type used for storage
var dbref = mongoose.mongo.BSONPure.DBRef;

// Constructor for schema type
function DBRef (value, options) {
SchemaType.call(this, value, options);
};

// Direct inheritence from schema type
DBRef.prototype.__proto__ = SchemaType.prototype;

// Testing method to evaluate whether check needed
DBRef.prototype.checkRequired = function (value) {
return !!value && value instanceof dbref;
};

// Casting function using raw or processed refs
DBRef.prototype.cast = function (value) {
var oid = SchemaTypes.ObjectId;
if (value === null) return value;
if (value instanceof dbref) return value;
if (typeof value !== 'object') throw new CastError('db reference', value);
return new dbref(value["$ref"], oid.prototype.cast(value["$id"]), value["$db"]);
};

// Condition handlers - glues condition to casting
DBRef.prototype.$conditionalHandlers = {
'$ne': handleSingle,
'$in': handleArray,
'$nin': handleArray
};

// Casting function used in queries & conditions
DBRef.prototype.castForQuery = function ($conditional, value) {
var handler;
if (arguments.length === 2) {
handler = this.$conditionalHandlers[$conditional];
if (!handler)
throw new Error("Can't use " + $conditional + " with DBRef.");
return handler.call(this, value);
} else {
value = $conditional;
return this.cast(value);
}
};

// Perform the installation
mongoose.SchemaTypes.DBRef = DBRef;
mongoose.Types.DBRef = dbref;
}
20 changes: 20 additions & 0 deletions lib/utils.js
@@ -0,0 +1,20 @@
/**
* lib/utils.js - module utilities loader
*
* Copyright 2011, Stuart Hudson <goulash1971@yahoo.com>
* Released under the terms of the MIT License.
*
* Version 0.0.1
*/

/**
* Utility that will de-reference a DBRef using the database
* assciated with a given connection
*
* @param {Connection} a database connection
* @param {DBRef} a valid database reference instance
* @param {Function} standard callback '(err, doc)'
*/
exports.fetch = fetch = function (conn, dbref, callback) {
return conn.db.dereference(dbref, callback);
}
33 changes: 33 additions & 0 deletions package.json
@@ -0,0 +1,33 @@
{
"name": "mongoose-dbref",
"private": true,
"description": "Plugin support for DBRef in Mongoose",
"version": "0.0.1",
"author": "Stuart Hudson <goulash1971@yahoo.com>",
"keywords": ["mongodb", "mongoose", "mongo", "types", "dbref"],
"homepage": "https://github.com/goulash1971/mongoose-dbref",
"contributors": [
"Stuart Hudson <goulash1971@yahoo.com> (http://goulash1971.com/)"
],
"dependencies": {
"mongoose": ">= 1.0.16"
},
"scripts": {
"test": "make test"
},
"directories": {
"lib": "lib/",
"test": "tests/"
},
"main": "./index",
"engines": {
"node": ">= 0.1.101"
},
"repository": {
"type": "git",
"url": "git://github.com/goulash1971/mongoose-dbref.git"
},
"licenses": [
{"type": "The MIT License", "url": "http://www.opensource.org/licenses/mit-license.php"}
]
}

0 comments on commit 064e699

Please sign in to comment.