Skip to content

Commit

Permalink
Added promise calls to Instance
Browse files Browse the repository at this point in the history
  • Loading branch information
notheotherben committed Sep 13, 2014
1 parent e674e5a commit fce4b5c
Showing 1 changed file with 125 additions and 111 deletions.
236 changes: 125 additions & 111 deletions lib/Instance.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ var ObjectID = require('mongodb').ObjectID,
_ = require('lodash'),
EventEmitter = require('events').EventEmitter,
debug = require('debug')('iridium:Instance'),
fn = require('functionality'),
Q = require('q'),

inherit = require('./utils/Inherit.js'),
diff = require('./utils/diff.js');
Expand Down Expand Up @@ -62,126 +64,105 @@ Object.defineProperty(Instance.prototype, 'document', {
enumerable: false
});

Instance.prototype.save = function(conditions, changes, callback) {
/// <signature>
/// <summary>Saves changes made to the current instance to the database without waiting for a response</summary>
/// </signature>
/// <signature>
/// <summary>Saves changes made to the current instance to the database without waiting for a response</summary>
/// <param name="changes" type="Object">MongoDB changes query to be used instead of differential patching</param>
/// </signature>
/// <signature>
/// <summary>Saves changes made to the current instance to the database</summary>
/// <param name="callback" value="callback(new Error(), this)">A function which is called when the save has been completed</param>
/// </signature>
/// <signature>
/// <summary>Saves changes made to the current instance to the database</summary>
/// <param name="changes" type="Object">MongoDB changes query to be used instead of differential patching</param>
/// <param name="callback" value="callback(new Error(), this)">A function which is called when the save has been completed</param>
/// </signature>
/// <signature>
/// <summary>Saves changes made to the current instance to the database</summary>
/// <param name="conditions" type="Object">A set of conditions used to determine aspects of the document to update, merged with _id: ...</param>
/// <param name="changes" type="Object">MongoDB changes query to be used instead of differential patching</param>
/// </signature>
/// <signature>
/// <summary>Saves changes made to the current instance to the database</summary>
/// <param name="conditions" type="Object">A set of conditions used to determine aspects of the document to update, merged with _id: ...</param>
/// <param name="changes" type="Object">MongoDB changes query to be used instead of differential patching</param>
/// <param name="callback" value="callback(new Error(), this)">A function which is called when the save has been completed</param>
/// </signature>

var args = Array.prototype.splice.call(arguments, 0);
conditions = null;
changes = null;
callback = null;
Instance.prototype.save = fn.first(function() {
this.deferred = Q.defer();
}).on(fn.opt(Function), function(callback) {
this.conditions = this.context.__state.model.uniqueConditions(this.context.__state.modified);

for(var i = args.length - 1; i >= 0; i--) {
if('function' == typeof args[i]) callback = args[i];
else if(!changes) changes = args[i];
else conditions = args[i];
var validation = this.context.__state.model.schemaValidator.validate(this.context.__state.modified);
if(validation.failed) {
this.aborted = true;
return this.deferred.reject(validation.error);
}

conditions = conditions || {};
var original = _.cloneDeep(this.context.__state.original);
var modified = _.cloneDeep(this.context.__state.modified);

var onError = (function (err) {
this.emit('error', err);
if(callback) return callback(err);
else throw err;
}).bind(this);

if(this.__state.isNew) {
var toCreate = _.cloneDeep(this.__state.modified);

this.__state.model.onCreating(toCreate, (function(err) {
if(err) return onError(err);
this.emit('creating', toCreate);
this.context.__state.model.toSource(original);
this.context.__state.model.toSource(modified);

this.__state.model.toSource(toCreate);
this.__state.model.collection.insert(toCreate, { w: 1 }, (function(err, created) {
if(err) return onError(err);
this.changes = Instance.diff(original, modified);

this.__state.isNew = false;
this.__state.isPartial = false;
this.__state.model.onRetrieved(conditions, created[0], callback || function() { }, (function(value) {
this.__state.model.fromSource(value);
this.__state.original = _.cloneDeep(value);
this.__state.modified = _.cloneDeep(value);
this.__extendSchema();
this.emit('retrieved', this);
return this;
}).bind(this), { partial: this.__state.isPartial });
}).bind(this));
}).bind(this));
}
if(callback) promiseCallback(this.deferred.promise, callback);
}).on(Object, fn.opt(Function), function(changes, callback) {
this.conditions = this.context.__state.model.uniqueConditions(this.context.__state.modified);
this.changes = changes;

if(!changes) {
var validation = this.__state.model.schemaValidator.validate(this.__state.modified);
if(validation.failed) return callback(validation.error);
if(callback) promiseCallback(this.deferred.promise, callback);
}).on(Object, Object, fn.opt(Function), function(conditions, changes, callback) {
this.conditions = conditions;
this.changes = changes;

var original = _.cloneDeep(this.__state.original);
var modified = _.cloneDeep(this.__state.modified);
this.context.__state.model.toSource(this.conditions);
_.merge(this.conditions, this.context.__state.model.uniqueConditions(this.context.__state.modified));

this.__state.model.toSource(original);
this.__state.model.toSource(modified);
if(callback) promiseCallback(this.deferred.promise, callback);
}).then(function() {
if(this.aborted) return this.deferred.promise;

changes = Instance.diff(original, modified);
if(Object.keys(this.changes).length == 0) {
this.deferred.resolve(this.context);
return this.deferred.promise;
}

if(Object.keys(changes).length === 0) return (callback || function() { })(null, this);

this.__state.model.onSaving(this, changes, (function(err) {
if(err) return onError(err);
this.emit('saving', this, changes);

this.__state.model.toSource(conditions);
_.merge(conditions, this.__state.model.uniqueConditions(this.__state.modified));
var insert = Q.nbind(this.context.__state.model.collection.insert, this.context.__state.model.collection);
var update = Q.nbind(this.context.__state.model.collection.update, this.context.__state.model.collection);
var findOne = Q.nbind(this.context.__state.model.collection.findOne, this.context.__state.model.collection);

if(this.context.__state.isNew) {
var toCreate = _.cloneDeep(this.context.__state.modified);

promisePipe(this.context.__state.model.onCreating(toCreate).then((function(toCreate) {
this.context.emit('creating', toCreate);

this.context.__state.model.toSource(toCreate);
return insert(toCreate, { w: 1 });
}).bind(this)).then((function(created) {
this.context.__state.isNew = false;
this.context.__state.isPartial = false;

return this.context.__state.model.onRetrieved(this.conditions, created[0], (function(value){
this.__state.model.fromSource(value);
this.__state.original = _.cloneDeep(value);
this.__state.modified = _.cloneDeep(value);
this.__extendSchema();
this.emit('retrieved', this);
}).bind(this.context), { partial: this.context.__state.isPartial });
}).bind(this)), this.deferred);
}
else {
promisePipe(this.context.__state.model.onSaving(this.context, this.changes).then((function() {
this.context.emit('saving', this.context, this.changes);

this.__state.model.collection.update(conditions, changes, { w: 1 }, (function (err, changed) {
if (err) return onError(err);
if (!changed) return (callback || function () { })(null, this);
return update(this.conditions, this.changes, { w: 1 });
}).bind(this)).then((function(changed) {
if(!changed) return Q(this.context);

var conditions = this.__state.model.uniqueConditions(this.__state.modified);
this.__state.model.collection.findOne(conditions, (function (err, latest) {
if (err) return onError(err);
var conditions = this.context.__state.model.uniqueConditions(this.context.__state.modified);
return findOne(conditions).then((function(latest) {
if(!latest) {
this.__state.isNew = true;
return (callback || function () { })(null, this);
this.context.__state.isNew = true;
return Q(this.context);
}

this.__state.model.onRetrieved(conditions, latest, callback || function () { }, (function (value) {
return this.context.__state.model.onRetrieved(conditions, latest, (function (value) {
this.__state.model.fromSource(value);
this.__state.isPartial = false;
this.__state.original = _.cloneDeep(value);
this.__state.modified = _.cloneDeep(value);
this.__extendSchema();
this.emit('retrieved', this);
return this;
}).bind(this));
}).bind(this.context));
}).bind(this));
}).bind(this));
}).bind(this));
};
}).bind(this)).fail((function(err) {
this.emit('error', err);
return Q.reject(err);
}).bind(this.context)), this.deferred);
}

return this.deferred.promise;
}).compile();

Instance.prototype.refresh = Instance.prototype.update = function(callback) {
/// <signature>
Expand All @@ -192,23 +173,20 @@ Instance.prototype.refresh = Instance.prototype.update = function(callback) {
/// <param name="callback" value="callback(new Error(), this)">A function to be called once the update is complete</param>
/// </signature>

var onError = (function (err) {
this.emit('error', err);
if(callback) return callback(err);
else throw err;
}).bind(this);
var deferred = Q.defer();
if(callback) promiseCallback(deferred.promise, callback);

var conditions = this.__state.model.uniqueConditions(this.__state.original);
this.__state.model.collection.findOne(conditions, (function(err, latest) {
if(err) return onError(err);
var findOne = Q.nbind(this.__state.model.collection.findOne, this.__state.model.collection);

promisePipe(findOne(conditions).then((function(latest) {
if(!latest) {
this.__state.isPartial = false;
this.__state.isNew = true;
this.__state.original = _.cloneDeep(this.__state.modified);
return this;
return Q(this);
}

this.__state.model.onRetrieved(conditions, latest, callback || function() { }, (function(value) {
return this.__state.model.onRetrieved(conditions, latest, (function(value) {
this.__state.model.fromSource(value);
this.__state.isNew = false;
this.__state.isPartial = false;
Expand All @@ -218,7 +196,12 @@ Instance.prototype.refresh = Instance.prototype.update = function(callback) {
this.emit('retrieved', this);
return this;
}).bind(this));
}).bind(this));
}).bind(this)).fail((function(err) {
this.emit('error', err);
return Q.reject(err);
}).bind(this)), deferred);

return deferred.promise;
};

Instance.prototype.remove = Instance.prototype.delete = function(callback) {
Expand All @@ -230,18 +213,30 @@ Instance.prototype.remove = Instance.prototype.delete = function(callback) {
/// <param name="callback" value="callback(new Error())">A function to be called when the object has been removed</param>
/// </signature>

if(this.__state.isNew) return (callback || function() { })(null, 0);
var deferred = Q.defer();
if(callback) promiseCallback(deferred.promise, callback);

if(this.__state.isNew) {
deferred.resolve(0);
return deferred.promise;
}

var conditions = this.__state.model.uniqueConditions(this.__state.modified);
this.__state.model.cache.drop(conditions, (function() {
this.emit('removing', this);
this.__state.model.collection.remove(conditions, { w: callback ? 1 : 0 }, (function(err, removed) {
if(err) this.emit('error', err);
else this.emit('removed', this);
this.__state.model.collection.remove(conditions, { w: 1 }, (function(err, removed) {
this.__state.isNew = true;
return callback(err, removed);
if(err) {
this.emit('error', err);
return deferred.reject(err);
}

this.emit('removed', this);
return deferred.resolve(this);
}).bind(this));
}).bind(this));

return deferred.promise;
};

Instance.prototype.select = function(collection, filter) {
Expand Down Expand Up @@ -378,3 +373,22 @@ Instance.forModel = function(model) {
};

Instance.diff = diff;


/**
* PROMISES STUFF
*/

function promisePipe(promise, deferred) {
promise.then(function(result) {
deferred.resolve(result);
}, function(err) {
deferred.reject(err);
}, function(progress) {
deferred.notify(progress);
});
}

function promiseCallback(promise, callback) {
promise.then(function(result) { callback(null, result); }, function(err) { callback(err); });
}

0 comments on commit fce4b5c

Please sign in to comment.