Comparing version 0.9.1 to 0.10.0
@@ -0,1 +1,9 @@ | ||
## 0.10.0 (2015-11-12) | ||
Features: | ||
- Added support for setting the 'unique' index on a field | ||
- Added support for specifying which reference fields should be populated in `loadOne()` and `loadMany()` | ||
Bugfixes: | ||
- Fixed issue in `isNativeId` where we weren't returning a proper Boolean. | ||
## 0.9.1 (2015-11-06) | ||
@@ -2,0 +10,0 @@ |
@@ -239,8 +239,13 @@ "use strict"; | ||
static create(data) { | ||
this.createIndexes(); | ||
if (typeof(data) !== 'undefined') { | ||
return this._fromData(data); | ||
} | ||
return this._instantiate(); | ||
} | ||
static createIndexes() { } | ||
static _instantiate() { | ||
@@ -314,3 +319,3 @@ var instance = new this(); | ||
// | ||
static populate(docs) { | ||
static populate(docs, fields) { | ||
if (!docs) return Promise.all([]); | ||
@@ -337,2 +342,7 @@ | ||
_.keys(anInstance._schema).forEach(function(key) { | ||
// Only populate specified fields | ||
if (isArray(fields) && fields.indexOf(key) < 0) { | ||
return; | ||
} | ||
// Handle array of references (ex: { type: [MyObject] }) | ||
@@ -339,0 +349,0 @@ if (isArray(anInstance._schema[key].type) && |
@@ -44,2 +44,6 @@ "use strict"; | ||
createIndex(collection, field, options) { | ||
throw new TypeError('You must override createIndex.'); | ||
} | ||
static connect(url, options) { | ||
@@ -46,0 +50,0 @@ throw new TypeError('You must override connect (static).'); |
@@ -179,2 +179,14 @@ "use strict"; | ||
createIndex(collection, field, options) { | ||
options = options || {}; | ||
options.unique = options.unique || false; | ||
options.sparse = options.sparse || false; | ||
var db = this._mongo.collection(collection); | ||
var keys = {}; | ||
keys[field] = 1; | ||
db.createIndex(keys, {unique: options.unique, sparse: options.sparse}); | ||
} | ||
static connect(url, options) { | ||
@@ -227,3 +239,3 @@ if (typeof(options) === 'undefined') { | ||
isNativeId(value) { | ||
return value instanceof ObjectId || String(value).match(/^[a-fA-F0-9]{24}$/); | ||
return value instanceof ObjectId || String(value).match(/^[a-fA-F0-9]{24}$/) !== null; | ||
} | ||
@@ -230,0 +242,0 @@ |
@@ -222,2 +222,11 @@ "use strict"; | ||
createIndex(collection, field, options) { | ||
options = options || {}; | ||
options.unique = options.unique || false; | ||
options.sparse = options.sparse || false; | ||
var db = getCollection(collection, this._collections, this._path); | ||
db.ensureIndex({fieldName: field, unique: options.unique, sparse: options.sparse}); | ||
} | ||
static connect(url, options) { | ||
@@ -297,3 +306,3 @@ // Could be directory path or 'memory' | ||
isNativeId(value) { | ||
return String(value).match(/^[a-zA-Z0-9]{16}$/); | ||
return String(value).match(/^[a-zA-Z0-9]{16}$/) !== null; | ||
} | ||
@@ -300,0 +309,0 @@ |
@@ -238,4 +238,4 @@ "use strict"; | ||
var doc = that._fromData(data); | ||
if (populate) { | ||
return that.populate(doc); | ||
if (populate === true || (isArray(populate) && populate.length > 0)) { | ||
return that.populate(doc, populate); | ||
} | ||
@@ -318,5 +318,8 @@ | ||
var docs = that._fromData(datas); | ||
if (options.populate) { | ||
return that.populate(docs); | ||
if (options.populate === true || | ||
(isArray(options.populate) && options.populate.length > 0)) { | ||
return that.populate(docs, options.populate); | ||
} | ||
return docs; | ||
@@ -334,2 +337,19 @@ }).then(function(docs) { | ||
static createIndexes() { | ||
if (this._indexesCreated) { | ||
return; | ||
} | ||
var that = this; | ||
var instance = this._instantiate(); | ||
_.keys(instance._schema).forEach(function(k) { | ||
if (instance._schema[k].unique) { | ||
DB().createIndex(that.collectionName(), k, {unique: true}); | ||
} | ||
}); | ||
this._indexesCreated = true; | ||
} | ||
static _fromData(datas) { | ||
@@ -336,0 +356,0 @@ var instances = super._fromData(datas); |
{ | ||
"name": "camo", | ||
"version": "0.9.1", | ||
"version": "0.10.0", | ||
"description": "A class-based ES6 ODM for Mongo-like databases.", | ||
@@ -42,3 +42,3 @@ "author": { | ||
"optionalDependencies": { | ||
"mongodb": "2.0.33", | ||
"mongodb": "2.0.42", | ||
"nedb": "1.1.2" | ||
@@ -45,0 +45,0 @@ }, |
@@ -150,3 +150,4 @@ # Camo | ||
max: 25, | ||
choices: [2, 3, 5, 7, 11, 13, 17, 19, 23] | ||
choices: [2, 3, 5, 7, 11, 13, 17, 19, 23], | ||
unique: true | ||
} | ||
@@ -164,2 +165,3 @@ ``` | ||
- `validate`: A 1-argument function that returns `false` if the value is invalid *(optional)* | ||
- `unique`: A boolean value indicating if a 'unique' index should be set *(optional)* | ||
@@ -265,4 +267,13 @@ To reference another document, just use its class name as the type. | ||
With the `.loadOne()` method you can now pass it options to modify the returned results: | ||
`.loadOne()` currently accepts the following option: | ||
- `populate`: Boolean value to load all or no references. Pass an array of field names to only populate the specified references | ||
- `Person.loadOne({name: 'Billy'}, {populate: true})` populates all references in `Person` object | ||
- `Person.loadOne({name: 'Billy'}, {populate: ['address', 'spouse']})` populates only 'address' and 'spouse' in `Person` object | ||
`.loadMany()` currently accepts the following options: | ||
- `populate`: Boolean value to load all or no references. Pass an array of field names to only populate the specified references | ||
- `Person.loadMany({lastName: 'Smith'}, {populate: true})` populates all references in `Person` object | ||
- `Person.loadMany({lastName: 'Smith'}, {populate: ['address', 'spouse']})` populates only 'address' and 'spouse' in `Person` object | ||
- `sort`: Sort the documents by the given field | ||
@@ -269,0 +280,0 @@ - `Person.loadMany({}, {sort: '-age'})` sorts by age in descending order |
@@ -6,2 +6,3 @@ "use strict"; | ||
var connect = require('../index').connect; | ||
var Document = require('../index').Document; | ||
var Data = require('./data'); | ||
@@ -13,2 +14,3 @@ var getData1 = require('./util').data1; | ||
var validateId = require('./util').validateId; | ||
var isNativeId = require('../lib/validate').isNativeId; | ||
@@ -54,2 +56,36 @@ describe('Client', function() { | ||
class Address extends Document { | ||
constructor() { | ||
super('address'); | ||
this.street = String; | ||
this.city = String; | ||
this.zipCode = Number; | ||
} | ||
} | ||
class Pet extends Document { | ||
constructor() { | ||
super('pet'); | ||
this.schema({ | ||
type: String, | ||
name: String, | ||
}); | ||
} | ||
} | ||
class User extends Document { | ||
constructor() { | ||
super('user'); | ||
this.schema({ | ||
firstName: String, | ||
lastName: String, | ||
pet: Pet, | ||
address: Address | ||
}); | ||
} | ||
} | ||
describe('#loadOne()', function() { | ||
@@ -68,2 +104,98 @@ it('should load a single object from the collection', function(done) { | ||
}); | ||
it('should populate all fields', function(done) { | ||
var address = Address.create({ | ||
street: '123 Fake St.', | ||
city: 'Cityville', | ||
zipCode: 12345 | ||
}); | ||
var dog = Pet.create({ | ||
type: 'dog', | ||
name: 'Fido', | ||
}); | ||
var user = User.create({ | ||
firstName: 'Billy', | ||
lastName: 'Bob', | ||
pet: dog, | ||
address: address | ||
}); | ||
Promise.all([address.save(), dog.save()]).then(function() { | ||
validateId(address); | ||
validateId(dog); | ||
return user.save(); | ||
}).then(function() { | ||
validateId(user); | ||
return User.loadOne({_id: user.id}, {populate: true}); | ||
}).then(function(u) { | ||
expect(u.pet).to.be.an.instanceof(Pet); | ||
expect(u.address).to.be.an.instanceof(Address); | ||
}).then(done, done); | ||
}); | ||
it('should not populate any fields', function(done) { | ||
var address = Address.create({ | ||
street: '123 Fake St.', | ||
city: 'Cityville', | ||
zipCode: 12345 | ||
}); | ||
var dog = Pet.create({ | ||
type: 'dog', | ||
name: 'Fido', | ||
}); | ||
var user = User.create({ | ||
firstName: 'Billy', | ||
lastName: 'Bob', | ||
pet: dog, | ||
address: address | ||
}); | ||
Promise.all([address.save(), dog.save()]).then(function() { | ||
validateId(address); | ||
validateId(dog); | ||
return user.save(); | ||
}).then(function() { | ||
validateId(user); | ||
return User.loadOne({_id: user.id}, {populate: false}); | ||
}).then(function(u) { | ||
expect(isNativeId(u.pet)).to.be.true; | ||
expect(isNativeId(u.address)).to.be.true; | ||
}).then(done, done); | ||
}); | ||
it('should populate specified fields', function(done) { | ||
var address = Address.create({ | ||
street: '123 Fake St.', | ||
city: 'Cityville', | ||
zipCode: 12345 | ||
}); | ||
var dog = Pet.create({ | ||
type: 'dog', | ||
name: 'Fido', | ||
}); | ||
var user = User.create({ | ||
firstName: 'Billy', | ||
lastName: 'Bob', | ||
pet: dog, | ||
address: address | ||
}); | ||
Promise.all([address.save(), dog.save()]).then(function() { | ||
validateId(address); | ||
validateId(dog); | ||
return user.save(); | ||
}).then(function() { | ||
validateId(user); | ||
return User.loadOne({_id: user.id}, {populate: ['pet']}); | ||
}).then(function(u) { | ||
expect(u.pet).to.be.an.instanceof(Pet); | ||
expect(isNativeId(u.address)).to.be.true; | ||
}).then(done, done); | ||
}); | ||
}); | ||
@@ -228,2 +360,128 @@ | ||
}); | ||
it('should populate all fields', function(done) { | ||
var address = Address.create({ | ||
street: '123 Fake St.', | ||
city: 'Cityville', | ||
zipCode: 12345 | ||
}); | ||
var dog = Pet.create({ | ||
type: 'dog', | ||
name: 'Fido', | ||
}); | ||
var user1 = User.create({ | ||
firstName: 'Billy', | ||
lastName: 'Bob', | ||
pet: dog, | ||
address: address | ||
}); | ||
var user2 = User.create({ | ||
firstName: 'Sally', | ||
lastName: 'Bob', | ||
pet: dog, | ||
address: address | ||
}); | ||
Promise.all([address.save(), dog.save()]).then(function() { | ||
validateId(address); | ||
validateId(dog); | ||
return Promise.all([user1.save(), user2.save()]); | ||
}).then(function() { | ||
validateId(user1); | ||
validateId(user2); | ||
return User.loadMany({}, {populate: true}); | ||
}).then(function(users) { | ||
expect(users[0].pet).to.be.an.instanceof(Pet); | ||
expect(users[0].address).to.be.an.instanceof(Address); | ||
expect(users[1].pet).to.be.an.instanceof(Pet); | ||
expect(users[1].address).to.be.an.instanceof(Address); | ||
}).then(done, done); | ||
}); | ||
it('should not populate any fields', function(done) { | ||
var address = Address.create({ | ||
street: '123 Fake St.', | ||
city: 'Cityville', | ||
zipCode: 12345 | ||
}); | ||
var dog = Pet.create({ | ||
type: 'dog', | ||
name: 'Fido', | ||
}); | ||
var user1 = User.create({ | ||
firstName: 'Billy', | ||
lastName: 'Bob', | ||
pet: dog, | ||
address: address | ||
}); | ||
var user2 = User.create({ | ||
firstName: 'Sally', | ||
lastName: 'Bob', | ||
pet: dog, | ||
address: address | ||
}); | ||
Promise.all([address.save(), dog.save()]).then(function() { | ||
validateId(address); | ||
validateId(dog); | ||
return Promise.all([user1.save(), user2.save()]); | ||
}).then(function() { | ||
validateId(user1); | ||
validateId(user2); | ||
return User.loadMany({}, {populate: false}); | ||
}).then(function(users) { | ||
expect(isNativeId(users[0].pet)).to.be.true; | ||
expect(isNativeId(users[0].address)).to.be.true; | ||
expect(isNativeId(users[1].pet)).to.be.true; | ||
expect(isNativeId(users[1].address)).to.be.true; | ||
}).then(done, done); | ||
}); | ||
it('should populate specified fields', function(done) { | ||
var address = Address.create({ | ||
street: '123 Fake St.', | ||
city: 'Cityville', | ||
zipCode: 12345 | ||
}); | ||
var dog = Pet.create({ | ||
type: 'dog', | ||
name: 'Fido', | ||
}); | ||
var user1 = User.create({ | ||
firstName: 'Billy', | ||
lastName: 'Bob', | ||
pet: dog, | ||
address: address | ||
}); | ||
var user2 = User.create({ | ||
firstName: 'Sally', | ||
lastName: 'Bob', | ||
pet: dog, | ||
address: address | ||
}); | ||
Promise.all([address.save(), dog.save()]).then(function() { | ||
validateId(address); | ||
validateId(dog); | ||
return Promise.all([user1.save(), user2.save()]); | ||
}).then(function() { | ||
validateId(user1); | ||
validateId(user2); | ||
return User.loadMany({}, {populate: ['pet']}); | ||
}).then(function(users) { | ||
expect(users[0].pet).to.be.an.instanceof(Pet); | ||
expect(isNativeId(users[0].address)).to.be.true; | ||
expect(users[1].pet).to.be.an.instanceof(Pet); | ||
expect(isNativeId(users[1].address)).to.be.true; | ||
}).then(done, done); | ||
}); | ||
}); | ||
@@ -230,0 +488,0 @@ |
@@ -148,2 +148,65 @@ "use strict"; | ||
}); | ||
describe('indexes', function() { | ||
it('should reject documents with duplicate values in unique-indexed fields', function(done) { | ||
class User extends Document { | ||
constructor() { | ||
super('user'); | ||
this.schema({ | ||
name: String, | ||
email: { | ||
type: String, | ||
unique: true | ||
} | ||
}); | ||
} | ||
} | ||
var user1 = User.create(); | ||
user1.name = 'Bill'; | ||
user1.email = 'billy@example.com'; | ||
var user2 = User.create(); | ||
user1.name = 'Billy'; | ||
user2.email = 'billy@example.com'; | ||
Promise.all([user1.save(), user2.save()]).then(function() { | ||
expect.fail(null, Error, 'Expected error, but got none.'); | ||
}).catch(function(error) { | ||
expect(error instanceof Error).to.be.true; | ||
}).then(done, done); | ||
}); | ||
it('should accept documents with duplicate values in non-unique-indexed fields', function(done) { | ||
class User extends Document { | ||
constructor() { | ||
super('user'); | ||
this.schema({ | ||
name: String, | ||
email: { | ||
type: String, | ||
unique: false | ||
} | ||
}); | ||
} | ||
} | ||
var user1 = User.create(); | ||
user1.name = 'Bill'; | ||
user1.email = 'billy@example.com'; | ||
var user2 = User.create(); | ||
user1.name = 'Billy'; | ||
user2.email = 'billy@example.com'; | ||
Promise.all([user1.save(), user2.save()]).then(function() { | ||
validateId(user1); | ||
validateId(user2); | ||
expect(user1.email).to.be.equal('billy@example.com'); | ||
expect(user2.email).to.be.equal('billy@example.com'); | ||
}).then(done, done); | ||
}); | ||
}); | ||
}); |
@@ -7,5 +7,3 @@ "use strict"; | ||
var connect = require('../index').connect; | ||
var Data = require('./data'); | ||
var getData1 = require('./util').data1; | ||
var getData2 = require('./util').data2; | ||
var Document = require('../index').Document; | ||
var validateId = require('./util').validateId; | ||
@@ -22,3 +20,3 @@ | ||
// floating around due to document.test.js? | ||
/*before(function(done) { | ||
before(function(done) { | ||
connect(url).then(function(db) { | ||
@@ -44,3 +42,3 @@ database = db; | ||
describe('#dropDatabase()', function() { | ||
/*describe('#dropDatabase()', function() { | ||
it('should drop the database and delete all its data', function(done) { | ||
@@ -98,2 +96,65 @@ | ||
});*/ | ||
describe('indexes', function() { | ||
it('should reject documents with duplicate values in unique-indexed fields', function(done) { | ||
class User extends Document { | ||
constructor() { | ||
super('user'); | ||
this.schema({ | ||
name: String, | ||
email: { | ||
type: String, | ||
unique: true | ||
} | ||
}); | ||
} | ||
} | ||
var user1 = User.create(); | ||
user1.name = 'Bill'; | ||
user1.email = 'billy@example.com'; | ||
var user2 = User.create(); | ||
user1.name = 'Billy'; | ||
user2.email = 'billy@example.com'; | ||
Promise.all([user1.save(), user2.save()]).then(function() { | ||
expect.fail(null, Error, 'Expected error, but got none.'); | ||
}).catch(function(error) { | ||
expect(error.errorType).to.be.equal('uniqueViolated'); | ||
}).then(done, done); | ||
}); | ||
it('should accept documents with duplicate values in non-unique-indexed fields', function(done) { | ||
class User extends Document { | ||
constructor() { | ||
super('user'); | ||
this.schema({ | ||
name: String, | ||
email: { | ||
type: String, | ||
unique: false | ||
} | ||
}); | ||
} | ||
} | ||
var user1 = User.create(); | ||
user1.name = 'Bill'; | ||
user1.email = 'billy@example.com'; | ||
var user2 = User.create(); | ||
user1.name = 'Billy'; | ||
user2.email = 'billy@example.com'; | ||
Promise.all([user1.save(), user2.save()]).then(function() { | ||
validateId(user1); | ||
validateId(user2); | ||
expect(user1.email).to.be.equal('billy@example.com'); | ||
expect(user2.email).to.be.equal('billy@example.com'); | ||
}).then(done, done); | ||
}); | ||
}); | ||
}); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
247415
5851
373