waterline
Advanced tools
Comparing version 0.4.7 to 0.5.0
111
adapter.js
@@ -126,3 +126,3 @@ var async = require('async'); | ||
}, function (err) { | ||
if (err) throw err; | ||
if (err) return cb(err); | ||
async.forEach(_.keys(deprecatedAttributes), function (attrName, cb) { | ||
@@ -143,5 +143,50 @@ adapterDef.removeAttribute(collectionName, attrName, deprecatedAttributes[attrName], cb); | ||
////////////////////////////////////////////////////////////////////// | ||
this.__save = function (collectionName, values, cb) { | ||
// TODO: create or update model in adapter, using id to determine the model in question | ||
// BUT don't include the save() method! | ||
// TODO: use whatever the primary key is configured to, not just `id` | ||
var pk = 'id'; | ||
var pkValue = values[pk]; | ||
// TODO: use updateOrCreate() | ||
this.update(collectionName, pkValue, values, cb); | ||
}; | ||
this.__destroy = function (collectionName, cb) { | ||
// TODO: destroy in adapter, using id to determine the model in question | ||
cb("destroy() NOT SUPPORTED YET!"); | ||
}; | ||
// Build a callback function which will decorate a model | ||
// with a save() method | ||
this.decorateModel = function (collectionName, cb) { | ||
return function (err, set) { | ||
if (_.isArray(set)) { | ||
// Add save() method to set | ||
_.each(set, function (model) { | ||
if (!_.isObject(model)) return; | ||
if (!model.save) model.save = _.bind(self.__save, self, collectionName, model); | ||
if (!model.destroy) model.destroy = _.bind(self.__destroy, self, collectionName, model); | ||
}); | ||
return cb(err,set); | ||
} | ||
else if (_.isObject(set)) { | ||
// Add save() method to model | ||
if (!set.save) set.save = _.bind(self.__save, self, collectionName, set); | ||
if (!set.destroy) set.destroy = _.bind(self.__destroy, self, collectionName, set); | ||
return cb(err,set); | ||
} | ||
else return cb(err,set); | ||
}; | ||
}; | ||
this.create = function(collectionName, values, cb) { | ||
// Build enhanced callback fn | ||
cb = this.decorateModel(collectionName, cb); | ||
if(!adapterDef.create) return cb("No create() method defined in adapter!"); | ||
adapterDef.create(collectionName, values, cb); | ||
@@ -152,2 +197,5 @@ }; | ||
this.findAll = function(collectionName, criteria, cb) { | ||
// Build enhanced callback fn | ||
cb = this.decorateModel(collectionName, cb); | ||
if(!adapterDef.find) return cb("No find() method defined in adapter!"); | ||
@@ -160,2 +208,3 @@ criteria = normalize.criteria(criteria); | ||
this.find = function(collectionName, criteria, cb) { | ||
// If no criteria specified AT ALL, use first model | ||
@@ -165,4 +214,4 @@ if (!criteria) criteria = {limit: 1}; | ||
this.findAll(collectionName, criteria, function (err, models) { | ||
if (!models) return cb(); | ||
if (models.length < 1) return cb(); | ||
if (!models) return cb(err); | ||
if (models.length < 1) return cb(err); | ||
else if (models.length > 1) return cb("More than one "+collectionName+" returned!"); | ||
@@ -174,2 +223,5 @@ else return cb(null,models[0]); | ||
this.count = function(collectionName, criteria, cb) { | ||
// Build enhanced callback fn | ||
cb = this.decorateModel(collectionName, cb); | ||
var self = this; | ||
@@ -187,2 +239,5 @@ criteria = normalize.criteria(criteria); | ||
this.update = function(collectionName, criteria, values, cb) { | ||
// Build enhanced callback fn | ||
cb = this.decorateModel(collectionName, cb); | ||
if(!adapterDef.update) return cb("No update() method defined in adapter!"); | ||
@@ -195,2 +250,5 @@ criteria = normalize.criteria(criteria); | ||
this.destroy = function(collectionName, criteria, cb) { | ||
// Build enhanced callback fn | ||
cb = this.decorateModel(collectionName, cb); | ||
if(!adapterDef.destroy) return cb("No destroy() method defined in adapter!"); | ||
@@ -212,2 +270,5 @@ criteria = normalize.criteria(criteria); | ||
if(adapterDef.findOrCreate) { | ||
// Build enhanced callback fn | ||
cb = this.decorateModel(collectionName, cb); | ||
adapterDef.findOrCreate(collectionName, criteria, values, cb); | ||
@@ -240,6 +301,11 @@ } | ||
this.createEach = function (collectionName, valuesList, cb) { | ||
var my = this; | ||
var self = this; | ||
// Custom user adapter behavior | ||
if (adapterDef.createEach) adapterDef.createEach.apply(this,arguments); | ||
if (adapterDef.createEach) { | ||
// Build enhanced callback fn | ||
cb = this.decorateModel(collectionName, cb); | ||
adapterDef.createEach(collectionName, valuesList, cb); | ||
} | ||
@@ -249,5 +315,5 @@ // Default behavior | ||
// Create transaction name based on collection | ||
my.transaction(collectionName+'.waterline.default.createEach', function (err,done) { | ||
self.transaction(collectionName+'.waterline.default.createEach', function (err,done) { | ||
async.forEach(valuesList, function (values,cb) { | ||
my.create(collectionName, values, cb); | ||
self.create(collectionName, values, cb); | ||
}, done); | ||
@@ -258,7 +324,12 @@ },cb); | ||
// If an optimized findOrCreateEach exists, use it, otherwise use an asynchronous loop with create() | ||
this.findOrCreateEach = function (collectionName, valuesList, cb) { | ||
var my = this; | ||
this.findOrCreateEach = function (collectionName, attributesToCheck, valuesList, cb) { | ||
var self = this; | ||
// Custom user adapter behavior | ||
if (adapterDef.findOrCreateEach) adapterDef.findOrCreateEach(collectionName,valuesList,cb); | ||
if (adapterDef.findOrCreateEach) { | ||
// Build enhanced callback fn | ||
cb = this.decorateModel(collectionName, cb); | ||
adapterDef.findOrCreateEach(collectionName, attributesToCheck, valuesList, cb); | ||
} | ||
@@ -268,5 +339,13 @@ // Default behavior | ||
// Create transaction name based on collection | ||
my.transaction(collectionName+'.waterline.default.createEach', function (err,done) { | ||
self.transaction(collectionName+'.waterline.default.findOrCreateEach', function (err,done) { | ||
async.forEach(valuesList, function (values,cb) { | ||
my.findOrCreate(collectionName, values, null, cb); | ||
// Check that each of the criteria keys match: | ||
// build a criteria query | ||
var criteria = {}; | ||
_.each(attributesToCheck, function (attrName) { | ||
criteria[attrName] = values[attrName]; | ||
}); | ||
return self.findOrCreate(collectionName, criteria, values, cb); | ||
}, done); | ||
@@ -294,3 +373,3 @@ },cb); | ||
if (adapterDef.transaction) return adapterDef.transaction(transactionName, atomicLogic, afterUnlock); | ||
else if (!adapterDef.commitLog) throw new Error("Cannot process transaction. Commit log disabled in adapter, and no custom transaction logic is defined."); | ||
else if (!adapterDef.commitLog) return afterUnlock("Cannot process transaction. Commit log disabled in adapter, and no custom transaction logic is defined."); | ||
@@ -309,3 +388,3 @@ // Generate unique lock | ||
console.error("But the transactionCollection is: ",this.transactionCollection); | ||
throw new Error("Transaction collection not defined!"); | ||
return afterUnlock("Transaction collection not defined!"); | ||
} | ||
@@ -432,3 +511,3 @@ this.transactionCollection.create(newLock, function afterCreatingTransaction(err, newLock) { | ||
this.drop(collection.identity, function afterDrop (err, data) { | ||
if(err) cb(err); | ||
if(err) return cb(err); | ||
else self.define(collection.identity, collection, cb); | ||
@@ -435,0 +514,0 @@ }); |
@@ -80,4 +80,4 @@ ////////////////////////////////////////////////////////////////////// | ||
var usage = _.str.capitalize(this.identity) + '.' + actualMethodName + '(someValue,[options],callback)'; | ||
if(_.isUndefined(value)) usageError('No value specified!', usage); | ||
if(options.where) usageError('Cannot specify `where` option in a dynamic ' + method + '*() query!', usage); | ||
if(_.isUndefined(value)) return usageError('No value specified!', usage, cb); | ||
if(options.where) return usageError('Cannot specify `where` option in a dynamic ' + method + '*() query!', usage, cb); | ||
@@ -263,3 +263,3 @@ | ||
if(_.isFunction(criteria) || _.isFunction(options)) { | ||
return usageError('Invalid options specified!', usage); | ||
return usageError('Invalid options specified!', usage, cb); | ||
} | ||
@@ -294,3 +294,3 @@ | ||
return this.find(criteria, options, cb); | ||
} else usageError('Criteria must be string or object!', usage); | ||
} else return usageError('Criteria must be string or object!', usage, cb); | ||
}; | ||
@@ -302,3 +302,3 @@ | ||
return this.findAll(criteria, options, cb); | ||
} else usageError('Criteria must be string or object!', usage); | ||
} else return usageError('Criteria must be string or object!', usage, cb); | ||
}; | ||
@@ -314,3 +314,3 @@ | ||
return this.findAll(criteria, options, cb); | ||
} else usageError('Criteria must be a string or object!', usage); | ||
} else return usageError('Criteria must be a string or object!', usage, cb); | ||
throw new notImplementedError(); | ||
@@ -327,3 +327,3 @@ }; | ||
return this.findAll(criteria, options, cb); | ||
} else usageError('Criteria must be a string or object!', usage); | ||
} else return usageError('Criteria must be a string or object!', usage, cb); | ||
throw new notImplementedError(); | ||
@@ -340,3 +340,3 @@ }; | ||
return this.findAll(criteria, options, cb); | ||
} else usageError('Criteria must be a string or object!', usage); | ||
} else return usageError('Criteria must be a string or object!', usage, cb); | ||
throw new notImplementedError(); | ||
@@ -369,5 +369,5 @@ }; | ||
criteria = _.extend({}, criteria, options); | ||
} else usageError('Invalid options specified!', usage); | ||
} else return usageError('Invalid options specified!', usage, cb); | ||
if(!_.isFunction(cb)) usageError('Invalid callback specified!', usage); | ||
if(!_.isFunction(cb)) return usageError('Invalid callback specified!', usage, cb); | ||
@@ -386,4 +386,4 @@ return adapter.count(this.identity, criteria, cb); | ||
var usage = _.str.capitalize(this.identity) + '.update(criteria, newValues, callback)'; | ||
if(!newValues) usageError('No updated values specified!', usage); | ||
if(!_.isFunction(cb)) usageError('Invalid callback specified!', usage); | ||
if(!newValues) return usageError('No updated values specified!', usage, cb); | ||
if(!_.isFunction(cb)) return usageError('Invalid callback specified!', usage, cb); | ||
@@ -444,9 +444,12 @@ // TODO: Validate constraints using Anchor | ||
} | ||
if (_.isArray(criteria)) { | ||
return this.findOrCreateEach(criteria,cb); | ||
// This is actually an implicit call to findOrCreateEach | ||
if (_.isArray(criteria) && _.isArray(values)) { | ||
return this.findOrCreateEach(criteria, values, cb); | ||
} | ||
var usage = _.str.capitalize(this.identity) + '.findOrCreate(criteria, values, callback)'; | ||
if(!criteria) usageError('No criteria option specified!', usage); | ||
if(!_.isFunction(cb)) usageError('Invalid callback specified!', usage); | ||
var usage = _.str.capitalize(this.identity) + '.findOrCreate([criteria], values, callback)'; | ||
if(!criteria) return usageError('No criteria option specified!', usage, cb); | ||
if(!_.isFunction(cb)) return usageError('Invalid callback specified!', usage, cb); | ||
return adapter.findOrCreate(this.identity, criteria, values, cb); | ||
@@ -460,6 +463,7 @@ }; | ||
this.createEach = function(valuesList, cb) { | ||
var usage = _.str.capitalize(this.identity) + '.createEach(valuesList, callback)'; | ||
if(!valuesList) usageError('No valuesList specified!', usage); | ||
if(!_.isArray(valuesList)) usageError('Invalid valuesList specified (should be an array!)', usage); | ||
if(!_.isFunction(cb)) usageError('Invalid callback specified!', usage); | ||
if(!valuesList) return usageError('No valuesList specified!', usage, cb); | ||
if(!_.isArray(valuesList)) return usageError('Invalid valuesList specified (should be an array!)', usage, cb); | ||
if(!_.isFunction(cb)) return usageError('Invalid callback specified!', usage, cb); | ||
adapter.createEach(this.identity, valuesList, cb); | ||
@@ -470,8 +474,14 @@ }; | ||
// For any that don't exist, create them | ||
this.findOrCreateEach = function(valuesList, cb) { | ||
var usage = _.str.capitalize(this.identity) + '.findOrCreateEach(valuesList, callback)'; | ||
if(!valuesList) usageError('No valuesList specified!', usage); | ||
if(!_.isArray(valuesList)) usageError('Invalid valuesList specified (should be an array!)', usage); | ||
if(!_.isFunction(cb)) usageError('Invalid callback specified!', usage); | ||
adapter.findOrCreateEach(this.identity, valuesList, cb); | ||
this.findOrCreateEach = function(attributesToCheck, valuesList, cb) { | ||
if(_.isFunction(valuesList)) { | ||
cb = valuesList; | ||
valuesList = null; | ||
} | ||
var usage = _.str.capitalize(this.identity) + '.findOrCreateEach(attributesToCheck, valuesList, callback)'; | ||
if(!_.isFunction(cb)) return usageError('Invalid callback specified!', usage, cb); | ||
if(!attributesToCheck) return usageError('No attributesToCheck specified!', usage, cb); | ||
if(!_.isArray(attributesToCheck)) return usageError('No attributesToCheck specified!', usage, cb); | ||
if(!valuesList) return usageError('No valuesList specified!', usage, cb); | ||
if(!_.isArray(valuesList)) return usageError('Invalid valuesList specified (should be an array!)', usage, cb); | ||
adapter.findOrCreateEach(this.identity, attributesToCheck, valuesList, cb); | ||
}; | ||
@@ -485,7 +495,7 @@ | ||
if(!atomicLogic) { | ||
return usageError('Missing required parameter: atomicLogicFunction!', usage); | ||
return usageError('Missing required parameter: atomicLogicFunction!', usage, cb); | ||
} else if(!_.isFunction(atomicLogic)) { | ||
return usageError('Invalid atomicLogicFunction! Not a function: ' + atomicLogic, usage); | ||
return usageError('Invalid atomicLogicFunction! Not a function: ' + atomicLogic, usage, cb); | ||
} else if(afterUnlock && !_.isFunction(afterUnlock)) { | ||
return usageError('Invalid afterUnlockFunction! Not a function: ' + afterUnlock, usage); | ||
return usageError('Invalid afterUnlockFunction! Not a function: ' + afterUnlock, usage, cb); | ||
} else return adapter.transaction(this.identity + '.' + transactionName, atomicLogic, afterUnlock); | ||
@@ -554,5 +564,9 @@ }; | ||
function usageError(err, usage) { | ||
console.error("\n\n"); | ||
throw new Error(err + '\n==============================================\nProper usage :: \n' + usage + '\n==============================================\n'); | ||
function usageError(err, usage, cb) { | ||
var message = err + '\n==============================================\nProper usage :: \n' + usage + '\n==============================================\n'; | ||
if (cb) return cb(message); | ||
else { | ||
console.error("\n\n"); | ||
throw new Error(message); | ||
} | ||
} | ||
@@ -559,0 +573,0 @@ |
// Global adapter defaults | ||
module.exports = { | ||
// By default assume the app path is the current directory | ||
appPath: __dirname, | ||
// ms to wait before warning that a tranaction is taking too long | ||
@@ -5,0 +8,0 @@ // TODO: move this logic as a configuration option into the actual transaction collection |
{ | ||
"name": "waterline", | ||
"version": "0.4.7", | ||
"version": "0.5.0", | ||
"description": "Adaptable data access layer for Node.js", | ||
@@ -5,0 +5,0 @@ "main": "waterline.js", |
@@ -93,5 +93,12 @@ /** | ||
it ('should not fail',function (done) { | ||
User.findOrCreateEach(testData,done); | ||
User.findOrCreateEach(['name','type'], testData, done); | ||
}); | ||
it ('SHOULD fail when only one arg is specified',function (done) { | ||
User.findOrCreateEach(testData, function (err) { | ||
if (err) return done(); | ||
else done("Should have failed, since no attributesToCheck arg was specified!"); | ||
}); | ||
}); | ||
it ('should have saved the proper values (with auto-increment values)',function (done) { | ||
@@ -98,0 +105,0 @@ User.findAll({type: testName},function (err,users) { |
@@ -14,2 +14,3 @@ // Dependencies | ||
var util = require('sails-util'); | ||
var fs = require('fs-extra'); | ||
@@ -28,2 +29,5 @@ /** | ||
// Trim slashes off of app path | ||
waterlineConfig.appPath = _.str.rtrim(waterlineConfig.appPath,'/'); | ||
// Only tear down waterline once | ||
@@ -134,9 +138,18 @@ // (if teardown() is called explicitly, don't tear it down when the process exits) | ||
function requireAdapter(adapterName, collectionName) { | ||
try { | ||
log('Loading module ' + adapterName + "..."); | ||
return require(adapterName)(); | ||
// First, try to stat the adapter module | ||
var modulePath = waterlineConfig.appPath+'/node_modules/'+adapterName; | ||
var exists = fs.existsSync(modulePath); | ||
log.verbose('Loading module ' + adapterName + "..."); | ||
// If it exists, require it. | ||
if (exists) return require(modulePath)(); | ||
else { | ||
var err = "Unknown adapter ("+adapterName+") in collection (" + collectionName +")"; | ||
log.error(err); | ||
log.error("Try running: npm install "+adapterName); | ||
log.error("To save the adapter as a permanent dependency, run: npm install "+adapterName+" --save"); | ||
process.exit(1); | ||
} | ||
catch (e) { | ||
throw new Error("Unknown adapter ("+adapterName+") in collection (" + collectionName +")"); | ||
} | ||
} | ||
@@ -143,0 +156,0 @@ |
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
126291
38
3190
2