Merge branch 'release/2.2.0' into production

This commit is contained in:
Raymond Feng 2014-07-30 16:27:39 -07:00
commit d334f42ccf
12 changed files with 2585 additions and 93 deletions

View File

@ -1,6 +1,7 @@
exports.ModelBuilder = exports.LDL = require('./lib/model-builder.js').ModelBuilder;
exports.DataSource = exports.Schema = require('./lib/datasource.js').DataSource;
exports.ModelBaseClass = require('./lib/model.js');
exports.GeoPoint = require('./lib/geo.js').GeoPoint;
exports.ValidationError = require('./lib/validations.js').ValidationError;
exports.__defineGetter__('version', function () {

View File

@ -17,6 +17,7 @@ exports.initialize = function initializeDataSource(dataSource, callback) {
};
exports.Memory = Memory;
exports.applyFilter = applyFilter;
function Memory(m, settings) {
if (m instanceof Memory) {

View File

@ -14,6 +14,7 @@ var Relation = require('./relations.js');
var Inclusion = require('./include.js');
var List = require('./list.js');
var geo = require('./geo');
var mergeQuery = require('./scope.js').mergeQuery;
var Memory = require('./connectors/memory').Memory;
var utils = require('./utils');
var fieldsToArray = utils.fieldsToArray;
@ -324,6 +325,47 @@ DataAccessObject.findById = function find(id, cb) {
}.bind(this));
};
DataAccessObject.findByIds = function(ids, cond, cb) {
if (typeof cond === 'function') {
cb = cond;
cond = {};
}
var pk = this.dataSource.idName(this.modelName) || 'id';
if (ids.length === 0) {
process.nextTick(function() { cb(null, []); });
return;
}
var filter = { where: {} };
filter.where[pk] = { inq: ids };
mergeQuery(filter, cond || {});
this.find(filter, function(err, results) {
cb(err, err ? results : this.sortByIds(ids, results));
}.bind(this));
};
DataAccessObject.sortByIds = function(ids, results) {
var pk = this.dataSource.idName(this.modelName) || 'id';
ids = ids.map(function(id) {
return (typeof id === 'object') ? id.toString() : id;
});
results.sort(function(x, y) {
var idA = (typeof x[pk] === 'object') ? x[pk].toString() : x[pk];
var idB = (typeof y[pk] === 'object') ? y[pk].toString() : y[pk];
var a = ids.indexOf(idA);
var b = ids.indexOf(idB);
if (a === -1 || b === -1) return 1; // last
if (a !== b) {
if (a > b) return 1;
if (a < b) return -1;
}
});
return results;
};
function convertNullToNotFoundError(ctx, cb) {
if (ctx.result !== null) return cb();
@ -1046,6 +1088,17 @@ DataAccessObject.prototype.remove =
}.bind(this));
}, null, cb);
};
/**
* Set a single attribute.
* Equivalent to `setAttributes({name: value})`
*
* @param {String} name Name of property
* @param {Mixed} value Value of property
*/
DataAccessObject.prototype.setAttribute = function setAttribute(name, value) {
this[name] = value;
};
/**
* Update a single attribute.
@ -1062,7 +1115,27 @@ DataAccessObject.prototype.updateAttribute = function updateAttribute(name, valu
};
/**
* Update saet of attributes.
* Update set of attributes.
*
* @trigger `change` hook
* @param {Object} data Data to update
*/
DataAccessObject.prototype.setAttributes = function setAttributes(data) {
if (typeof data !== 'object') return;
var Model = this.constructor;
var inst = this;
// update instance's properties
for (var key in data) {
inst.setAttribute(key, data[key]);
}
Model.emit('set', inst);
};
/**
* Update set of attributes.
* Performs validation before updating.
*
* @trigger `validation`, `save` and `update` hooks
@ -1086,9 +1159,7 @@ DataAccessObject.prototype.updateAttributes = function updateAttributes(data, cb
}
// update instance's properties
for (var key in data) {
inst[key] = data[key];
}
inst.setAttributes(data);
inst.isValid(function (valid) {
if (!valid) {

View File

@ -12,6 +12,7 @@ var EventEmitter = require('events').EventEmitter;
var util = require('util');
var assert = require('assert');
var async = require('async');
var traverse = require('traverse');
if (process.env.DEBUG === 'loopback') {
// For back-compatibility
@ -396,12 +397,9 @@ DataSource.prototype.defineRelations = function (modelClass, relations) {
// Check if the through model doesn't exist or resolved
if (!throughModel || isModelDataSourceAttached(throughModel)) {
// The target model is resolved
var params = {
foreignKey: relation.foreignKey,
as: name,
model: model,
options: relation.options
};
var params = traverse(relation).clone();
params.as = name;
params.model = model;
if (throughModel) {
params.through = throughModel;
}
@ -415,13 +413,10 @@ DataSource.prototype.defineRelations = function (modelClass, relations) {
throughModel.once('dataSourceAttached', function (model) {
if (isModelDataSourceAttached(targetModel)) {
// The target model is resolved
var params = {
foreignKey: relation.foreignKey,
as: name,
model: targetModel,
through: model,
options: relation.options
};
var params = traverse(relations).clone();
params.as = name;
params.model = targetModel;
params.through = model;
modelClass[relation.type].call(modelClass, name, params);
}
});
@ -438,17 +433,14 @@ DataSource.prototype.defineRelations = function (modelClass, relations) {
if (r.through) {
throughModel = isModelClass(r.through) ? r.through : this.getModel(r.through, true);
}
if (!isModelDataSourceAttached(targetModel) || (throughModel && !isModelDataSourceAttached(throughModel))) {
if ((!r.polymorphic && !isModelDataSourceAttached(targetModel)) || (throughModel && !isModelDataSourceAttached(throughModel))) {
// Create a listener to defer the relation set up
createListener(rn, r, targetModel, throughModel);
} else {
// The target model is resolved
var params = {
foreignKey: r.foreignKey,
as: rn,
model: targetModel,
options: r.options
};
var params = traverse(r).clone();
params.as = rn;
params.model = targetModel;
if (throughModel) {
params.through = throughModel;
}

View File

@ -94,7 +94,7 @@ Inclusion.include = function (objects, include, cb) {
subInclude = null;
}
var relation = relations[relationName];
if (!relation) {
cb(new Error('Relation "' + relationName + '" is not defined for '
+ self.modelName + ' model'));
@ -106,7 +106,7 @@ Inclusion.include = function (objects, include, cb) {
cb();
return;
}
// Calling the relation method for each object
async.each(objs, function (obj, callback) {
if(relation.type === 'belongsTo') {
@ -133,11 +133,11 @@ Inclusion.include = function (objects, include, cb) {
obj.__cachedRelations[relationName] = result;
if(obj === inst) {
obj.__data[relationName] = result;
obj.strict = false;
obj.setStrict(false);
} else {
obj[relationName] = result;
}
if (subInclude && result) {
var subItems = relation.multiple ? result : [result];
// Recursively include the related models

View File

@ -161,6 +161,9 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
};
// mix in EventEmitter (don't inherit from)
var events = new EventEmitter();
// The model can have more than 10 listeners for lazy relationship setup
// See https://github.com/strongloop/loopback/issues/404
events.setMaxListeners(32);
for (var f in EventEmitter.prototype) {
if (typeof EventEmitter.prototype[f] === 'function') {
ModelClass[f] = EventEmitter.prototype[f].bind(events);

File diff suppressed because it is too large Load Diff

View File

@ -161,3 +161,11 @@ RelationMixin.hasAndBelongsToMany = function hasAndBelongsToMany(modelTo, params
RelationMixin.hasOne = function hasMany(modelTo, params) {
RelationDefinition.hasOne(this, modelTo, params);
};
RelationMixin.referencesMany = function hasMany(modelTo, params) {
RelationDefinition.referencesMany(this, modelTo, params);
};
RelationMixin.embedsMany = function hasMany(modelTo, params) {
RelationDefinition.embedsMany(this, modelTo, params);
};

View File

@ -18,7 +18,7 @@ function ScopeDefinition(definition) {
ScopeDefinition.prototype.related = function(receiver, scopeParams, condOrRefresh, cb) {
var name = this.name;
var self = receiver;
var actualCond = {};
var actualRefresh = false;
var saveOnCache = true;
@ -224,6 +224,8 @@ function defineScope(cls, targetClass, name, params, methods) {
var where = (this._scope && this._scope.where) || {};
targetClass.destroyAll(where, cb);
}
return definition;
}
/*!

View File

@ -1,6 +1,6 @@
{
"name": "loopback-datasource-juggler",
"version": "2.1.1",
"version": "2.2.0",
"description": "LoopBack DataSoure Juggler",
"keywords": [
"StrongLoop",

View File

@ -50,6 +50,45 @@ describe('basic-querying', function () {
});
});
describe('findById', function () {
before(function(done) {
var people = [
{ id: 1, name: 'a', vip: true },
{ id: 2, name: 'b' },
{ id: 3, name: 'c' },
{ id: 4, name: 'd', vip: true },
{ id: 5, name: 'e' },
{ id: 6, name: 'f' }
];
User.destroyAll(function() {
User.create(people, done);
});
});
it('should query by ids', function (done) {
User.findByIds([3, 2, 1], function (err, users) {
should.exist(users);
should.not.exist(err);
var names = users.map(function(u) { return u.name; });
names.should.eql(['c', 'b', 'a']);
done();
});
});
it('should query by ids and condition', function (done) {
User.findByIds([4, 3, 2, 1],
{ where: { vip: true } }, function (err, users) {
should.exist(users);
should.not.exist(err);
var names = users.map(function(u) { return u.name; });
names.should.eql(['d', 'a']);
done();
});
});
});
describe('find', function () {

File diff suppressed because it is too large Load Diff