waterline-dirty
Advanced tools
Comparing version 0.3.0 to 0.4.1
@@ -17,3 +17,3 @@ /*--------------------------------------------------------------- | ||
function DirtyAdapter () { | ||
module.exports = function () { | ||
@@ -34,6 +34,20 @@ | ||
// Maintain connections to open file and memory stores | ||
var connections = { }; | ||
// String to precede key name for schema defininitions | ||
var schemaPrefix = 'waterline_schema_'; | ||
// String to precede key name for actual data in collection | ||
var dataPrefix = 'waterline_data_'; | ||
var adapter = { | ||
config: { | ||
// Enable transactions by allowing Dirty to create a commitLog | ||
commitLog: true, | ||
// Default configuration for collections | ||
defaults: { | ||
// If inMemory is true, all data will be destroyed when the server stops | ||
@@ -44,26 +58,26 @@ // otherwise, it will be written to disk | ||
// File path for disk file output (when NOT in inMemory mode) | ||
dbFilePath: './.waterline/dirty.db', | ||
filePath: './.waterline/dirty.db' | ||
}, | ||
// String to precede key name for schema defininitions | ||
schemaPrefix: 'waterline_schema_', | ||
// Logic to handle the (re)instantiation of collections | ||
registerCollection: function(collection, cb) { | ||
var self = this; | ||
var collectionName = collection.identity; | ||
// String to precede key name for actual data in collection | ||
dataPrefix: 'waterline_data_' | ||
}, | ||
// If the configuration in this collection corresponds | ||
// with an existing connection, reuse it | ||
connections[collection.identity] = _.find(connections, function (connection, name) { | ||
return connection.inMemory === collection.inMemory && | ||
connection.filePath === collection.filePath; | ||
}); | ||
// Initialize the underlying data model | ||
initialize: function(cb) { | ||
var my = this; | ||
if (connections[collection.identity]) afterwards(); | ||
if(! this.config.inMemory) { | ||
// Check that dbFilePath file exists and build tree as neessary | ||
require('fs-extra').touch(this.config.dbFilePath, function(err) { | ||
if(err) return cb(err); | ||
my.db = new(dirty.Dirty)(my.config.dbFilePath); | ||
// Otherwise initialize for the first time | ||
else { | ||
connect(collection, function (err, connection) { | ||
// Save reference to connection | ||
connections[collection.identity] = connection; | ||
afterwards(); | ||
}); | ||
} else { | ||
this.db = new(dirty.Dirty)(); | ||
afterwards(); | ||
} | ||
@@ -73,61 +87,58 @@ | ||
// Trigger callback with no error | ||
my.db.on('load', function() { | ||
cb(); | ||
}); | ||
} | ||
}, | ||
// Grab current auto-increment value from database and populate it in-memory | ||
// (it's ok if it doesn't exist-- it will only exist if Dirty persisting to disk | ||
// and the collection has been initialized at least once) | ||
var schema = connections[collectionName].db.get(schemaPrefix + collectionName); | ||
// Logic to handle the (re)instantiation of collections | ||
initializeCollection: function(collectionName, cb) { | ||
var self = this; | ||
statusDb[collectionName] = (schema && schema.autoIncrement) ? schema : {autoIncrement: 1}; | ||
// Grab current auto-increment value from database and populate it in-memory | ||
var schema = this.db.get(this.config.schemaPrefix + collectionName); | ||
statusDb[collectionName] = (schema && schema.autoIncrement) ? schema : {autoIncrement: 1}; | ||
self.getAutoIncrementAttribute(collectionName, function (err,aiAttr) { | ||
// Check that the resurrected auto-increment value is valid | ||
self.find(collectionName, { | ||
where: { | ||
id: statusDb[collectionName].autoIncrement | ||
} | ||
}, function (err, models) { | ||
if (err) return cb(err); | ||
self.getAutoIncrementAttribute(collectionName, function (err,aiAttr) { | ||
// Check that the resurrected auto-increment value is valid | ||
self.find(collectionName, { | ||
where: { | ||
id: statusDb[collectionName].autoIncrement | ||
} | ||
}, function (err, models) { | ||
if (err) return cb(err); | ||
// If that model already exists, warn the user and generate the next-best possible auto-increment key | ||
if (models && models.length) { | ||
// If that model already exists, warn the user and generate the next-best possible auto-increment key | ||
if (models && models.length) { | ||
// Find max | ||
self.find(collectionName, {}, function (err,models) { | ||
var autoIncrement = _.max(models,function (model){ | ||
return model[aiAttr]; | ||
}); | ||
autoIncrement = autoIncrement && autoIncrement[aiAttr] || 0; | ||
autoIncrement++; | ||
// Find max | ||
self.find(collectionName, {}, function (err,models) { | ||
var autoIncrement = _.max(models,function (model){ | ||
return model[aiAttr]; | ||
statusDb[collectionName].autoIncrement = autoIncrement; | ||
cb(); | ||
}); | ||
autoIncrement = autoIncrement && autoIncrement[aiAttr] || 0; | ||
autoIncrement++; | ||
} | ||
else cb(); | ||
}); | ||
}); | ||
self.log.warn("On-disk auto-increment ID corrupt, using: "+autoIncrement+" on attribute:",aiAttr); | ||
statusDb[collectionName].autoIncrement = autoIncrement; | ||
cb(); | ||
}); | ||
} | ||
else cb(); | ||
}); | ||
}); | ||
} | ||
}, | ||
// Logic to handle flushing collection data to disk before the adapter shuts down | ||
teardownCollection: function(collectionName, cb) { | ||
var my = this; | ||
// Always go ahead and write the new auto-increment to disc, even though it will be wrong sometimes | ||
// (this is done so that the auto-increment counter can be "resurrected" when the adapter is restarted from disk) | ||
var schema = _.extend(this.db.get(this.config.schemaPrefix + collectionName),{ | ||
autoIncrement: statusDb[collectionName].autoIncrement | ||
}); | ||
this.log.info("Waterline saving "+collectionName+" collection..."); | ||
this.db.set(this.config.schemaPrefix + collectionName, schema, function (err) { | ||
my.db = null; | ||
cb && cb(err); | ||
}); | ||
// Flush data to disk before the adapter shuts down | ||
teardown: function(cb) { | ||
async.forEach(_.keys(connections), function (collectionName, cb) { | ||
// Always go ahead and write the new auto-increment to disc, even though it will be wrong sometimes | ||
// (this is done so that the auto-increment counter can be "resurrected" when the adapter is restarted from disk) | ||
var schema = connections[collectionName].db.get(schemaPrefix + collectionName); | ||
if (!schema) return cb(badSchemaError(collectionName, schemaPrefix)); | ||
schema = _.extend(schema,{ | ||
autoIncrement: statusDb[collectionName].autoIncrement | ||
}); | ||
connections[collectionName].db.set(schemaPrefix + collectionName, schema, function (err) { | ||
connections[collectionName].db = null; | ||
cb && cb(err); | ||
}); | ||
}, cb); | ||
}, | ||
@@ -139,4 +150,7 @@ | ||
describe: function(collectionName, cb) { | ||
this.log.verbose(" DESCRIBING :: " + collectionName); | ||
var schema = this.db.get(this.config.schemaPrefix + collectionName); | ||
// (it's ok if schema doesn't exist-- it will only exist if Dirty persisting to disk | ||
// and the collection has been initialized at least once) | ||
var schema = connections[collectionName].db.get(schemaPrefix + collectionName); | ||
var attributes = schema && schema.attributes; | ||
@@ -147,19 +161,18 @@ return cb(null, attributes); | ||
// Create a new collection | ||
define: function(collectionName, attributes, cb) { | ||
this.log.verbose(" DEFINING " + collectionName, { | ||
as: schema | ||
}); | ||
define: function(collectionName, definition, cb) { | ||
var self = this; | ||
var schema = { | ||
attributes: _.clone(attributes), | ||
definition = _.extend({ | ||
// Reset autoIncrement counter | ||
autoIncrement: 1 | ||
}; | ||
}, definition); | ||
// Write schema and status objects | ||
return self.db.set(this.config.schemaPrefix + collectionName, schema, function(err) { | ||
// Write schema objects | ||
return connections[collectionName].db.set(schemaPrefix + collectionName, definition, function(err) { | ||
if(err) return cb(err); | ||
// Set in-memory schema for this collection | ||
statusDb[collectionName] = schema; | ||
// Set in-memory definition for this collection | ||
statusDb[collectionName] = definition; | ||
cb(); | ||
@@ -171,20 +184,14 @@ }); | ||
drop: function(collectionName, cb) { | ||
var self = this; | ||
self.log.verbose(" DROPPING " + collectionName); | ||
return self.db.rm(self.config.dataPrefix + collectionName, function(err) { | ||
return connections[collectionName].db.rm(dataPrefix + collectionName, function(err) { | ||
if(err) return cb("Could not drop collection!"); | ||
return self.db.rm(self.config.schemaPrefix + collectionName, cb); | ||
return connections[collectionName].db.rm(schemaPrefix + collectionName, cb); | ||
}); | ||
}, | ||
// No alter necessary-- use the default in waterline core | ||
// Create one or more new models in the collection | ||
create: function(collectionName, values, cb) { | ||
this.log.verbose(" CREATING :: " + collectionName, values); | ||
values = _.clone(values) || {}; | ||
var dataKey = this.config.dataPrefix + collectionName; | ||
var data = this.db.get(dataKey); | ||
var dataKey = dataPrefix + collectionName; | ||
var data = connections[collectionName].db.get(dataKey); | ||
var self = this; | ||
@@ -194,3 +201,4 @@ | ||
// Lookup schema & status so we know all of the attribute names and the current auto-increment value | ||
var schema = this.db.get(this.config.schemaPrefix + collectionName); | ||
var schema = connections[collectionName].db.get(schemaPrefix + collectionName); | ||
if (!schema) return cb(badSchemaError(collectionName, schemaPrefix)); | ||
@@ -210,3 +218,3 @@ // Auto increment fields that need it | ||
// Replace data collection and go back | ||
self.db.set(dataKey, data, function(err) { | ||
connections[collectionName].db.set(dataKey, data, function(err) { | ||
return cb(err, values); | ||
@@ -222,4 +230,4 @@ }); | ||
find: function(collectionName, options, cb) { | ||
var dataKey = this.config.dataPrefix + collectionName; | ||
var data = this.db.get(dataKey) || []; | ||
var dataKey = dataPrefix + collectionName; | ||
var data = connections[collectionName].db.get(dataKey) || []; | ||
@@ -239,11 +247,7 @@ // Get indices from original data which match, in order | ||
update: function(collectionName, options, values, cb) { | ||
this.log.verbose(" UPDATING :: " + collectionName, { | ||
options: options, | ||
values: values | ||
}); | ||
var my = this; | ||
var dataKey = this.config.dataPrefix + collectionName; | ||
var data = this.db.get(dataKey); | ||
var dataKey = dataPrefix + collectionName; | ||
var data = connections[collectionName].db.get(dataKey); | ||
@@ -265,3 +269,3 @@ // Query result set using options | ||
// Replace data collection and go back | ||
this.db.set(dataKey, data, function(err) { | ||
connections[collectionName].db.set(dataKey, data, function(err) { | ||
cb(err, resultSet); | ||
@@ -273,7 +277,5 @@ }); | ||
destroy: function(collectionName, options, cb) { | ||
this.log.verbose(" DESTROYING :: " + collectionName, options); | ||
var dataKey = dataPrefix + collectionName; | ||
var data = connections[collectionName].db.get(dataKey); | ||
var dataKey = this.config.dataPrefix + collectionName; | ||
var data = this.db.get(dataKey); | ||
// Query result set using options | ||
@@ -290,3 +292,3 @@ var matchIndices = getMatchIndices(data,options); | ||
// Replace data collection and respond with what's left | ||
this.db.set(dataKey, data, function(err) { | ||
connections[collectionName].db.set(dataKey, data, function(err) { | ||
cb(err); | ||
@@ -309,2 +311,37 @@ }); | ||
// Initialize an underlying data connection given a collection def | ||
function connect (collection, cb) { | ||
var collectionName = collection.identity; | ||
// Default to inMemory if no file path is specified | ||
if (_.isUndefined(collection.inMemory) && !collection.filePath) { | ||
collection.inMemory = true; | ||
} | ||
if( collection.inMemory ) { | ||
var memDb = new(dirty.Dirty)(); | ||
memDb.on('load', function() { | ||
cb(null, { | ||
db : memDb, | ||
inMemory : true | ||
}); | ||
}); | ||
} | ||
else { | ||
// Check that filePath file exists and build tree as neessary | ||
require('fs-extra').touch(collection.filePath, function(err) { | ||
if(err) return cb(err); | ||
var fileDb = new(dirty.Dirty)(collection.filePath); | ||
fileDb.on('load', function() { | ||
cb(null, { | ||
db : fileDb, | ||
inMemory : true | ||
}); | ||
}); | ||
}); | ||
} | ||
} | ||
// Look for auto-increment field, increment counter accordingly, and return refined value set | ||
@@ -338,12 +375,8 @@ function doAutoIncrement (collectionName, attributes, values, ctx, cb) { | ||
return adapter; | ||
} | ||
function badSchemaError(collectionName, schemaPrefix) { | ||
return new Error('Cannot get schema from Dirty for collection: ' + collectionName + ' using schema prefix: '+schemaPrefix); | ||
} | ||
// Public API | ||
// * NOTE: The public API for adapters is a function that can be passed a set of options | ||
// * It returns the complete adapter, augmented with the options provided | ||
module.exports = function (options) { | ||
var adapter = new DirtyAdapter(); | ||
adapter.config = _.extend(adapter.config, options || {}); | ||
return adapter; | ||
}; | ||
}; |
{ | ||
"name": "waterline-dirty", | ||
"version": "0.3.0", | ||
"version": "0.4.1", | ||
"description": "Waterline adapter for felixge's node-dirty", | ||
@@ -5,0 +5,0 @@ "main": "DirtyAdapter.js", |
20203
480