camo
Advanced tools
Comparing version 0.4.0 to 0.5.0
@@ -0,1 +1,16 @@ | ||
## 0.5.0 (2015-06-26) | ||
Features: | ||
- Exposed `getClient()` method for retrieving the active Camo client. | ||
- Added `options` parameter to `connect()` so options can be passed to backend DB client. | ||
- Static method `Document.fromData()` is now a private helper method. Static method `.create()` should be used instead. | ||
Bugfixes: | ||
- In `Document._fromData()`, added check to see if ID exists before assigning | ||
- Changed `BaseDocument._fromData()` so it returns data in same form as it was passed. | ||
+ i.e. Array of data returned as array, single object returned as single object. | ||
- Fixed bug where assigning an array of Documents in `.create()` lost the references. | ||
- Stopped using the depracated `_.extend()` alias. Now using `_.assign()` instead. ([#1](https://github.com/scottwrobinson/camo/issues/1)). | ||
- Fixed `get` and `set` issue with Proxy ([#3](https://github.com/scottwrobinson/camo/issues/3)). | ||
## 0.4.0 (2015-06-22) | ||
@@ -2,0 +17,0 @@ |
@@ -6,3 +6,5 @@ "use strict"; | ||
exports.connect = require('./lib/db').connect; | ||
exports.getClient = require('./lib/clients').getClient; | ||
exports.Document = require('./lib/document'); | ||
exports.EmbeddedDocument = require('./lib/embedded-document'); |
@@ -33,3 +33,3 @@ "use strict"; | ||
let schemaProxyHandler = { | ||
get: function(target, propKey) { | ||
get: function(target, propKey, receiver) { | ||
// Return current value, if set | ||
@@ -45,6 +45,6 @@ if (propKey in target._values) { | ||
return Reflect.get(target, propKey); | ||
return Reflect.get(target, propKey, receiver); | ||
}, | ||
set: function(target, propKey, value) { | ||
set: function(target, propKey, value, receiver) { | ||
if (propKey in target._schema) { | ||
@@ -61,3 +61,3 @@ target._values[propKey] = value; | ||
return Reflect.set(target, propKey, value); | ||
return Reflect.set(target, propKey, value, receiver); | ||
}, | ||
@@ -78,6 +78,6 @@ | ||
constructor() { | ||
this._schema = { // Defines document structure/properties | ||
_id: { type: String }, // Native ID to backend database | ||
this._schema = { // Defines document structure/properties | ||
_id: { type: DB().nativeIdType() }, // Native ID to backend database | ||
}; | ||
this._values = {}; // Contains values for properties defined in schema | ||
this._values = {}; // Contains values for properties defined in schema | ||
} | ||
@@ -106,3 +106,3 @@ | ||
if (!extension) return; | ||
_.extend(this._schema, extension); | ||
_.assign(this._schema, extension); | ||
} | ||
@@ -163,3 +163,4 @@ | ||
if (value !== null && value.documentClass && value.documentClass() === 'embedded') { | ||
if (value !== null && value !== undefined && | ||
value.documentClass && value.documentClass() === 'embedded') { | ||
value.validate(); | ||
@@ -206,3 +207,10 @@ return; | ||
static create() { | ||
static create(data) { | ||
if (typeof(data) !== 'undefined') { | ||
return this._fromData(data); | ||
} | ||
return this._instantiate(); | ||
} | ||
static _instantiate() { | ||
var instance = new this(); | ||
@@ -216,3 +224,3 @@ instance.generateSchema(); | ||
// need to know about child classes | ||
static fromData(datas, populate) { | ||
static _fromData(datas) { | ||
var that = this; | ||
@@ -224,10 +232,6 @@ | ||
if (typeof(populate) === 'undefined') { | ||
populate = true; | ||
} | ||
var documents = []; | ||
var embeddedPromises = []; | ||
datas.forEach(function(d) { | ||
var instance = that.create(); | ||
var instance = that._instantiate(); | ||
_.keys(d).forEach(function(key) { | ||
@@ -246,14 +250,4 @@ | ||
// TODO: We should dereference in 'fromData'. This way | ||
// we only need to check if its a document (not even a | ||
// Base/Embedded doc) and call 'fromData' on that. | ||
if (type.documentClass && type.documentClass() === 'embedded') { | ||
// For each EmbeddedDocument, save a promise that | ||
// will later populate its data (done right before | ||
// we return from this function) | ||
var p = Promise.resolve(type.fromData(value, populate)) | ||
.then(function(embedded) { | ||
instance._values[key] = embedded[0]; | ||
}); | ||
embeddedPromises.push(p); | ||
instance._values[key] = type._fromData(value); | ||
} else { | ||
@@ -267,17 +261,16 @@ instance._values[key] = value; | ||
}); | ||
// Populate references or just return docs | ||
return Promise.all(embeddedPromises).then(function() { | ||
if (populate) { | ||
return that.dereferenceDocuments(documents).then(function(docs) { | ||
return docs; | ||
}); | ||
} | ||
return Promise.all(documents); | ||
}); | ||
if (documents.length === 1) { | ||
return documents[0]; | ||
} | ||
return documents; | ||
} | ||
populate() { | ||
return BaseDocument.populate(this); | ||
} | ||
// TODO : EMBEDDED | ||
// | ||
static dereferenceDocuments(docs) { | ||
static populate(docs) { | ||
if (!docs) return Promise.all([]); | ||
@@ -284,0 +277,0 @@ |
@@ -36,3 +36,3 @@ "use strict"; | ||
static connect(url) { | ||
static connect(url, options) { | ||
throw new TypeError('You must override connect (static).'); | ||
@@ -61,2 +61,6 @@ } | ||
nativeIdType() { | ||
throw new TypeError('You must override nativeIdType.'); | ||
} | ||
driver() { | ||
@@ -63,0 +67,0 @@ throw new TypeError('You must override driver.'); |
"use strict"; | ||
var _ = require('lodash'); | ||
var path = require('path'); | ||
var fs = require('fs'); | ||
var _ = require('lodash'); | ||
var MDBClient = require('mongodb').MongoClient; | ||
@@ -111,5 +111,8 @@ var ObjectId = require('mongodb').ObjectId; | ||
static connect(url) { | ||
static connect(url, options) { | ||
if (typeof(options) === 'undefined') { | ||
options = { }; | ||
} | ||
return new Promise(function(resolve, reject) { | ||
MDBClient.connect(url, function(error, client) { | ||
MDBClient.connect(url, options, function(error, client) { | ||
if (error) return reject(error); | ||
@@ -159,2 +162,6 @@ return resolve(new MongoClient(url, client)); | ||
nativeIdType() { | ||
return ObjectId; | ||
} | ||
driver() { | ||
@@ -161,0 +168,0 @@ return this._mongo; |
"use strict"; | ||
var _ = require('lodash'); | ||
var path = require('path'); | ||
var fs = require('fs'); | ||
var _ = require('lodash'); | ||
var Datastore = require('nedb'); | ||
@@ -138,3 +138,3 @@ var DatabaseClient = require('./client'); | ||
static connect(url) { | ||
static connect(url, options) { | ||
var dbFolderPath = urlToPath(url); | ||
@@ -209,2 +209,6 @@ | ||
nativeIdType() { | ||
return String; | ||
} | ||
driver() { | ||
@@ -211,0 +215,0 @@ return this._collections; |
var NeDbClient = require('./clients/nedbclient'); | ||
var MongoClient = require('./clients/mongoclient'); | ||
exports.connect = function(url) { | ||
exports.connect = function(url, options) { | ||
if (url.indexOf('nedb://') > -1) { | ||
// url example: nedb://path/to/file/folder | ||
return NeDbClient.connect(url).then(function(db) { | ||
return NeDbClient.connect(url, options).then(function(db) { | ||
global.CLIENT = db; | ||
@@ -13,3 +13,3 @@ return db; | ||
// url example: 'mongodb://localhost:27017/myproject' | ||
return MongoClient.connect(url).then(function(db) { | ||
return MongoClient.connect(url, options).then(function(db) { | ||
global.CLIENT = db; | ||
@@ -16,0 +16,0 @@ return db; |
@@ -219,6 +219,11 @@ "use strict"; | ||
return that.fromData(data, populate); | ||
var doc = that._fromData(data); | ||
if (populate) { | ||
return that.populate(doc); | ||
} | ||
return doc; | ||
}).then(function(docs) { | ||
if (docs && docs.length > 0) { | ||
return docs[0]; | ||
if (docs) { | ||
return docs; | ||
} | ||
@@ -240,5 +245,10 @@ return null; | ||
.then(function(datas) { | ||
return that.fromData(datas, populate); | ||
var docs = that._fromData(datas); | ||
if (populate) { | ||
return that.populate(docs); | ||
} | ||
return docs; | ||
}).then(function(docs) { | ||
return docs; | ||
// Ensure we always return an array | ||
return [].concat(docs); | ||
}); | ||
@@ -252,13 +262,19 @@ } | ||
static fromData(datas, populate) { | ||
if (!isArray(datas)) { | ||
datas = [datas]; | ||
static _fromData(datas) { | ||
var instances = super._fromData(datas); | ||
// This way we preserve the original structure of the data. Data | ||
// that was passed as an array is returned as an array, and data | ||
// passes as a single object is returned as single object | ||
var datasArray = [].concat(datas); | ||
var instancesArray = [].concat(instances); | ||
for (var i = 0; i < instancesArray.length; i++) { | ||
if (datasArray[i].hasOwnProperty('_id')) { | ||
instancesArray[i].id = datasArray[i]._id; | ||
} else { | ||
instancesArray[i].id = null; | ||
} | ||
} | ||
return super.fromData(datas, populate).then(function(instances) { | ||
for (var i = 0; i < instances.length; i++) { | ||
instances[i].id = datas[i]._id; | ||
} | ||
return instances; | ||
}); | ||
return instances; | ||
} | ||
@@ -265,0 +281,0 @@ |
var _ = require('lodash'); | ||
var ObjectId = null; | ||
try { | ||
ObjectId = require('mongodb').ObjectId; | ||
} catch(Error) { } | ||
var isString = function(s) { | ||
@@ -39,10 +44,18 @@ return _.isString(s); | ||
var isObjectId = function(o) { | ||
if (ObjectId === null) { | ||
return false; | ||
} | ||
return String(o).match(/^[a-fA-F0-9]{24}$/); | ||
}; | ||
var isSupportedType = function(t) { | ||
return (t === String || t === Number || t === Boolean || | ||
t === Buffer || t === Date || t === Array || | ||
isArray(t) || t === Object || typeof(t.documentClass) === 'function'); | ||
isArray(t) || t === Object || t instanceof Object || | ||
typeof(t.documentClass) === 'function'); | ||
}; | ||
var isType = function(value, type) { | ||
if (type === String) { | ||
if (type === String) { | ||
return isString(value); | ||
@@ -65,2 +78,4 @@ } else if (type === Number) { | ||
return isEmbeddedDocument(value); | ||
} else if (type === ObjectId) { | ||
return isObjectId(value); | ||
} else { | ||
@@ -67,0 +82,0 @@ throw new Error('Unsupported type: ' + type.name); |
{ | ||
"name": "camo", | ||
"version": "0.4.0", | ||
"version": "0.5.0", | ||
"description": "A lightweight ES6 ODM for Mongo-like databases.", | ||
@@ -5,0 +5,0 @@ "author": { |
@@ -208,5 +208,6 @@ # Camo | ||
```javascript | ||
var lassie = Dog.create(); | ||
lassie.name = 'Lassie'; | ||
lassie.breed = 'Collie'; | ||
var lassie = Dog.create({ | ||
name: 'Lassie', | ||
breed: 'Collie' | ||
}); | ||
@@ -213,0 +214,0 @@ lassie.save().then(function(l) { |
@@ -42,2 +42,185 @@ "use strict"; | ||
describe('instantiation', function() { | ||
it('should allow creation of instance', function(done) { | ||
class User extends Document { | ||
constructor() { | ||
super('user'); | ||
this.firstName = String; | ||
this.lastName = String; | ||
} | ||
} | ||
var user = User.create(); | ||
user.firstName = 'Billy'; | ||
user.lastName = 'Bob'; | ||
user.save().then(function() { | ||
validateId(user); | ||
}).then(done, done); | ||
}); | ||
it('should allow creation of instance with data', function(done) { | ||
class User extends Document { | ||
constructor() { | ||
super('user'); | ||
this.firstName = String; | ||
this.lastName = String; | ||
this.nicknames = [String]; | ||
} | ||
} | ||
var user = User.create({ | ||
firstName: 'Billy', | ||
lastName: 'Bob', | ||
nicknames: ['Bill', 'William', 'Will'] | ||
}); | ||
expect(user.firstName).to.be.equal('Billy'); | ||
expect(user.lastName).to.be.equal('Bob'); | ||
expect(user.nicknames).to.have.length(3); | ||
expect(user.nicknames).to.include('Bill'); | ||
expect(user.nicknames).to.include('William'); | ||
expect(user.nicknames).to.include('Will'); | ||
done(); | ||
}); | ||
it('should allow creation of instance with references', function(done) { | ||
class Coffee extends Document { | ||
constructor() { | ||
super('coffee'); | ||
this.temp = Number; | ||
} | ||
} | ||
class User extends Document { | ||
constructor() { | ||
super('user'); | ||
this.drinks = [Coffee]; | ||
} | ||
} | ||
var coffee = Coffee.create(); | ||
coffee.temp = 105; | ||
coffee.save().then(function() { | ||
var user = User.create({ drinks: [coffee] }); | ||
expect(user.drinks).to.have.length(1); | ||
}).then(done, done); | ||
}); | ||
}); | ||
describe('class', function() { | ||
it('should allow use of member variables in getters', function(done) { | ||
class User extends Document { | ||
constructor() { | ||
super('user'); | ||
this.firstName = String; | ||
this.lastName = String; | ||
} | ||
get fullName() { | ||
return this.firstName + ' ' + this.lastName; | ||
} | ||
} | ||
var user = User.create(); | ||
user.firstName = 'Billy'; | ||
user.lastName = 'Bob'; | ||
user.save().then(function() { | ||
validateId(user); | ||
expect(user.fullName).to.be.equal('Billy Bob'); | ||
}).then(done, done); | ||
}); | ||
it('should allow use of member variables in setters', function(done) { | ||
class User extends Document { | ||
constructor() { | ||
super('user'); | ||
this.firstName = String; | ||
this.lastName = String; | ||
} | ||
get fullName() { | ||
return this.firstName + ' ' + this.lastName; | ||
} | ||
set fullName(name) { | ||
var nameArr = name.split(' '); | ||
this.firstName = nameArr[0]; | ||
this.lastName = nameArr[1]; | ||
} | ||
} | ||
var user = User.create(); | ||
user.fullName = 'Billy Bob'; | ||
user.save().then(function() { | ||
validateId(user); | ||
expect(user.firstName).to.be.equal('Billy'); | ||
expect(user.lastName).to.be.equal('Bob'); | ||
}).then(done, done); | ||
}); | ||
it('should allow use of member variables in methods', function(done) { | ||
class User extends Document { | ||
constructor() { | ||
super('user'); | ||
this.firstName = String; | ||
this.lastName = String; | ||
} | ||
fullName() { | ||
return this.firstName + ' ' + this.lastName; | ||
} | ||
} | ||
var user = User.create(); | ||
user.firstName = 'Billy'; | ||
user.lastName = 'Bob'; | ||
user.save().then(function() { | ||
validateId(user); | ||
expect(user.fullName()).to.be.equal('Billy Bob'); | ||
}).then(done, done); | ||
}); | ||
it('should allow schemas to be extended', function(done) { | ||
class User extends Document { | ||
constructor(collection) { | ||
super(collection); | ||
this.firstName = String; | ||
this.lastName = String; | ||
} | ||
} | ||
class ProUser extends User { | ||
constructor() { | ||
super('prouser'); | ||
this.paymentMethod = String; | ||
} | ||
} | ||
var user = ProUser.create(); | ||
user.firstName = 'Billy'; | ||
user.lastName = 'Bob'; | ||
user.paymentMethod = 'cash'; | ||
user.save().then(function() { | ||
validateId(user); | ||
expect(user.firstName).to.be.equal('Billy'); | ||
expect(user.lastName).to.be.equal('Bob'); | ||
expect(user.paymentMethod).to.be.equal('cash'); | ||
}).then(done, done); | ||
}); | ||
}); | ||
describe('types', function() { | ||
@@ -44,0 +227,0 @@ it('should allow reference types', function(done) { |
177722
25
4257
316
6