Comparing version 1.1.2 to 2.0.0
@@ -0,0 +0,0 @@ function Cursor (cursor, client, model, projection) { |
@@ -0,0 +0,0 @@ // like a cursor, but for single document returning queries |
@@ -0,0 +0,0 @@ NeDBClient = require('./nedb') |
@@ -0,0 +0,0 @@ const MongoDB = require('mongodb') |
@@ -0,0 +0,0 @@ const path = require('path') |
@@ -0,0 +0,0 @@ // returns a Mongo update query |
@@ -23,2 +23,6 @@ const isRawObject = require('../util').isRawObject | ||
Instance.prototype.isUpdated = function (field) { | ||
return field in this._updates | ||
} | ||
Instance.prototype.save = function () { | ||
@@ -86,6 +90,6 @@ return this._model._save(this) | ||
set (value) { | ||
const coerced = model.coerce(field, value) | ||
if (Array.isArray(value) || | ||
value instanceof Instance || | ||
isRawObject(value)) { | ||
this._updates[field] = new UpdateProxy(o, field, value) | ||
this._updates[field] = new UpdateProxy(o, field, coerced, model.schema[field]) | ||
Object.defineProperty(this._updates[field], '_replace', { | ||
@@ -97,3 +101,3 @@ value: true, | ||
} | ||
this._updates[field] = value | ||
this._updates[field] = coerced | ||
}, | ||
@@ -103,7 +107,5 @@ }) | ||
o[field] = processed[field] | ||
} | ||
else if (Array.isArray(processed[field]) || | ||
processed[field] instanceof Instance || | ||
} else if (Array.isArray(processed[field]) || | ||
isRawObject(processed[field])) { | ||
o._values[field] = new UpdateProxy(o, field, processed[field]) | ||
o._values[field] = new UpdateProxy(o, field, processed[field], model.schema[field]) | ||
} else { | ||
@@ -117,3 +119,3 @@ o._values[field] = processed[field] | ||
if (Array.isArray(reference) || reference instanceof Instance) { | ||
o._values[field] = new UpdateProxy(o, field, reference) | ||
o._values[field] = new UpdateProxy(o, field, reference, model.schema[field]) | ||
} else { | ||
@@ -120,0 +122,0 @@ o._values[field] = reference |
@@ -7,2 +7,3 @@ const isRawObject = require('../util').isRawObject | ||
prototype.count = function (query) { | ||
this.coerceQuery(query) | ||
return this.client.count(this.name, query) | ||
@@ -29,2 +30,3 @@ } | ||
prototype.deleteMany = function (query) { | ||
this.coerceQuery(query) | ||
if (this._hasDeleteHooks()) { | ||
@@ -36,3 +38,3 @@ let instances | ||
return Promise.all(instances.map(instance => { | ||
return runHooks(this.predelete, instance) | ||
return promisifyHook(this.predelete, instance) | ||
})) | ||
@@ -45,3 +47,3 @@ }).then(() => { | ||
return Promise.all(instances.map(instance => { | ||
return runHooks(this.postdelete, instance) | ||
return promisifyHook(this.postdelete, instance) | ||
})) | ||
@@ -56,2 +58,3 @@ }).then(() => { | ||
prototype.deleteOne = function (query) { | ||
this.coerceQuery(query) | ||
if (this._hasDeleteHooks()) { | ||
@@ -62,3 +65,3 @@ let instance | ||
instance = found | ||
return runHooks(this.predelete, instance) | ||
return promisifyHook(this.predelete, instance) | ||
}).then(() => { | ||
@@ -69,3 +72,3 @@ this.client.deleteOne(this.name, query).then(count => { | ||
}).then(() => { | ||
return runHooks(this.postdelete, instance) | ||
return promisifyHook(this.postdelete, instance) | ||
}).then(() => { | ||
@@ -79,2 +82,3 @@ return deletedCount | ||
prototype.find = function (query, projection) { | ||
this.coerceQuery(query) | ||
return this.client.find(this.name, query, projection, this) | ||
@@ -88,2 +92,3 @@ } | ||
prototype.findOne = function (query, projection) { | ||
this.coerceQuery(query) | ||
return this.client.findOne(this.name, query, projection, this) | ||
@@ -136,20 +141,4 @@ } | ||
if (instance.__original) { | ||
return runHooks(hook, instance) | ||
} | ||
return Promise.resolve() | ||
} | ||
function runHooks (hook, instance) { | ||
if (typeof hook === 'function') { | ||
return promisifyHook(hook, instance) | ||
} | ||
if (typeof hook === 'object') { | ||
const hooks = [] | ||
for (const field in hook) { | ||
if (field in instance._updates) { | ||
hooks.push(promisifyHook(hook[field], instance)) | ||
} | ||
} | ||
return Promise.all(hooks) | ||
} | ||
return Promise.resolve() | ||
@@ -159,2 +148,5 @@ } | ||
function promisifyHook (hook, instance) { | ||
if (!hook) { | ||
return Promise.resolve() | ||
} | ||
let returnedPromise | ||
@@ -210,9 +202,9 @@ const promisified = new Promise((resolve, reject) => { | ||
return runOncreate(this.oncreate, instance).then(() => { | ||
return runHooks(this.prevalidate, instance) | ||
return promisifyHook(this.prevalidate, instance) | ||
}).then(() => { | ||
return this.validate(instance) | ||
}).then(() => { | ||
return runHooks(this.postvalidate, instance) | ||
return promisifyHook(this.postvalidate, instance) | ||
}).then(() => { | ||
return runHooks(this.presave, instance) | ||
return promisifyHook(this.presave, instance) | ||
}).then(() => { | ||
@@ -228,3 +220,3 @@ return this.client.save(this.name, instance) | ||
}).then(() => { | ||
return runHooks(this.postsave, instance) | ||
return promisifyHook(this.postsave, instance) | ||
}).then(() => { | ||
@@ -231,0 +223,0 @@ // copy _updates values into _values |
@@ -1,2 +0,2 @@ | ||
const deepEqual = require('deep-equal') | ||
const traverse = require('traverse') | ||
@@ -8,2 +8,3 @@ const Typeclass = require('../typeclass') | ||
const ModelReference = require('./reference') | ||
const isRawObject = require('../util').isRawObject | ||
@@ -49,4 +50,4 @@ const hooks = ['oncreate', 'prevalidate', 'postvalidate', 'presave', 'postsave', 'predelete', 'postdelete'] | ||
if (!('_id' in schema)) { | ||
// _id is optional because it might need to be created | ||
o.schema._id = Typeclass([Typeclass.types.String, Typeclass.types.Number, null]) | ||
// ObjectID by default. Can be overriden. The typeclass handles the null case directly. | ||
o.schema._id = Typeclass.types.ObjectID(client.ObjectID) | ||
} | ||
@@ -91,2 +92,73 @@ for (const field in schema) { | ||
Model.prototype.coerce = function (field, value) { | ||
if (value === undefined) { | ||
return value | ||
} | ||
return this.schema[field].coerce(value) | ||
} | ||
Model.prototype.coerceQuery = function (query) { | ||
if (Object.keys(query).length === 0) { | ||
return | ||
} | ||
const schema = this.schema | ||
const ObjectID = this.client.ObjectID | ||
// traverse the query and transform only leaf nodes | ||
traverse(query).forEach(function (value) { | ||
if (this.isLeaf) { | ||
const field = this.path.filter(segment => { | ||
if (segment === '$and') return false | ||
if (segment === '$or') return false | ||
if (segment === '$not') return false | ||
if (segment === '$nor') return false | ||
if (String(parseInt(segment)) === segment) return false | ||
return true | ||
}) | ||
// ObjectIDs are Objects and their leaf nodes should be ignored | ||
if (this.parent.node instanceof ObjectID) return | ||
if (this.parent.node instanceof Buffer) return | ||
// certain queries should not be coerced | ||
if (field === '$expr') return | ||
if (field === '$text') return | ||
if (field === '$where') return | ||
if (field === '$jsonSchema') return | ||
if (this.key === '$regex') return | ||
if (this.key === '$exists') return | ||
if (this.key === '$type') return | ||
if (this.key === '$size') return this.update(Number(value)) | ||
// these are not actually leaf nodes. something else will need to be done, | ||
// but only if users need geospatial queries | ||
if (this.key === '$near') return | ||
if (this.key === '$geoIntersects') return | ||
if (this.key === '$geoWithin') return | ||
if (this.key === '$nearSphere') return | ||
let segments | ||
if (field[0].indexOf('.') > -1) { | ||
segments = field[0].split('.') | ||
} else if (isRawObject(schema[field[0]].type)) { | ||
segments = field | ||
} | ||
if (segments) { | ||
let typeclass = schema[segments.shift()] | ||
for (let i = 0; i < segments.length; i++) { | ||
let segment = segments[i] | ||
// last element is an array index | ||
//if (i === segments.length - 1 && String(parseInt(segment)) === segment) | ||
//segment = 0 | ||
if (String(parseInt(segment)) === segment) { | ||
typeclass = typeclass.type[0] | ||
} else { | ||
typeclass = typeclass.type[segment] | ||
} | ||
} | ||
this.update(typeclass.coerce(value)) | ||
return | ||
} | ||
this.update(schema[field[0]].coerce(value)) | ||
} | ||
}) | ||
} | ||
Model.prototype.validate = function (record) { | ||
@@ -93,0 +165,0 @@ return new Promise((resolve, reject) => { |
const deepEqual = require('deep-equal') | ||
const traverse = require('traverse') | ||
const ModelReference = require('./reference') | ||
const isRawObject = require('../util').isRawObject | ||
function UpdateProxy (parent, field, target, typeclass) { | ||
if (Array.isArray(target)) { | ||
return ArrayProxy(parent, field, target, typeclass) | ||
} | ||
return ObjectProxy(parent, field, target, typeclass) | ||
} | ||
// if any of these methods are called on an array, the array is modified beyond our ability to | ||
@@ -10,29 +18,7 @@ // use native MongoDB array manipulation. Therefore the whole array must be replaced | ||
function UpdateProxy (parent, field, target) { | ||
function ArrayProxy (parent, field, target, typeclass) { | ||
// each proxy has its own _updates object which is also a proxy so that changes bubble up | ||
defineProp(target, '_updates', new Proxy({}, { | ||
set (target, prop, value) { | ||
parent._updates[field] = parent[field] | ||
target[prop] = value | ||
return true | ||
}, | ||
})) | ||
define_updates(parent, field, target) | ||
if (target._proxied) { | ||
// reset all child proxies | ||
// might be better to try and use _updates to do this more efficiently | ||
traverse(target).forEach(function (target) { | ||
if (Array.isArray(target) || isRawObject(target)) { | ||
if (this.key !== undefined) { | ||
const parent = this.parent.node | ||
const field = this.key | ||
defineProp(target, '_updates', new Proxy({}, { | ||
set (target, prop, value) { | ||
parent._updates[field] = parent[field] | ||
target[prop] = value | ||
return true | ||
}, | ||
})) | ||
} | ||
} | ||
}) | ||
resetChildProxies(target) | ||
return target | ||
@@ -42,5 +28,8 @@ } | ||
for (const field of Object.keys(target)) { | ||
if (Array.isArray(target[field]) || isRawObject(target[field])) { | ||
target[field] = new UpdateProxy(target, field, target[field]) | ||
if (Array.isArray(target[field])) { | ||
target[field] = ArrayProxy(target, field, target[field], typeclass.type[0]) | ||
} | ||
if (isRawObject(target[field])) { | ||
target[field] = ObjectProxy(target, field, target[field], typeclass.type[0]) | ||
} | ||
} | ||
@@ -52,71 +41,89 @@ Object.defineProperty(target, '_proxied', { | ||
get (target, prop) { | ||
if (Array.isArray(target)) { | ||
if (prop === '$push') { | ||
parent._updates[field] = parent[field] | ||
return function (push, options) { | ||
options = options || {} | ||
if (!('_$push' in target)) { | ||
defineProp(target, '_$push', []) | ||
if (prop === '$push') { | ||
parent._updates[field] = parent[field] | ||
return function (push, options) { | ||
push = typeclass.coerce(push) | ||
options = options || {} | ||
if (!('_$push' in target)) { | ||
defineProp(target, '_$push', []) | ||
} | ||
if (options.$slice) { | ||
defineProp(target, '_$slice', options.$slice) | ||
} | ||
if (options.$position) { | ||
defineProp(target, '_$position', options.$position) | ||
} | ||
if (Array.isArray(push)) { | ||
for (const p of push) { | ||
target._$push.push(p) | ||
} | ||
if (options.$slice) { | ||
defineProp(target, '_$slice', options.$slice) | ||
} | ||
if (options.$position) { | ||
defineProp(target, '_$position', options.$position) | ||
} | ||
if (Array.isArray(push)) { | ||
for (const p of push) { | ||
target._$push.push(p) | ||
} | ||
} else { | ||
target._$push.push(push) | ||
} | ||
$push(target, push, options.$slice, options.$position) | ||
} else { | ||
target._$push.push(push) | ||
} | ||
$push(target, push, options.$slice, options.$position) | ||
} | ||
if (prop === '$pop') { | ||
parent._updates[field] = parent[field] | ||
return function (direction) { | ||
defineProp(target, '_$pop', direction) | ||
$pop(target, direction) | ||
} | ||
if (prop === '$pop') { | ||
parent._updates[field] = parent[field] | ||
return function (direction) { | ||
defineProp(target, '_$pop', direction) | ||
$pop(target, direction) | ||
} | ||
} | ||
if (prop === '$addToSet') { | ||
parent._updates[field] = parent[field] | ||
return function (add) { | ||
add = typeclass.coerce(add) | ||
if (!('_$addToSet' in target)) { | ||
defineProp(target, '_$addToSet', []) | ||
} | ||
} | ||
if (prop === '$addToSet') { | ||
parent._updates[field] = parent[field] | ||
return function (add) { | ||
if (!('_$addToSet' in target)) { | ||
defineProp(target, '_$addToSet', []) | ||
if (Array.isArray(add)) { | ||
for (const a of add) { | ||
target._$addToSet.push(a) | ||
} | ||
if (Array.isArray(add)) { | ||
for (const a of add) { | ||
target._$addToSet.push(a) | ||
} | ||
} else { | ||
target._$addToSet.push(add) | ||
} | ||
$addToSet(target, add) | ||
} else { | ||
target._$addToSet.push(add) | ||
} | ||
$addToSet(target, typeclass.coerce(add)) | ||
} | ||
if (prop === '$pull') { | ||
parent._updates[field] = parent[field] | ||
return function (filter) { | ||
defineProp(target, '_$pull', filter) | ||
$pull(target, filter) | ||
} | ||
} | ||
if (prop === '$pull') { | ||
parent._updates[field] = parent[field] | ||
return function (filter) { | ||
defineProp(target, '_$pull', filter) | ||
$pull(target, filter) | ||
} | ||
if (arrayModifyingMethods.includes(prop)) { | ||
defineProp(target, '_replace', true) | ||
parent._updates[field] = parent[field] | ||
} | ||
if (arrayModifyingMethods.includes(prop)) { | ||
defineProp(target, '_replace', true) | ||
parent._updates[field] = parent[field] | ||
} | ||
if (prop === 'push') { | ||
return function (...elems) { | ||
return target.push(...typeclass.coerce(elems)) | ||
} | ||
} | ||
if (prop === 'unshift') { | ||
return function (...elems) { | ||
return target.unshift(...typeclass.coerce(elems)) | ||
} | ||
} | ||
if (prop === 'splice') { | ||
return function (start, numRemoved, ...elems) { | ||
return target.splice(start, numRemoved, ...typeclass.coerce(elems)) | ||
} | ||
} | ||
return target[prop] | ||
}, | ||
set (target, prop, value) { | ||
if (Array.isArray(value) || isRawObject(value)) { | ||
target[prop] = new UpdateProxy(target, prop, value) | ||
const coerced = typeclass.type[0].coerce(value) | ||
if (Array.isArray(value)) { | ||
target[prop] = ArrayProxy(target, prop, coerced, typeclass.type[0]) | ||
} else if (isRawObject(value)) { | ||
target[prop] = ObjectProxy(target, prop, coerced, typeclass.type[0]) | ||
} else { | ||
target[prop] = value | ||
target[prop] = coerced | ||
} | ||
// this will cause _updates to bubble up until they hit the instance | ||
target._updates[prop] = value | ||
target._updates[prop] = coerced | ||
return true | ||
@@ -127,2 +134,45 @@ }, | ||
function ObjectProxy (parent, field, target, typeclass) { | ||
if (typeclass.type instanceof ModelReference) { | ||
// pretend like it's a normal typeclass | ||
typeclass = { | ||
type: typeclass.type.model.schema, | ||
} | ||
} | ||
// each proxy has its own _updates object which is also a proxy so that changes bubble up | ||
define_updates(parent, field, target) | ||
if (target._proxied) { | ||
resetChildProxies(target) | ||
return target | ||
} | ||
// map any object elements in the target into proxies | ||
for (const field of Object.keys(target)) { | ||
if (Array.isArray(target[field])) { | ||
target[field] = ArrayProxy(target, field, target[field], typeclass.type[field]) | ||
} | ||
if (isRawObject(target[field])) { | ||
target[field] = ObjectProxy(target, field, target[field], typeclass.type[field]) | ||
} | ||
} | ||
Object.defineProperty(target, '_proxied', { | ||
value: true, | ||
}) | ||
// Object proxy | ||
return new Proxy(target, { | ||
set (target, prop, value) { | ||
const coerced = typeclass.type[prop].coerce(value) | ||
if (Array.isArray(value)) { | ||
target[prop] = ArrayProxy(target, prop, coerced, typeclass.type[prop]) | ||
} else if (isRawObject(value)) { | ||
target[prop] = ObjectProxy(target, prop, coerced, typeclass.type[prop]) | ||
} else { | ||
target[prop] = coerced | ||
} | ||
// this will cause _updates to bubble up until they hit the instance | ||
target._updates[prop] = coerced | ||
return true | ||
}, | ||
}) | ||
} | ||
function defineProp (obj, key, value) { | ||
@@ -136,2 +186,26 @@ Object.defineProperty(obj, key, { | ||
function define_updates (parent, field, target) { | ||
defineProp(target, '_updates', new Proxy({}, { | ||
set (target, prop, value) { | ||
parent._updates[field] = parent[field] | ||
target[prop] = value | ||
return true | ||
}, | ||
})) | ||
} | ||
function resetChildProxies (target) { | ||
// might be better to try and use _updates to do this more efficiently | ||
traverse(target).forEach(function (target) { | ||
if (Array.isArray(target) || isRawObject(target)) { | ||
if (this.key !== undefined) { | ||
const parent = this.parent.node | ||
const field = this.key | ||
define_updates(parent, field, target) | ||
} | ||
} | ||
}) | ||
} | ||
// use Array.prototype.method.call to avoid triggering array replacement | ||
function $push (array, toPush, slice, position) { | ||
@@ -142,3 +216,3 @@ toPush = Array.isArray(toPush) ? toPush : [toPush] | ||
if (slice) { | ||
Array.prototype.slice.call(array, 0, slice) | ||
Array.prototype.splice.call(array, slice) | ||
} | ||
@@ -145,0 +219,0 @@ } |
@@ -55,7 +55,7 @@ const type = require('./type') | ||
const promises = [] | ||
for (const prop in this.type) { | ||
for (const prop of Object.keys(this.type)) { | ||
promises.push(this.type[prop].check(value[prop])) | ||
if (!this.type[prop].check(value[prop])) { | ||
return resolve(false) | ||
} | ||
//if (!this.type[prop].check(value[prop])) { | ||
//return resolve(false) | ||
//} | ||
} | ||
@@ -72,2 +72,56 @@ Promise.all(promises).then(matches => { | ||
Typeclass.prototype.coerce = function (value) { | ||
if (this.type instanceof ModelReference) { | ||
if (this.type.type === 'reference') { | ||
// if it's an object, we're going to saveRefs it, so it should not be coerced now | ||
// or it's an ObjectID in which case we don't need to coerce it anyway | ||
if (typeof value === 'object') { | ||
return value | ||
} | ||
return this.type.model.schema._id.coerce(value) | ||
} | ||
if (this.type.type === 'embed' || this.type.type === 'embedOnly') { | ||
for (const field of Object.keys(value)) { | ||
value[field] = this.type.model.coerce(field, value[field]) | ||
} | ||
return value | ||
} | ||
} | ||
if (this.type === Boolean) { | ||
if (value === 'true') { | ||
return true | ||
} | ||
if (value === 'false') { | ||
return false | ||
} | ||
return Boolean(value) | ||
} | ||
if (this.type === String) { | ||
return String(value) | ||
} | ||
if (this.type === Number) { | ||
return Number(value) | ||
} | ||
if (this.type === Date) { | ||
return new Date(value) | ||
} | ||
if (type.isArray(this.type)) { | ||
if (Array.isArray(value)) { | ||
return value.map(val => this.type[0].coerce(val)) | ||
} | ||
return this.type[0].coerce(value) | ||
} | ||
// in the interest of keeping coerce synchronous, we will not perform type coersion on Option types | ||
// currently it's impossible to determine which type the value should be coerced to in a synchronous way | ||
if (type.isOption(this.type)) { | ||
return value | ||
} | ||
if (typeof this.type === 'object') { | ||
for (const prop of Object.keys(this.type)) { | ||
value[prop] = this.type[prop].coerce(value[prop]) | ||
} | ||
return value | ||
} | ||
} | ||
Typeclass.prototype.__init = function (src) { | ||
@@ -74,0 +128,0 @@ this.type = undefined |
@@ -7,2 +7,3 @@ const construction = require('./construction') | ||
const _Date = require('./date')() | ||
const _ObjectID = require('./objectid') | ||
const util = require('./util') | ||
@@ -71,2 +72,3 @@ | ||
}), | ||
ObjectID: _ObjectID, | ||
Email: util.Email, | ||
@@ -73,0 +75,0 @@ URL: util.URL, |
@@ -0,0 +0,0 @@ // contains utility Typeclasses |
{ | ||
"name": "nekodb", | ||
"version": "1.1.2", | ||
"version": "2.0.0", | ||
"description": "Tiny ODM for MongoDB/NeDB", | ||
"main": "ko.js", | ||
"scripts": { | ||
"test": "tape ./test/test.js | tap-spec", | ||
"test": "tape ./test/test.js | tap-spec --color", | ||
"coverage": "nyc tape ./test/test.js | tap-spec", | ||
@@ -9,0 +9,0 @@ "coverageHTML": "nyc report --reporter=html" |
@@ -193,2 +193,4 @@ # NekoDB | ||
#### You must call ko.connect before creating your models or you will get the error "ko.models is not defined" | ||
## Creating schemas | ||
@@ -826,9 +828,5 @@ | ||
Hooks can be a function, which will run regardless of what fields have been updated, or an object | ||
whose keys are fields, and whose values are the hooks to run only when the specified field is | ||
updated. To add a named hook that always runs, use `''` as the field name. | ||
To determine whether a hook should modify a certain field you can use the instance method | ||
`isUpdated` which takes a field name. | ||
For expensive operations, or operations which should not be repeated, you should use a hook | ||
object with named hooks to limit when the hooks are run. | ||
#### oncreate | ||
@@ -865,13 +863,13 @@ Runs immediately after a model is created. Used to asynchronously add default values to a model | ||
$$hooks: { | ||
presave: { | ||
password: (user, next) => { | ||
bcrypt.hash(user.password, saltRounds, function (err, hash) { | ||
if (err) { | ||
return next(err) | ||
} | ||
user.password = hash | ||
next() | ||
}) | ||
} | ||
} | ||
presave: (user, next) => { | ||
if (user.isUpdated('password')) { | ||
bcrypt.hash(user.password, saltRounds, function (err, hash) { | ||
if (err) { | ||
return next(err) | ||
} | ||
user.password = hash | ||
next() | ||
}) | ||
} | ||
} | ||
} | ||
@@ -923,2 +921,3 @@ }) | ||
``` | ||
Now an error will be thrown if we attempt to create two users with the same username. | ||
@@ -925,0 +924,0 @@ |
module.exports = { | ||
testMongo: false, | ||
testMongo: true, | ||
client: 'mongodb', | ||
@@ -4,0 +4,0 @@ username: '', |
@@ -11,2 +11,3 @@ const path = require('path') | ||
const joinTests = require('./tests/model.join') | ||
const coerceTests = require('./tests/model.coerce') | ||
const cursorTests = require('./tests/cursor') | ||
@@ -27,8 +28,10 @@ const hooksTests = require('./tests/hooks') | ||
joinTests(ko, () => { | ||
cursorTests(ko, () => { | ||
hooksTests(ko, () => { | ||
indexTests(ko, () => { | ||
arrayOpsTests(ko, () => { | ||
proxySetTests(ko, () => { | ||
next() | ||
coerceTests(ko, () => { | ||
cursorTests(ko, () => { | ||
hooksTests(ko, () => { | ||
indexTests(ko, () => { | ||
arrayOpsTests(ko, () => { | ||
proxySetTests(ko, () => { | ||
next() | ||
}) | ||
}) | ||
@@ -35,0 +38,0 @@ }) |
@@ -9,3 +9,5 @@ const test = require('tape') | ||
ko.models({ | ||
ko_db_test_cursor: {}, | ||
ko_db_test_cursor: { | ||
_id: ko.Number, | ||
}, | ||
}) | ||
@@ -12,0 +14,0 @@ |
@@ -10,2 +10,3 @@ const test = require('tape') | ||
name: ko.String, | ||
update: ko.String, | ||
$$hooks: { | ||
@@ -27,2 +28,6 @@ oncreate: function (instance, next) { | ||
instance.name = 'New value' | ||
if (instance.isUpdated('update')) { | ||
instance.update = 'this field was updated' | ||
} | ||
next() | ||
@@ -49,6 +54,7 @@ }, | ||
name: 'Old value', | ||
update: 'Old value', | ||
}).save().then(instance => { | ||
t.equal(instance.additionalValue, 'Added', 'Added value in postsave') | ||
}).then(() => { | ||
return HookModel.count({name: 'New value'}) | ||
return HookModel.count({name: 'New value', update: 'this field was updated'}) | ||
}).then(count => { | ||
@@ -60,2 +66,3 @@ t.equal(count, 1, 'Saved to database with changed value') | ||
name: 'Value', | ||
update: 'Value', | ||
}).save() | ||
@@ -71,45 +78,2 @@ }).then(() => { | ||
test('Named hooks should only run at the appropriate time', function (t) { | ||
const NamedHookModel = ko.Model('NamedHooks', { | ||
field1: ko.String.match(/^[a-z]*$/), | ||
field2: ko.String.match(/^[a-z]*$/), | ||
}) | ||
NamedHookModel.presave = { | ||
field1: (instance, next) => { | ||
instance.field1 = '__' + instance.field1 | ||
next() | ||
}, | ||
field2: (instance, next) => { | ||
instance.field2 = '__' + instance.field2 | ||
next() | ||
}, | ||
} | ||
NamedHookModel.create({ | ||
_id: '0', | ||
field1: 'abc', | ||
field2: 'def', | ||
}).save().then(model => { | ||
t.deepEqual(model, { | ||
_id: '0', | ||
field1: '__abc', | ||
field2: '__def', | ||
}, 'Presave hooks were run when model was created') | ||
model.field1 = 'ghi' | ||
return model.save() | ||
}).then(model => { | ||
t.equal(model.field1, '__ghi', 'Updated field ran presave hook') | ||
t.equal(model.field2, '__def', 'Not updated field did not run presave hook') | ||
return model.save() | ||
}).then(model => { | ||
t.equal(model.field1, '__ghi', 'When no updates occurred, hooks did not run') | ||
t.equal(model.field2, '__def', 'When no updates occurred, hooks did not run') | ||
t.end() | ||
}).catch(err => { | ||
console.log(err) | ||
t.error(err) | ||
t.end() | ||
}) | ||
}) | ||
test('All done', function (t) { | ||
@@ -116,0 +80,0 @@ t.end() |
@@ -0,0 +0,0 @@ const test = require('tape') |
@@ -10,7 +10,8 @@ const test = require('tape') | ||
RefModel = ko.Model('ko_db_test_join_ref', { | ||
_id: ko.Number, | ||
field: ko.String, | ||
}) | ||
JoinModel = ko.Model('ko_db_test_join', { | ||
_id: ko.Number, | ||
ref: RefModel, | ||
@@ -21,2 +22,3 @@ ref2: RefModel, | ||
JoinArrModel = ko.Model('ko_db_test_join_multi', { | ||
_id: ko.Number, | ||
refs: [RefModel], | ||
@@ -23,0 +25,0 @@ }) |
@@ -87,3 +87,5 @@ const test = require('tape') | ||
test('Model#findById', function (t) { | ||
const FindByIdModel = ko.Model('ko_db_test_methods_findbyid', {}) | ||
const FindByIdModel = ko.Model('ko_db_test_methods_findbyid', { | ||
_id: ko.String, | ||
}) | ||
@@ -144,5 +146,5 @@ FindByIdModel.create({ | ||
DeleteModel.create({name: 'Barry'}).save(), | ||
DeleteModel.create({_id: 'B0R1S', name: 'Boris'}).save(), | ||
]).then(() => { | ||
return DeleteModel.deleteById('B0R1S') | ||
DeleteModel.create({name: 'Boris'}).save(), | ||
]).then(models => { | ||
return DeleteModel.deleteById(models[4]._id) | ||
}).then(() => { | ||
@@ -149,0 +151,0 @@ return DeleteModel.count({}) |
@@ -46,6 +46,7 @@ const test = require('tape') | ||
ko_db_test_ref3: { | ||
field: ko.String | ||
_id: ko.String, | ||
field: ko.String, | ||
}, | ||
ko_db_test_test3: { | ||
ref: ko.models.ko_db_test_ref3.reference() | ||
ref: ko.models.ko_db_test_ref3.reference(), | ||
} | ||
@@ -55,3 +56,3 @@ }) | ||
_id: '0', | ||
field: 'value' | ||
field: 'value', | ||
}).save().then(ref => { | ||
@@ -91,3 +92,3 @@ const model = ko.models.ko_db_test_test3.create({ | ||
ko_db_test_ref4: { | ||
field: ko.String | ||
field: ko.Number.max(99) | ||
}, | ||
@@ -99,3 +100,3 @@ ko_db_test_test4: { | ||
const model = ko.models.ko_db_test_test4.create({ | ||
ref: { field: 'valid' }, | ||
ref: { field: 10 }, | ||
}) | ||
@@ -108,2 +109,5 @@ ko.models.ko_db_test_test4.validate(model).then(() => { | ||
}).saveRefs() | ||
}).then(saved => { | ||
t.fail('Model saved where it should have failed') | ||
t.end() | ||
}).catch(err => { | ||
@@ -110,0 +114,0 @@ // rejected promise means validation failed |
@@ -17,6 +17,16 @@ const test = require('tape') | ||
t.equal(count, 1, 'Count incremented after creation') | ||
return SimpleModel.create({_id: 1, string: 'nice'}).save().then(saved => { | ||
return saved | ||
}) | ||
}).then(simple => { | ||
t.end() | ||
}).catch(err => { | ||
t.error(err) | ||
t.end() | ||
}) | ||
}) | ||
test('Saving a new simple model with specified _id type', function (t) { | ||
const SimpleModelNumID = ko.Model('ko_db_test_save_simple_numid', { | ||
_id: ko.Number, | ||
string: ko.String, | ||
}) | ||
SimpleModelNumID.create({_id: 1, string: 'nice'}).save().then(simple => { | ||
t.deepEqual(simple, { | ||
@@ -26,5 +36,5 @@ _id: 1, | ||
}, 'Instance uses assigned _id when specified and contains data') | ||
return SimpleModel.count({}) | ||
return SimpleModelNumID.count({}) | ||
}).then(count => { | ||
t.equal(count, 2, 'Count incremented after creation') | ||
t.equal(count, 1, 'Count incremented after creation') | ||
t.end() | ||
@@ -39,2 +49,3 @@ }).catch(err => { | ||
const SimpleModelFail = ko.Model('ko_db_test_save_simple_fail', { | ||
_id: ko.String, | ||
string: ko.String[5], | ||
@@ -50,2 +61,3 @@ number: ko.Number, | ||
t.deepEqual(fields, { | ||
_id: undefined, | ||
string: undefined, | ||
@@ -59,2 +71,3 @@ number: undefined, | ||
return SimpleModelFail.create({ | ||
_id: '101', | ||
string: 'too long string', | ||
@@ -100,2 +113,3 @@ number: 0, | ||
const ArrayModel = ko.Model('ko_db_test_save_array', { | ||
_id: ko.String, | ||
array: [ko.Number], | ||
@@ -147,2 +161,3 @@ }) | ||
const DocumentModel = ko.Model('ko_db_test_save_document', { | ||
_id: ko.String, | ||
document: { | ||
@@ -202,2 +217,3 @@ field: ko.String, | ||
const ReferencedModel = ko.Model('ko_db_test_save_referenced', { | ||
_id: ko.String, | ||
string: ko.String, | ||
@@ -207,2 +223,3 @@ }) | ||
const ModelWithRef = ko.Model('ko_db_test_save_with_ref', { | ||
_id: ko.String, | ||
field: ko.String, | ||
@@ -283,2 +300,3 @@ ref: ReferencedModel, | ||
}).catch(err => { | ||
console.error(err) | ||
t.error(err) | ||
@@ -340,2 +358,3 @@ t.end() | ||
const EmbedModel = ko.Model('ko_db_test_save_embed_only', { | ||
_id: ko.Number, | ||
ref: EmbeddedModel.embedOnly(), | ||
@@ -372,6 +391,7 @@ }) | ||
const EmbeddedModel = ko.Model('ko_db_test_save_invalid_embedded_only', { | ||
string: ko.String, | ||
string: ko.String[5], | ||
}) | ||
const EmbedModel = ko.Model('ko_db_test_save_invalid_embed_only', { | ||
_id: ko.Number, | ||
ref: EmbeddedModel, | ||
@@ -382,3 +402,3 @@ }) | ||
ref: { | ||
string: 1234, | ||
string: '123456', | ||
}, | ||
@@ -390,3 +410,3 @@ }).saveAll().then(() => { | ||
t.deepEqual(err, { | ||
ref: { string: 1234 }, | ||
ref: { string: '123456' }, | ||
}, 'Returned a correct errors object') | ||
@@ -399,6 +419,7 @@ t.end() | ||
const EmbeddedModel = ko.Model('ko_db_test_save_embedded_only', { | ||
string: ko.String, | ||
string: ko.String[5], | ||
}) | ||
const EmbedModel = ko.Model('ko_db_test_save_embed_only', { | ||
_id: ko.Number, | ||
ref: EmbeddedModel.embedOnly(), | ||
@@ -409,3 +430,3 @@ }) | ||
ref: { | ||
string: 1234, | ||
string: '123456', | ||
}, | ||
@@ -417,3 +438,3 @@ }).saveAll().then(() => { | ||
t.deepEqual(err, { | ||
ref: { string: 1234 }, | ||
ref: { string: '123456' }, | ||
}, 'Returned a correct errors object') | ||
@@ -426,2 +447,3 @@ t.end() | ||
const ReferencedModel = ko.Model('ko_db_test_save_arr_referenced', { | ||
_id: ko.Number, | ||
string: ko.String, | ||
@@ -525,3 +547,3 @@ }) | ||
}).catch(err => { | ||
console.log(err) | ||
console.error(err) | ||
t.error(err) | ||
@@ -534,2 +556,3 @@ t.end() | ||
const EmbeddedModel = ko.Model('ko_db_test_save_arr_embedded', { | ||
_id: ko.Number, | ||
string: ko.String, | ||
@@ -539,2 +562,3 @@ }) | ||
const EmbedModel = ko.Model('ko_db_test_save_arr_embed', { | ||
_id: ko.Number, | ||
name: ko.String, | ||
@@ -545,6 +569,9 @@ refs: [EmbeddedModel.embed()], | ||
EmbedModel.create({ | ||
_id: 12, | ||
name: 'xyz', | ||
refs: [{ | ||
_id: 100, | ||
string: '1', | ||
}, { | ||
_id: 101, | ||
string: '2', | ||
@@ -551,0 +578,0 @@ }], |
@@ -37,3 +37,3 @@ const test = require('tape') | ||
const ArrPush = ko.Model('ko_db_test_arrayop_push', { | ||
array: [ko.String], | ||
array: [ko.String[10]], | ||
}) | ||
@@ -66,7 +66,7 @@ | ||
try { | ||
model.array.$push(100) | ||
model.array.$push('too long of a string') | ||
await model.save() | ||
t.fail('Model saved where it should have failed') | ||
} catch (err) { | ||
console.log(err) | ||
//console.log(err) | ||
t.pass('Threw an error when attempting to $push invalid value') | ||
@@ -112,3 +112,3 @@ } | ||
const ArrAddToSet = ko.Model('ko_db_test_arrayop_addtoset', { | ||
array: [ko.String], | ||
array: [ko.String[10]], | ||
}) | ||
@@ -147,7 +147,7 @@ | ||
try { | ||
model.array.$addToSet(100) | ||
model.array.$addToSet('too long of a string') | ||
await model.save() | ||
t.fail('Model saved where it should have failed') | ||
} catch (err) { | ||
console.log(err) | ||
//console.log(err) | ||
t.pass('Threw an error when attempting to $addToSet invalid value') | ||
@@ -192,3 +192,3 @@ } | ||
} catch (err) { | ||
console.log(err) | ||
//console.log(err) | ||
t.pass('Model would not save an empty array') | ||
@@ -195,0 +195,0 @@ } |
@@ -6,4 +6,4 @@ const test = require('tape') | ||
const ArrayModel = ko.Model('ko_db_test_proxyset_arr', { | ||
array: [ko.String], | ||
arraysArray: [[ko.Number]], | ||
array: [ko.String[10]], | ||
arraysArray: [[ko.Number.max(100)]], | ||
}) | ||
@@ -41,10 +41,19 @@ | ||
try { | ||
model.array[0] = 123 | ||
model.array[0] = 'way too long of a string' | ||
await model.save() | ||
t.fail('Model saved where it should have failed') | ||
} catch (err) { | ||
console.log(err) | ||
//console.log(err) | ||
t.pass('Did not save an invalid field') | ||
} | ||
try { | ||
model.arraysArray[0][0] = 100000 | ||
await model.save() | ||
t.fail('Model saved where it should have failed') | ||
} catch (err) { | ||
//console.log(err) | ||
t.pass('Did not save an invalid field') | ||
} | ||
t.end() | ||
@@ -51,0 +60,0 @@ }) |
178854
39
4460
1110