Skip to content

Commit

Permalink
Added save() to modelInstance.
Browse files Browse the repository at this point in the history
Data can now be saved to the db. It differentiates between inserts and
updates, using db.collection.insert and db.collection.findAndModify,
respectively.
  • Loading branch information
colladow committed Jun 16, 2010
1 parent 8d9ed22 commit fd87ca2
Show file tree
Hide file tree
Showing 3 changed files with 176 additions and 50 deletions.
37 changes: 25 additions & 12 deletions examples/test.js
Expand Up @@ -75,14 +75,11 @@ var user = model.model({
}
});

/*
sys.puts(user.toString());
var u = user.create({
username: 'colladow',
first: 'Wilson',
username: 'colladox',
first: 'Xilson',
last: 'Collado',
langs: ['javascript', 'python'],
langs: ['java', 'perl'],
address: {
street: '7711 60th St',
city: 'Ridgewood',
Expand All @@ -91,12 +88,28 @@ var u = user.create({
}
});

sys.puts(u);
sys.puts(u.validate());
sys.puts(u.fullName());
*/
var u2 = user.getById('4c12bef6c428a53049e36cc5', function(instance){

var u2 = user.getById('4c1929557fa277510c000001', function(instance){
sys.puts(instance.fullName());

var newLangs = instance.get('langs');
//newLangs.push('C');
newLangs.pop();
instance.set('langs', newLangs);

instance.save(function(success, obj){
var u3 = user.getById('4c1929557fa277510c000001', function(instance){
sys.puts(sys.inspect(instance.get('langs')));
});
});
});
/*
u.save(function(success, obj){
if(success){
sys.puts('Created => ');
sys.puts(sys.inspect(obj));
sys.puts(sys.inspect(u.get('_id')));
}
});
*/
78 changes: 58 additions & 20 deletions lib/db.js
@@ -1,37 +1,75 @@
var mongo = require('mongodb/');

var server = new mongo.Server('localhost', mongo.Connection.DEFAULT_PORT);
var db = new mongo.Db('test', server);

// DB connection and query helper methods
// the intention is to help "flatten" out the mongodb native api
var helper = function(){
var self = {};
var server = new mongo.Server('localhost', mongo.Connection.DEFAULT_PORT),
db = new mongo.Db('test', server),
conn = null,
self = {};

// grab a collection handler from the db
self.collection = function(collectionName, callback){
db.open(function(err, connection){
if(!conn && !err){
conn = connection;
}

conn.collection(collectionName, function(err, coll){
callback(err, coll);
});
});
};

// run a findOne querying on an ObjectId
self.getModelById = function(collectionName, id, callback){
self.findOne(collectionName, { '_id': id }, callback);
};

// insert documents into the database
// docs can be one document or an array of documents
self.insert = function(collectionName, docs, callback){
self.collection(collectionName, function(err, coll){
coll.insert(docs, function(err, objects){
callback(err, objects);

db.close();
});
});
};

// run a find query and pass the cursor along
self.find = function(collectionName, query, callback){
db.open(function(err, db){
db.collection(collectionName, function(err, coll){
coll.find(query, function(err, cursor){
callback(err, cursor);
self.collection(collectionName, function(err, coll){
coll.find(query, function(err, cursor){
callback(err, cursor);

db.close();
});
db.close();
});
});
};

self.findOne = function(collectionName, query, callback){
db.open(function(err, db){
db.collection(collectionName, function(err, coll){
coll.findOne(query, function(err, doc){
callback(err, doc);
db.close();
});
// runs a findAndModify to update single instances
// very useful for _id based updates
self.findAndModify = function(collectionName, query, doc, callback){
self.collection(collectionName, function(err, coll){
coll.findAndModify(query, [], doc, function(err, obj){
callback(err, obj);

db.close();
});
});
};

self.getModelById = function(collectionName, id, callback){
self.findOne(collectionName, { '_id': id }, callback);
// run a findOne query and pass the document along
self.findOne = function(collectionName, query, callback){
self.collection(collectionName, function(err, coll){
coll.findOne(query, function(err, doc){
callback(err, doc);

db.close();
});
});
};

return self;
Expand Down
111 changes: 93 additions & 18 deletions lib/model.js
Expand Up @@ -38,10 +38,14 @@ var field = function(options){
// The model.model object represents a document in the database.
var model = function(options){
// special options include
// fields -> an object of field names to instances of model.field
// validate -> a custom validation function to validate this model
// independently of field validation, can be used
// to test things where one field depends on the other
// fields -> an object of field names to instances of model.field
// methods -> an object of instance methods to attach to the modelInstance
// name -> the name of this model
// collectionName -> the name of the collection in the db.
// if not provided, defaults to the name in lower case
// validate -> a custom validation function to validate this model
// independently of field validation, can be used
// to test things where one field depends on the other
var fields = {},
methods = {},
name = options.name || '',
Expand All @@ -54,17 +58,23 @@ var model = function(options){
}

// create an instance of this model
self.create = function(obj){
self.create = function(obj, isNew){
isNew = (typeof isNew === 'undefined') ? true : isNew;

var instance = modelInstance({
collection: collectionName,
fields: fields,
values: obj,
methods: methods,
customValidation: validate
customValidation: validate,
newFlag: isNew
});

return instance;
};

// query the database for the data associated with this model
// based on an ObjectID. strings are converted to ObjectID.
self.getById = function(id, callback){
if(typeof id === 'string'){
id = mongo.ObjectID.createFromHexString(id);
Expand All @@ -76,14 +86,8 @@ var model = function(options){
}

db.getModelById(collectionName, id, function(err, doc){
var instance = modelInstance({
fields: fields,
values: doc,
methods: methods,
customValidation: validate
});

callback(instance);
// get the data and create a new modelInstance
callback(self.create(doc, false));
});
};

Expand All @@ -104,6 +108,7 @@ var model = function(options){
}
}

// if an _id is not provided, create a new one
if(!fields['_id']){
fields['_id'] = field({
type: mongo.ObjectID,
Expand All @@ -116,6 +121,7 @@ var model = function(options){
validate = options.validate;
}

// unpack the modelInstance methods
if(options.methods){
for(var m in options.methods){
if(options.methods.hasOwnProperty(m) && typeof options.methods[m] === 'function'){
Expand All @@ -130,21 +136,89 @@ var model = function(options){
// The model.modelInstance object is an instance of a model defined by model.model.
var modelInstance = function modelInstance(spec){
var fields = spec.fields, // the list of model.field objects
collection = spec.collection,
customValidation = spec.customValidation,
fieldValues = {},
methods = spec.methods,
newFields = [],
dirtyFields = [],
newFlag = spec.newFlag,
self = {};

self.get = function(fieldName){
return fieldValues[fieldName];
};

self.set = function(fieldName, value){
if(!fields[fieldName]){
newFields.push(fieldName);
self.isBound = function(){
return !newFlag;
};

// update or insert this model
self.save = function(callback){
var err = self.validate(),
doc = {},
affectedFields = null;

// make sure the data validates
if(err.length){
callback(false, err);
return;
}

if(newFlag){
// if this is a new model object...

// grab the field values
for(var f in fields){
if(fields.hasOwnProperty(f)){
doc[f] = self.get(f);
}
}

// insert the data into the database
db.insert(collection, doc, function(err, obj){
if(err){
callback(false, err);
return;
}

// update the ObjectId for this instance
if(obj['_id']){
self.set('_id', obj['_id']);
}

// clear the dirty fields and the newFlag (it's not new anymore)
dirtyFields = [];
newFlag = false;

// forward the object
callback(true, obj);
});
}else{
// if this is instance is already in the db...

// grab the fields that have changed
dirtyFields.forEach(function(f){
if(fields.hasOwnProperty(f)){
doc[f] = self.get(f);
}
});

// update the data in the db
db.findAndModify(collection, { '_id': self.get('_id') }, { '$set': doc }, function(err, obj){
if(err){
callback(false, err);
return;
}

dirtyFields = []; // clear the dirty fields
callback(true, obj);
});
}
};

self.set = function(fieldName, value){
dirtyFields.push(fieldName);

fieldValues[fieldName] = value;
};

Expand Down Expand Up @@ -182,6 +256,7 @@ var modelInstance = function modelInstance(spec){
self.set(val, spec.values[val]);
}
}
dirtyFields = []; // clear the dirty fields

// set an objectid if it's not given
if(spec.values && !spec.values['_id']){
Expand Down

0 comments on commit fd87ca2

Please sign in to comment.