New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

orm

Package Overview
Dependencies
Maintainers
1
Versions
103
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

orm - npm Package Compare versions

Comparing version 2.1.0 to 2.1.1

lib/Drivers/helpers.js

32

Changelog.md

@@ -0,1 +1,33 @@

### v2.2.0 - (to do, in future)
- Fixes error code spelling: `PARAM_MISSMATCH` -> `PARAM_MISMATCH`
### v2.1.1 - 13 Sep 2013
- Add TypeScript interface
- Allow custom join tables (#276)
- Fixes stack overflow when saving auto-fetched model with relations (#279)
- Unique validator can be scoped and case insensitive (#288)
- Allow async express middleware (#291)
- Allow finding by associations (#293)
- Fix sqlite find with boolean (#292)
- Fix `afterLoad` hook error handling (#301)
- Allow auto-escaping for custom queries (#304)
- Add support for custom property types (#305)
- Allow ordering by raw sql - .orderRaw() when chaining (#308, #311)
- Fix saving Instance.extra fields (#312)
- Fix `NaN` handling (#310)
- Fix incorrect SQL query (#313)
- Deprecated `PARAM_MISSMATCH` ErrorCode in favour of correctly spelt `PARAM_MISMATCH` (#315)
- Add promises to query chain (#316)
- Adds a test for hasMany.delAccessor with arguments switched (#320)
- Allow passing timezone in database connection string, local timezone is now default (#325, #303)
- Adds ability to call db.load() with multiple files (closes #329)
- For mysql driver, when using pool, use con.release() instead of con.end() (if defined) (closes #335)
- Passes error from afterLoad hook to ready event
- Most errors now have a model property
- Adds connection.pool and connection.debug settings
- Fixes throw when calling ChainFind.first() or .last() and it has an error
- Removes upper limit on VARCHAR column size
- Allows multi-key models to support hasMany
### v2.1.0 - 3 Aug 2013

@@ -2,0 +34,0 @@

20

lib/AggregateFunctions.js

@@ -7,3 +7,3 @@ var ErrorCodes = require("./ErrorCodes");

function AggregateFunctions(opts) {
if (typeof opts.driver.getQuery != "function") {
if (typeof opts.driver.getQuery !== "function") {
throw ErrorCodes.generateError(ErrorCodes.NO_SUPPORT, "This driver does not support aggregate functions");

@@ -30,3 +30,3 @@ }

if (fun == "distinct") {
if (fun === "distinct") {
used_distinct = true;

@@ -44,3 +44,3 @@ }

limit: function (offset, limit) {
if (typeof limit == "number") {
if (typeof limit === "number") {
opts.limit = [ offset, limit ];

@@ -58,3 +58,3 @@ } else {

if (arguments.length === 0) {
throw ErrorCodes.generateError(ErrorCodes.PARAM_MISSMATCH, "When using append you must at least define one property");
throw ErrorCodes.generateError(ErrorCodes.PARAM_MISMATCH, "When using append you must at least define one property");
}

@@ -67,4 +67,4 @@ opts.properties = opts.properties.concat(Array.isArray(arguments[0]) ?

as: function (alias) {
if (aggregates.length === 0 || (aggregates.length == 1 && aggregates[0].length === 0)) {
throw ErrorCodes.generateError(ErrorCodes.PARAM_MISSMATCH, "No aggregate functions defined yet");
if (aggregates.length === 0 || (aggregates.length === 1 && aggregates[0].length === 0)) {
throw ErrorCodes.generateError(ErrorCodes.PARAM_MISMATCH, "No aggregate functions defined yet");
}

@@ -86,3 +86,3 @@

if (fun.toLowerCase() == "distinct") {
if (fun.toLowerCase() === "distinct") {
used_distinct = true;

@@ -94,3 +94,3 @@ }

get: function (cb) {
if (typeof cb != "function") {
if (typeof cb !== "function") {
throw ErrorCodes.generateError(ErrorCodes.MISSING_CALLBACK, "You must pass a callback to Model.aggregate().get()");

@@ -102,3 +102,3 @@ }

if (aggregates.length === 0) {
throw ErrorCodes.generateError(ErrorCodes.PARAM_MISSMATCH, "Missing aggregate functions");
throw ErrorCodes.generateError(ErrorCodes.PARAM_MISMATCH, "Missing aggregate functions");
}

@@ -143,3 +143,3 @@

if (used_distinct && aggregates.length == 1) {
if (used_distinct && aggregates.length === 1) {
for (i = 0; i < data.length; i++) {

@@ -146,0 +146,0 @@ items.push(data[i][Object.keys(data[i]).pop()]);

@@ -76,3 +76,3 @@ var _ = require('lodash');

if (!Instance[Model.id]) {
cb(ErrorCodes.generateError(ErrorCodes.NOT_DEFINED, "Instance not saved, cannot get extension"));
cb(ErrorCodes.generateError(ErrorCodes.NOT_DEFINED, "Instance not saved, cannot get extension", { model: Model.table }));
} else {

@@ -90,3 +90,3 @@ association.model.get(util.values(Instance, Model.id), function (err, extension) {

if (!Instance[Model.id]) {
cb(ErrorCodes.generateError(ErrorCodes.NOT_DEFINED, "Instance not saved, cannot get extension"));
cb(ErrorCodes.generateError(ErrorCodes.NOT_DEFINED, "Instance not saved, cannot get extension", { model: Model.table }));
} else {

@@ -131,3 +131,3 @@ association.model.get(util.values(Instance, Model.id), cb);

if (!Instance[Model.id]) {
cb(ErrorCodes.generateError(ErrorCodes.NOT_DEFINED, "Instance not saved, cannot get extension"));
cb(ErrorCodes.generateError(ErrorCodes.NOT_DEFINED, "Instance not saved, cannot get extension", { model: Model.table }));
} else {

@@ -134,0 +134,0 @@ var conditions = {};

@@ -9,9 +9,2 @@ var _ = require("lodash");

exports.prepare = function (Model, associations) {
if (Model.id.length > 1) {
Model.hasMany = function () {
throw ErrorCodes.generateError(ErrorCodes.NO_SUPPORT, "Model.hasMany() does not support multiple keys models");
};
return;
}
Model.hasMany = function () {

@@ -44,3 +37,3 @@ var name;

for (var k in props) {
props[k] = Property.normalize(props[k], Model.settings);
props[k] = Property.normalize(props[k], {}, Model.settings);
}

@@ -50,2 +43,3 @@ }

var assocName = opts.name || ucfirst(name);
var assocTemplateName = opts.accessor || assocName;
var association = {

@@ -62,7 +56,7 @@ name : name,

mergeAssocId : util.wrapFieldObject(opts.mergeAssocId, Model, name, Model.properties) || util.formatField(Model, name, true, opts.reversed),
getAccessor : opts.getAccessor || ("get" + assocName),
setAccessor : opts.setAccessor || ("set" + assocName),
hasAccessor : opts.hasAccessor || ("has" + assocName),
delAccessor : opts.delAccessor || ("remove" + assocName),
addAccessor : opts.addAccessor || ("add" + assocName)
getAccessor : opts.getAccessor || ("get" + assocTemplateName),
setAccessor : opts.setAccessor || ("set" + assocTemplateName),
hasAccessor : opts.hasAccessor || ("has" + assocTemplateName),
delAccessor : opts.delAccessor || ("remove" + assocTemplateName),
addAccessor : opts.addAccessor || ("add" + assocTemplateName)
};

@@ -75,2 +69,3 @@

reversed : true,
association : opts.reverseAssociation,
mergeTable : association.mergeTable,

@@ -247,3 +242,3 @@ mergeId : association.mergeAssocId,

if (Instances.length === 0) {
throw ErrorCodes.generateError(ErrorCodes.PARAM_MISSMATCH, "No associations defined");
throw ErrorCodes.generateError(ErrorCodes.PARAM_MISMATCH, "No associations defined", { model: Model.name });
}

@@ -271,4 +266,18 @@

value: function () {
var Associations = Array.prototype.slice.apply(arguments);
var cb = (typeof Associations[Associations.length - 1] == "function" ? Associations.pop() : noOperation);
var Associations = [];
var cb = noOperation;
for (var i = 0; i < arguments.length; i++) {
switch (typeof arguments[i]) {
case "function":
cb = arguments[i];
break;
case "object":
if (Array.isArray(arguments[i])) {
Associations = Associations.concat(arguments[i]);
} else if (arguments[i].isInstance) {
Associations.push(arguments[i]);
}
break;
}
}
var conditions = {};

@@ -314,5 +323,7 @@ var run = function () {

var run = function () {
var savedAssociations = [];
var saveNextAssociation = function () {
if (Associations.length === 0) {
return cb();
return cb(null, savedAssociations);
}

@@ -339,2 +350,4 @@

savedAssociations.push(Association);
return saveNextAssociation();

@@ -352,2 +365,4 @@ });

savedAssociations.push(Association);
return saveNextAssociation();

@@ -379,3 +394,3 @@ });

if (Associations.length === 0) {
throw ErrorCodes.generateError(ErrorCodes.PARAM_MISSMATCH, "No associations defined");
throw ErrorCodes.generateError(ErrorCodes.PARAM_MISMATCH, "No associations defined", { model: Model.name });
}

@@ -382,0 +397,0 @@

@@ -9,2 +9,3 @@ var _ = require("lodash");

var assocName;
var assocTemplateName;
var association = {

@@ -37,2 +38,3 @@ name : Model.table,

assocName = ucfirst(association.name);
assocTemplateName = association.accessor || assocName;

@@ -46,3 +48,3 @@ if (!association.hasOwnProperty("field")) {

if (!association.hasOwnProperty(k + "Accessor")) {
association[k + "Accessor"] = Accessors[k] + assocName;
association[k + "Accessor"] = Accessors[k] + assocTemplateName;
}

@@ -55,2 +57,4 @@ }

if (!association.reversed) {
Model.allProperties[k] = _.omit(association.field[k], 'klass');
Model.allProperties[k].klass = 'hasOne';
model_fields.push(k);

@@ -63,2 +67,4 @@ }

reversed : true,
accessor : association.reverseAccessor,
reverseAccessor: undefined,
field : association.field,

@@ -70,3 +76,3 @@ autoFetch : association.autoFetch,

Model["findBy" + assocName] = function () {
Model["findBy" + assocTemplateName] = function () {
var cb = null, conditions = null, options = {};

@@ -90,3 +96,3 @@

if (conditions === null) {
throw ErrorCodes.generateError(ErrorCodes.PARAM_MISSMATCH, ".findBy(" + assocName + ") is missing a conditions object");
throw ErrorCodes.generateError(ErrorCodes.PARAM_MISMATCH, ".findBy(" + assocName + ") is missing a conditions object");
}

@@ -93,0 +99,0 @@

var _ = require("lodash");
var Singleton = require("./Singleton");
var ChainInstance = require("./ChainInstance");
var Promise = require("./Promise").Promise;

@@ -8,2 +9,3 @@ module.exports = ChainFind;

function ChainFind(Model, opts) {
var promise = null;
var chain = {

@@ -13,14 +15,14 @@ find: function () {

arguments = Array.prototype.slice.call(arguments);
var args = Array.prototype.slice.call(arguments);
opts.conditions = opts.conditions || {};
if (typeof _.last(arguments) == 'function') {
cb = arguments.pop();
if (typeof _.last(args) === 'function') {
cb = args.pop();
}
if (typeof arguments[0] == 'object') {
_.extend(opts.conditions, arguments[0]);
} else if (typeof arguments[0] == 'string') {
opts.conditions.__sql = opts.conditions.__sql || []
opts.conditions.__sql.push(arguments);
if (typeof args[0] === 'object') {
_.extend(opts.conditions, args[0]);
} else if (typeof args[0] === 'string') {
opts.conditions.__sql = opts.conditions.__sql || [];
opts.conditions.__sql.push(args);
}

@@ -56,9 +58,16 @@

}
if (property[0] == "-") {
if (property[0] === "-") {
opts.order.push([ property.substr(1), "Z" ]);
} else {
opts.order.push([ property, (order && order.toUpperCase() == "Z" ? "Z" : "A") ]);
opts.order.push([ property, (order && order.toUpperCase() === "Z" ? "Z" : "A") ]);
}
return this;
},
orderRaw: function (str, args) {
if (!Array.isArray(opts.order)) {
opts.order = [];
}
opts.order.push([ str, args || [] ]);
return this;
},
count: function (cb) {

@@ -102,3 +111,3 @@ opts.driver.count(opts.table, opts.conditions, {}, function (err, data) {

return this.run(function (err, items) {
return cb(err, items.length > 0 ? items[0] : null);
return cb(err, items && items.length > 0 ? items[0] : null);
});

@@ -108,3 +117,3 @@ },

return this.run(function (err, items) {
return cb(err, items.length > 0 ? items[items.length - 1] : null);
return cb(err, items && items.length > 0 ? items[items.length - 1] : null);
});

@@ -118,2 +127,16 @@ },

},
success: function (cb) {
if (!promise) {
promise = new Promise();
promise.handle(this.all);
}
return promise.success(cb);
},
fail: function (cb) {
if (!promise) {
promise = new Promise();
promise.handle(this.all);
}
return promise.fail(cb);
},
all: function (cb) {

@@ -135,12 +158,14 @@ opts.driver.find(opts.only, opts.table, opts.conditions, {

var createInstance = function (idx) {
opts.newInstance(data[idx], function (err, instance) {
data[idx] = instance;
if (--pending === 0) {
return cb(null, data);
}
});
};
for (var i = 0; i < data.length; i++) {
(function (idx) {
opts.newInstance(data[idx], function (err, instance) {
data[idx] = instance;
if (--pending === 0) {
return cb(null, data);
}
});
})(i);
createInstance(i);
}

@@ -162,3 +187,3 @@ });

"exists", "settings", "aggregate" ].indexOf(k) >= 0) continue;
if (typeof Model[k] != "function") continue;
if (typeof Model[k] !== "function") continue;

@@ -184,3 +209,3 @@ chain[k] = Model[k];

for (var i = 0; i < assocIds.length; i++) {
if (typeof conditions[assocIds[i]] == 'undefined')
if (typeof conditions[assocIds[i]] === 'undefined')
conditions[assocIds[i]] = source[ids[i]];

@@ -187,0 +212,0 @@ else if(Array.isArray(conditions[assocIds[i]]))

@@ -63,3 +63,3 @@ module.exports = ChainInstance;

if (i >= instances.length) {
if (typeof cb == "function") {
if (typeof cb === "function") {
cb();

@@ -72,3 +72,3 @@ }

if (err) {
if (typeof cb == "function") {
if (typeof cb === "function") {
cb(err);

@@ -87,3 +87,3 @@ }

if (typeof cb == "function") {
if (typeof cb === "function") {
return calls.forEach(cb);

@@ -90,0 +90,0 @@ }

@@ -26,35 +26,25 @@ var ErrorCodes = require("../../ErrorCodes");

var definitions = [];
var k, i, pending;
var k, i, pending, prop;
var primary_keys = opts.id.map(function (k) { return driver.query.escapeId(k); });
var keys = [];
for (i = 0; i < opts.id.length; i++) {
if (opts.properties.hasOwnProperty(opts.id[i])) continue;
keys.push(driver.query.escapeId(opts.id[i]));
for (k in opts.allProperties) {
prop = opts.allProperties[k];
definitions.push(buildColumnDefinition(driver, k, prop));
}
for (i = 0; i < keys.length; i++) {
definitions.push(keys[i] + " INT(10) UNSIGNED NOT NULL");
}
if (opts.id.length == 1 && !opts.extension) {
definitions[definitions.length - 1] += " AUTO_INCREMENT";
}
for (k in opts.properties) {
definitions.push(buildColumnDefinition(driver, k, opts.properties[k]));
}
for (i = 0; i < opts.one_associations.length; i++) {
if (opts.one_associations[i].extension) continue;
if (opts.one_associations[i].reversed) continue;
for (k in opts.one_associations[i].field) {
definitions.push(buildColumnDefinition(driver, k, opts.one_associations[i].field[k]));
for (k in opts.allProperties) {
prop = opts.allProperties[k];
if (prop.unique === true) {
definitions.push("UNIQUE (" + driver.query.escapeId(k) + ")");
} else if (prop.index) {
definitions.push("INDEX (" + driver.query.escapeId(k) + ")");
}
}
for (k in opts.properties) {
if (opts.properties[k].unique === true) {
for (k in opts.allProperties) {
prop = opts.allProperties[k];
if (prop.unique === true) {
definitions.push("UNIQUE KEY " + driver.query.escapeId(k) + " (" + driver.query.escapeId(k) + ")");
} else if (opts.properties[k].index) {
} else if (prop.index) {
definitions.push("INDEX (" + driver.query.escapeId(k) + ")");

@@ -136,3 +126,4 @@ }

function buildColumnDefinition(driver, name, prop) {
var def;
var def = driver.query.escapeId(name);
var customType;

@@ -142,12 +133,15 @@ switch (prop.type) {

if (prop.big === true) {
def = driver.query.escapeId(name) + " LONGTEXT";
def += " LONGTEXT";
} else {
def = driver.query.escapeId(name) + " VARCHAR(" + Math.min(Math.max(parseInt(prop.size, 10) || 255, 1), 65535) + ")";
def += " VARCHAR(" + Math.min(Math.max(parseInt(prop.size, 10) || 255, 1), 65535) + ")";
}
break;
case "serial":
def += " INT(10) UNSIGNED NOT NULL AUTO_INCREMENT";
break;
case "number":
if (prop.rational === false) {
def = driver.query.escapeId(name) + " " + colTypes.integer[prop.size || 4];
def += " " + colTypes.integer[prop.size || 4];
} else {
def = driver.query.escapeId(name) + " " + colTypes.floating[prop.size || 4];
def += " " + colTypes.floating[prop.size || 4];
}

@@ -159,9 +153,9 @@ if (prop.unsigned === true) {

case "boolean":
def = driver.query.escapeId(name) + " BOOLEAN";
def += " BOOLEAN";
break;
case "date":
if (prop.time === false) {
def = driver.query.escapeId(name) + " DATE";
def += " DATE";
} else {
def = driver.query.escapeId(name) + " DATETIME";
def += " DATETIME";
}

@@ -172,9 +166,9 @@ break;

if (prop.big === true) {
def = driver.query.escapeId(name) + " LONGBLOB";
def += " LONGBLOB";
} else {
def = driver.query.escapeId(name) + " BLOB";
def += " BLOB";
}
break;
case "enum":
def = driver.query.escapeId(name) + " ENUM (" +
def += " ENUM (" +
prop.values.map(driver.query.escapeVal.bind(driver.query)) +

@@ -184,8 +178,13 @@ ")";

case "point":
def = driver.query.escapeId(name) + " POINT";
def += " POINT";
break;
default:
throw ErrorCodes.generateError(ErrorCodes.NO_SUPPORT, "Unknown property type: '" + prop.type + "'", {
property : prop
});
customType = driver.customTypes[prop.type];
if (customType) {
def += " " + customType.datastoreType(prop);
} else {
throw ErrorCodes.generateError(ErrorCodes.NO_SUPPORT, "Unknown property type: '" + prop.type + "'", {
property : prop
});
}
}

@@ -192,0 +191,0 @@ if (prop.required === true) {

@@ -27,26 +27,14 @@ var ErrorCodes = require("../../ErrorCodes");

var definitions = [];
var k, i, pending;
var k, i, pending, prop;
var primary_keys = opts.id.map(function (k) { return driver.query.escapeId(k); });
var keys = [];
for (i = 0; i < opts.id.length; i++) {
if (opts.properties.hasOwnProperty(opts.id[i])) continue;
keys.push(driver.query.escapeId(opts.id[i]));
}
for (k in opts.allProperties) {
prop = opts.allProperties[k];
definitions.push(buildColumnDefinition(driver, opts.table, k, prop));
for (i = 0; i < keys.length; i++) {
definitions.push(keys[i] + " INTEGER NOT NULL");
}
if (opts.id.length == 1 && !opts.extension) {
definitions[definitions.length - 1] = keys[0] + " SERIAL";
}
for (k in opts.properties) {
definitions.push(buildColumnDefinition(driver, opts.table, k, opts.properties[k]));
if (opts.properties[k].type == "enum") {
if (prop.type == "enum") {
typequeries.push(
"CREATE TYPE " + driver.query.escapeId("enum_" + opts.table + "_" + k) + " AS ENUM (" +
opts.properties[k].values.map(driver.query.escapeVal.bind(driver)) + ")"
prop.values.map(driver.query.escapeVal.bind(driver)) + ")"
);

@@ -56,14 +44,7 @@ }

for (i = 0; i < opts.one_associations.length; i++) {
if (opts.one_associations[i].extension) continue;
if (opts.one_associations[i].reversed) continue;
for (k in opts.one_associations[i].field) {
definitions.push(buildColumnDefinition(driver, opts.table, k, opts.one_associations[i].field[k]));
}
}
for (k in opts.properties) {
if (opts.properties[k].unique === true) {
for (k in opts.allProperties) {
prop = opts.allProperties[k];
if (prop.unique === true) {
definitions.push("UNIQUE (" + driver.query.escapeId(k) + ")");
} else if (opts.properties[k].index) {
} else if (prop.index) {
definitions.push("INDEX (" + driver.query.escapeId(k) + ")");

@@ -82,3 +63,3 @@ }

});
for (i = 0; i < opts.one_associations.length; i++) {

@@ -107,3 +88,3 @@ if (opts.one_associations[i].extension) continue;

typequeries = [];
for (k in opts.many_associations[i].mergeId) {

@@ -127,3 +108,3 @@ definitions.push(buildColumnDefinition(driver, opts.many_associations[i].mergeTable, k, opts.many_associations[i].mergeId[k]));

}
var index = null;

@@ -205,3 +186,4 @@ for (k in opts.many_associations[i].mergeId) {

function buildColumnDefinition(driver, table, name, prop) {
var def;
var def = driver.query.escapeId(name);
var customType;

@@ -211,22 +193,25 @@ switch (prop.type) {

if (prop.big === true) {
def = driver.query.escapeId(name) + " TEXT";
def += " TEXT";
} else {
def = driver.query.escapeId(name) + " VARCHAR(" + Math.min(Math.max(parseInt(prop.size, 10) || 255, 1), 65535) + ")";
def += " VARCHAR(" + Math.max(parseInt(prop.size, 10) || 255, 1) + ")";
}
break;
case "serial":
def += " SERIAL";
break;
case "number":
if (prop.rational === false) {
def = driver.query.escapeId(name) + " " + colTypes.integer[prop.size || 4];
def += " " + colTypes.integer[prop.size || 4];
} else {
def = driver.query.escapeId(name) + " " + colTypes.floating[prop.size || 4];
def += " " + colTypes.floating[prop.size || 4];
}
break;
case "boolean":
def = driver.query.escapeId(name) + " BOOLEAN";
def += " BOOLEAN";
break;
case "date":
if (prop.time === false) {
def = driver.query.escapeId(name) + " DATE";
def += " DATE";
} else {
def = driver.query.escapeId(name) + " TIMESTAMP WITHOUT TIME ZONE";
def += " TIMESTAMP WITHOUT TIME ZONE";
}

@@ -236,14 +221,19 @@ break;

case "object":
def = driver.query.escapeId(name) + " BYTEA";
def += " BYTEA";
break;
case "enum":
def = driver.query.escapeId(name) + " " + driver.query.escapeId("enum_" + table + "_" + name);
def += " " + driver.query.escapeId("enum_" + table + "_" + name);
break;
case "point":
def = driver.query.escapeId(name) + " POINT";
def += " POINT";
break;
default:
throw ErrorCodes.generateError(ErrorCodes.NO_SUPPORT, "Unknown property type: '" + prop.type + "'", {
property : prop
});
customType = driver.customTypes[prop.type];
if (customType) {
def += " " + customType.datastoreType(prop);
} else {
throw ErrorCodes.generateError(ErrorCodes.NO_SUPPORT, "Unknown property type: '" + prop.type + "'", {
property : prop
});
}
}

@@ -250,0 +240,0 @@ if (prop.required === true) {

@@ -25,32 +25,11 @@ var ErrorCodes = require("../../ErrorCodes");

var definitions = [];
var k, i, pending;
var k, i, pending, prop;
var primary_keys = opts.id.map(function (k) { return driver.query.escapeId(k); });
var keys = [];
for (i = 0; i < opts.id.length; i++) {
if (opts.properties.hasOwnProperty(opts.id[i])) continue;
keys.push(driver.query.escapeId(opts.id[i]));
for (k in opts.allProperties) {
prop = opts.allProperties[k];
definitions.push(buildColumnDefinition(driver, k, prop));
}
for (i = 0; i < keys.length; i++) {
definitions.push(keys[i] + " INTEGER UNSIGNED NOT NULL");
}
if (opts.id.length == 1 && !opts.extension) {
definitions[definitions.length - 1] = keys[0] + " INTEGER PRIMARY KEY AUTOINCREMENT";
}
for (k in opts.properties) {
definitions.push(buildColumnDefinition(driver, k, opts.properties[k]));
}
for (i = 0; i < opts.one_associations.length; i++) {
if (opts.one_associations[i].extension) continue;
if (opts.one_associations[i].reversed) continue;
for (k in opts.one_associations[i].field) {
definitions.push(buildColumnDefinition(driver, k, opts.one_associations[i].field[k]));
}
}
if (keys.length > 1) {

@@ -79,3 +58,3 @@ definitions.push("PRIMARY KEY (" + primary_keys.join(", ") + ")");

}
for (i = 0; i < opts.one_associations.length; i++) {

@@ -117,3 +96,3 @@ if (opts.one_associations[i].extension) continue;

}
var index = null;

@@ -153,32 +132,41 @@ for (k in opts.many_associations[i].mergeId) {

function buildColumnDefinition(driver, name, prop) {
var def;
var def = driver.query.escapeId(name);
var customType;
switch (prop.type) {
case "text":
def = driver.query.escapeId(name) + " TEXT";
def += " TEXT";
break;
case "serial":
def += " INTEGER PRIMARY KEY AUTOINCREMENT";
break;
case "number":
if (prop.rational === false) {
def = driver.query.escapeId(name) + " INTEGER";
def += " INTEGER";
} else {
def = driver.query.escapeId(name) + " REAL";
def += " REAL";
}
break;
case "boolean":
def = driver.query.escapeId(name) + " INTEGER UNSIGNED";
def += " INTEGER UNSIGNED";
break;
case "date":
def = driver.query.escapeId(name) + " DATETIME";
def += " DATETIME";
break;
case "binary":
case "object":
def = driver.query.escapeId(name) + " BLOB";
def += " BLOB";
break;
case "enum":
def = driver.query.escapeId(name) + " INTEGER";
def += " INTEGER";
break;
default:
throw ErrorCodes.generateError(ErrorCodes.NO_SUPPORT, "Unknown property type: '" + prop.type + "'", {
property : prop
});
customType = driver.customTypes[prop.type];
if (customType) {
def += " " + customType.datastoreType(prop);
} else {
throw ErrorCodes.generateError(ErrorCodes.NO_SUPPORT, "Unknown property type: '" + prop.type + "'", {
property : prop
});
}
}

@@ -185,0 +173,0 @@ if (prop.required === true) {

@@ -1,4 +0,5 @@

var mongodb = require("mongodb");
var util = require("../../Utilities");
var _ = require('lodash');
var Utilities = require("../../Utilities");
var mongodb = require("mongodb");
var util = require("util");
var _ = require('lodash');

@@ -13,2 +14,6 @@ exports.Driver = Driver;

if (!this.config.timezone) {
this.config.timezone = "local";
}
this.opts.settings.set("properties.primary_key", "_id");

@@ -104,3 +109,3 @@ this.opts.settings.set("properties.association_key", function (name, field) {

convertToDB(conditions);
convertToDB(conditions, this.config.timezone);

@@ -133,3 +138,3 @@ var cursor = (fields ? collection.find(conditions, fields) : collection.find(conditions));

for (var i = 0; i < docs.length; i++) {
convertFromDB(docs[i]);
convertFromDB(docs[i], this.config.timezone);
if (opts.extra && opts.extra[docs[i]._id]) {

@@ -154,3 +159,3 @@ docs[i] = _.merge(docs[i], _.omit(opts.extra[docs[i]._id], '_id'));

}
});
}.bind(this));
};

@@ -161,3 +166,3 @@

convertToDB(conditions);
convertToDB(conditions, this.config.timezone);

@@ -189,3 +194,3 @@ var cursor = collection.find(conditions);

Driver.prototype.insert = function (table, data, id_prop, cb) {
convertToDB(data);
convertToDB(data, this.config.timezone);

@@ -208,7 +213,7 @@ return this.db.collection(table).insert(

}
convertFromDB(ids);
convertFromDB(ids, this.config.timezone);
}
return cb(null, ids);
}
}.bind(this)
);

@@ -269,3 +274,3 @@ };

options.order[0] = options.order[0][1];
options.order = util.standardizeOrder(options.order);
options.order = Utilities.standardizeOrder(options.order);
}

@@ -331,4 +336,4 @@

Driver.prototype.update = function (table, changes, conditions, cb) {
convertToDB(changes);
convertToDB(conditions);
convertToDB(changes, this.config.timezone);
convertToDB(conditions, this.config.timezone);

@@ -349,3 +354,3 @@ return this.db.collection(table).update(

Driver.prototype.remove = function (table, conditions, cb) {
convertToDB(conditions);
convertToDB(conditions, this.config.timezone);

@@ -359,7 +364,7 @@ return this.db.collection(table).remove(conditions, cb);

function convertToDB(obj) {
function convertToDB(obj, timeZone) {
for (var k in obj) {
if (Array.isArray(obj[k])) {
for (var i = 0; i < obj[k].length; i++) {
obj[k][i] = convertToDBVal(k, obj[k][i]);
obj[k][i] = convertToDBVal(k, obj[k][i], timeZone);
}

@@ -371,12 +376,15 @@

obj[k] = convertToDBVal(k, obj[k]);
obj[k] = convertToDBVal(k, obj[k], timeZone);
}
}
function convertFromDB(obj) {
function convertFromDB(obj, timezone) {
for (var k in obj) {
if (obj[k] instanceof mongodb.ObjectID) {
obj[k] = obj[k].toString();
} else if (obj[k] instanceof mongodb.Binary) {
continue;
}
if (obj[k] instanceof mongodb.Binary) {
obj[k] = new Buffer(obj[k].value(), "binary");
continue;
}

@@ -386,3 +394,3 @@ }

function convertToDBVal(key, value) {
function convertToDBVal(key, value, timezone) {
if (value && typeof value.sql_comparator == "function") {

@@ -426,1 +434,5 @@ var val = (key != "_id" ? value.val : new mongodb.ObjectID(value.val));

}
Object.defineProperty(Driver.prototype, "isSql", {
value: false
});

@@ -1,3 +0,5 @@

var mysql = require("mysql");
var Query = require("sql-query").Query;
var _ = require("lodash");
var mysql = require("mysql");
var Query = require("sql-query").Query;
var helpers = require("../helpers");

@@ -9,8 +11,8 @@ exports.Driver = Driver;

this.opts = opts || {};
this.query = new Query("mysql");
this.customTypes = {};
if (!this.config.timezone) {
// force UTC if not defined, UTC is always better..
this.config.timezone = "Z";
this.config.timezone = "local";
}
this.query = new Query({ dialect: "mysql", timezone: config.timezone });

@@ -25,5 +27,7 @@ this.reconnect(null, connection);

"SUM", "COUNT",
"DISTINCT" ];
"DISTINCT"];
}
_.extend(Driver.prototype, helpers.sql);
Driver.prototype.sync = function (opts, cb) {

@@ -54,3 +58,7 @@ return require("../DDL/mysql").sync(this, opts, cb);

if (!err) {
con.end();
if (con.release) {
con.release();
} else {
con.end();
}
}

@@ -85,3 +93,3 @@ return cb(err);

Driver.prototype.execQuery = function (query, cb) {
Driver.prototype.execSimpleQuery = function (query, cb) {
if (this.opts.debug) {

@@ -135,3 +143,3 @@ require("../../Debug").sql('mysql', query);

this.execQuery(q, cb);
this.execSimpleQuery(q, cb);
};

@@ -163,3 +171,3 @@

this.execQuery(q, cb);
this.execSimpleQuery(q, cb);
};

@@ -173,3 +181,3 @@

this.execQuery(q, function (err, info) {
this.execSimpleQuery(q, function (err, info) {
if (err) return cb(err);

@@ -199,3 +207,3 @@

this.execQuery(q, cb);
this.execSimpleQuery(q, cb);
};

@@ -209,3 +217,3 @@

this.execQuery(q, cb);
this.execSimpleQuery(q, cb);
};

@@ -216,3 +224,3 @@

this.execQuery(q, cb);
this.execSimpleQuery(q, cb);
};

@@ -227,3 +235,7 @@

con.query(query, function (err, data) {
con.end();
if (con.release) {
con.release();
} else {
con.end();
}

@@ -236,31 +248,51 @@ return cb(err, data);

Driver.prototype.valueToProperty = function (value, property) {
var customType;
switch (property.type) {
case "boolean":
return !!value;
value = !!value;
break;
case "object":
if (typeof value == "object" && !Buffer.isBuffer(value)) {
return value;
break;
}
try {
return JSON.parse(value);
value = JSON.parse(value);
} catch (e) {
return null;
value = null;
}
break;
default:
return value;
customType = this.customTypes[property.type];
if(customType && 'valueToProperty' in customType) {
value = customType.valueToProperty(value);
}
}
return value;
};
Driver.prototype.propertyToValue = function (value, property) {
var customType;
switch (property.type) {
case "boolean":
return (value) ? 1 : 0;
value = (value) ? 1 : 0;
break;
case "object":
return JSON.stringify(value);
value = JSON.stringify(value);
break;
case "point":
return function() { return 'POINT(' + value.x + ', ' + value.y + ')'; };
break;
default:
return value;
customType = this.customTypes[property.type];
if(customType && 'propertyToValue' in customType) {
value = customType.propertyToValue(value);
}
}
return value;
};
Object.defineProperty(Driver.prototype, "isSql", {
value: true
});

@@ -1,3 +0,5 @@

var pg = require("pg");
var Query = require("sql-query").Query;
var _ = require("lodash");
var pg = require("pg");
var Query = require("sql-query").Query;
var helpers = require("../helpers");

@@ -10,12 +12,19 @@ exports.Driver = Driver;

this.opts = opts || {};
this.query = new Query("postgresql");
if (!this.config.timezone) {
this.config.timezone = "local";
}
this.query = new Query({ dialect: "postgresql", timezone: this.config.timezone });
this.customTypes = {};
if (connection) {
this.db = connection;
} else {
if (config.query && config.query.ssl) {
if (this.config.query && this.config.query.ssl) {
config.ssl = true;
this.config = config;
} else {
this.config = config.href || config;
this.config = _.extend(this.config, config);
// } else {
// this.config = _.extend(this.config, config);
// this.config = config.href || config;
}

@@ -31,5 +40,3 @@

for (var name in functions) {
this.constructor.prototype[name] = functions[name];
}
_.extend(this.constructor.prototype, functions);

@@ -53,3 +60,3 @@ this.aggregate_functions = [ "ABS", "CEIL", "FLOOR", "ROUND",

},
execQuery: function (query, cb) {
execSimpleQuery: function (query, cb) {
if (this.opts.debug) {

@@ -83,3 +90,3 @@ require("../../Debug").sql('postgres', query);

},
execQuery: function (query, cb) {
execSimpleQuery: function (query, cb) {
if (this.opts.debug) {

@@ -106,2 +113,4 @@ require("../../Debug").sql('postgres', query);

_.extend(Driver.prototype, helpers.sql);
Driver.prototype.sync = function (opts, cb) {

@@ -116,3 +125,3 @@ return require("../DDL/postgres").sync(this, opts, cb);

Driver.prototype.ping = function (cb) {
this.execQuery("SELECT * FROM pg_stat_activity LIMIT 1", function () {
this.execSimpleQuery("SELECT * FROM pg_stat_activity LIMIT 1", function () {
return cb();

@@ -124,3 +133,7 @@ });

Driver.prototype.close = function (cb) {
this.db.end(cb);
this.db.end();
if (typeof cb == "function") cb();
return;
};

@@ -167,3 +180,3 @@

this.execQuery(q, cb);
this.execSimpleQuery(q, cb);
};

@@ -195,3 +208,3 @@

this.execQuery(q, cb);
this.execSimpleQuery(q, cb);
};

@@ -205,3 +218,3 @@

this.execQuery(q + " RETURNING *", function (err, results) {
this.execSimpleQuery(q + " RETURNING *", function (err, results) {
if (err) {

@@ -230,3 +243,3 @@ return cb(err);

this.execQuery(q, cb);
this.execSimpleQuery(q, cb);
};

@@ -240,3 +253,3 @@

this.execQuery(q, cb);
this.execSimpleQuery(q, cb);
};

@@ -247,15 +260,17 @@

this.execQuery(q, cb);
this.execSimpleQuery(q, cb);
};
Driver.prototype.valueToProperty = function (value, property) {
var customType, v;
switch (property.type) {
case "object":
if (typeof value == "object" && !Buffer.isBuffer(value)) {
return value;
break;
}
try {
return JSON.parse(value);
value = JSON.parse(value);
} catch (e) {
return null;
value = null;
}

@@ -267,19 +282,52 @@ break;

if (m) {
return { x : parseFloat(m[1], 10) , y : parseFloat(m[2], 10) };
value = { x : parseFloat(m[1], 10) , y : parseFloat(m[2], 10) };
}
}
return value;
break;
case "date":
if (this.config.timezone && this.config.timezone != 'local') {
var tz = convertTimezone(this.config.timezone);
// shift local to UTC
value.setTime(value.getTime() - (value.getTimezoneOffset() * 60000));
if (tz !== false) {
// shift UTC to timezone
value.setTime(value.getTime() - (tz * 60000));
}
}
break;
case "number":
if (value !== null) {
return Number(value);
if (typeof value != 'number' && value !== null) {
v = Number(value);
if (!isNaN(v)) value = v;
}
break;
default:
return value;
customType = this.customTypes[property.type];
if(customType && 'valueToProperty' in customType) {
value = customType.valueToProperty(value);
}
}
return value;
};
Driver.prototype.propertyToValue = function (value, property) {
var customType;
switch (property.type) {
case "object":
return JSON.stringify(value);
value = JSON.stringify(value);
break;
case "date":
if (this.config.timezone && this.config.timezone != 'local') {
var tz = convertTimezone(this.config.timezone);
// shift local to UTC
value.setTime(value.getTime() + (value.getTimezoneOffset() * 60000));
if (tz !== false) {
// shift UTC to timezone
value.setTime(value.getTime() + (tz * 60000));
}
}
break;
case "point":

@@ -289,5 +337,24 @@ return function () {

};
break;
default:
return value;
customType = this.customTypes[property.type];
if(customType && 'propertyToValue' in customType) {
value = customType.propertyToValue(value);
}
}
return value;
};
Object.defineProperty(Driver.prototype, "isSql", {
value: true
});
function convertTimezone(tz) {
if (tz == "Z") return 0;
var m = tz.match(/([\+\-\s])(\d\d):?(\d\d)?/);
if (m) {
return (m[1] == '-' ? -1 : 1) * (parseInt(m[2], 10) + ((m[3] ? parseInt(m[3], 10) : 0) / 60)) * 60;
}
return false;
}

@@ -0,3 +1,6 @@

var _ = require("lodash");
var util = require("util");
var sqlite3 = require("sqlite3");
var Query = require("sql-query").Query;
var helpers = require("../helpers");

@@ -9,4 +12,10 @@ exports.Driver = Driver;

this.opts = opts || {};
this.query = new Query("sqlite");
if (!this.config.timezone) {
this.config.timezone = "local";
}
this.query = new Query({ dialect: "sqlite", timezone: this.config.timezone });
this.customTypes = {};
if (connection) {

@@ -32,2 +41,4 @@ this.db = connection;

_.extend(Driver.prototype, helpers.sql);
Driver.prototype.sync = function (opts, cb) {

@@ -66,3 +77,3 @@ return require("../DDL/sqlite").sync(this, opts, cb);

Driver.prototype.execQuery = function (query, cb) {
Driver.prototype.execSimpleQuery = function (query, cb) {
if (this.opts.debug) {

@@ -207,33 +218,65 @@ require("../../Debug").sql('mysql', query);

Driver.prototype.valueToProperty = function (value, property) {
var v, customType;
switch (property.type) {
case "boolean":
return !!value;
value = !!value;
break;
case "object":
if (typeof value == "object" && !Buffer.isBuffer(value)) {
return value;
break;
}
try {
return JSON.parse(value);
value = JSON.parse(value);
} catch (e) {
return null;
value = null;
}
break;
case "number":
if (typeof value != 'number' && value !== null) {
v = Number(value);
if (!isNaN(v)) {
value = v;
}
}
break;
case "date":
if (typeof value === 'string') {
if (typeof value == 'string') {
if (value.indexOf('Z', value.length - 1) === -1) {
return new Date(value + 'Z');
value = new Date(value + 'Z');
} else {
value = new Date(value);
}
if (this.config.timezone && this.config.timezone != 'local') {
var tz = convertTimezone(this.config.timezone);
// shift local to UTC
value.setTime(value.getTime() - (value.getTimezoneOffset() * 60000));
if (tz !== false) {
// shift UTC to timezone
value.setTime(value.getTime() - (tz * 60000));
}
}
}
return new Date(value);
break;
default:
return value;
customType = this.customTypes[property.type];
if(customType && 'valueToProperty' in customType) {
value = customType.valueToProperty(value);
}
}
return value;
};
Driver.prototype.propertyToValue = function (value, property) {
var customType;
switch (property.type) {
case "boolean":
return (value) ? 1 : 0;
value = (value) ? 1 : 0;
break;
case "object":
return JSON.stringify(value);
value = JSON.stringify(value);
break;
case "date":

@@ -253,3 +296,4 @@ if (this.config.query && this.config.query.strdates) {

if (property.time === false) {
return strdate;
value = strdate;
break;
}

@@ -277,10 +321,27 @@

strdate += ' ' + hours + ':' + minutes + ':' + seconds + '.' + millis + '000';
return strdate;
value = strdate;
}
}
return value;
break;
default:
return value;
customType = this.customTypes[property.type];
if(customType && 'propertyToValue' in customType) {
value = customType.propertyToValue(value);
}
}
return value;
};
Object.defineProperty(Driver.prototype, "isSql", {
value: true
});
function convertTimezone(tz) {
if (tz == "Z") return 0;
var m = tz.match(/([\+\-\s])(\d\d):?(\d\d)?/);
if (m) {
return (m[1] == '-' ? -1 : 1) * (parseInt(m[2], 10) + ((m[3] ? parseInt(m[3], 10) : 0) / 60)) * 60;
}
return false;
}

@@ -6,6 +6,14 @@ exports.QUERY_ERROR = 1;

exports.MISSING_CALLBACK = 5;
exports.PARAM_MISSMATCH = 6;
exports.PARAM_MISMATCH = 6;
exports.CONNECTION_LOST = 10;
// Deprecated, remove on next major release.
Object.defineProperty(exports, "PARAM_MISSMATCH", {
enumerable: true, get: function () {
console.log("PARAM_MISSMATCH spelling is deprecated. Use PARAM_MISMATCH instead");
return exports.PARAM_MISMATCH;
}
});
Object.defineProperty(exports, "generateError", {

@@ -12,0 +20,0 @@ value: function (code, message, extra) {

@@ -13,6 +13,4 @@ var orm = require("./ORM");

orm.connect(uri, function (err, db) {
_pending -= 1;
if (err) {
if (typeof opts.error == "function") {
if (typeof opts.error === "function") {
opts.error(err);

@@ -33,3 +31,10 @@ } else {

}
if (typeof opts.define == "function") {
if (typeof opts.define === "function") {
if (opts.define.length > 2) {
return opts.define(db, _models, function () {
return checkRequestQueue();
});
}
opts.define(db, _models);

@@ -41,3 +46,3 @@ }

return function ORM(req, res, next) {
return function ORM_ExpressMiddleware(req, res, next) {
if (!req.hasOwnProperty("models")) {

@@ -58,2 +63,4 @@ req.models = _models;

function checkRequestQueue() {
_pending -= 1;
if (_pending > 0) return;

@@ -60,0 +67,0 @@ if (_queue.length === 0) return;

@@ -6,3 +6,3 @@ exports.trigger = function () {

if (typeof cb == "function") {
if (typeof cb === "function") {
cb.apply(self, args);

@@ -20,3 +20,3 @@ }

if (typeof cb == "function") {
if (typeof cb === "function") {
cb.apply(self, args);

@@ -23,0 +23,0 @@

@@ -31,2 +31,3 @@ var Property = require("./Property");

Hook.wait(instance, opts.hooks.beforeValidation, function (err) {
var k, i;
if (err) {

@@ -36,12 +37,6 @@ return saveError(cb, err);

for (var k in Model.properties) {
if (!Model.properties.hasOwnProperty(k)) continue;
if (opts.data[k] == null && Model.properties[k].hasOwnProperty("defaultValue")) {
opts.data[k] = Model.properties[k].defaultValue;
}
}
for (var i = 0; i < opts.one_associations.length; i++) {
for (i = 0; i < opts.one_associations.length; i++) {
for (k in opts.one_associations[i].field) {
if (opts.one_associations[i].required && opts.data[k] == null) {
var err = new Error("Property required");
if (opts.one_associations[i].required && opts.data[k] === null) {
var err = new Error("Property required");

@@ -52,2 +47,3 @@ err.field = k;

err.type = "validation";
err.model = Model.table;

@@ -67,8 +63,10 @@ if (!Model.settings.get("instance.returnAllErrors")) {

for (var k in opts.validations) {
for (k in opts.validations) {
required = false;
if (Model.properties[k]) {
required = Model.properties[k].required;
} else {
for (var i = 0; i < opts.one_associations.length; i++) {
if (opts.one_associations[i].field == k) {
for (i = 0; i < opts.one_associations.length; i++) {
if (opts.one_associations[i].field === k) {
required = opts.one_associations[i].required;

@@ -82,3 +80,3 @@ break;

}
for (var i = 0; i < opts.validations[k].length; i++) {
for (i = 0; i < opts.validations[k].length; i++) {
checks.add(k, opts.validations[k][i]);

@@ -88,3 +86,5 @@ }

checks.context("instance", instance);
checks.context("model", Model);
checks.context("driver", opts.driver);

@@ -97,3 +97,3 @@ return checks.check(instance, cb);

Hook.trigger(instance, opts.hooks.afterSave, false);
if (typeof cb == "function") {
if (typeof cb === "function") {
cb(err, instance);

@@ -123,5 +123,8 @@ }

if (opts.changes.length === 0) {
return saveAssociations(function (err) {
return afterSave(cb, false, err);
});
if (saveOptions.saveAssociations === false) {
return saveInstanceExtra(cb);
}
return saveAssociations(function (err) {
return afterSave(cb, false, err);
});
}

@@ -147,10 +150,13 @@

var getInstanceData = function () {
var data = {};
var data = {}, prop;
for (var k in opts.data) {
if (!opts.data.hasOwnProperty(k)) continue;
prop = Model.allProperties[k];
if (Model.properties[k]) {
data[k] = Property.validate(opts.data[k], Model.properties[k]);
if (prop) {
if (prop.type === 'serial' && opts.data[k] == null) continue;
data[k] = Property.validate(opts.data[k], prop);
if (opts.driver.propertyToValue) {
data[k] = opts.driver.propertyToValue(data[k], Model.properties[k]);
data[k] = opts.driver.propertyToValue(data[k], prop);
}

@@ -161,2 +167,3 @@ } else {

}
return data;

@@ -227,3 +234,3 @@ };

var saveAssociations = function (cb) {
var pending = 0, errored = false, i, j;
var pending = 1, errored = false, i, j;
var saveAssociation = function (accessor, instances) {

@@ -246,44 +253,49 @@ pending += 1;

var _saveOneAssociation = function (assoc) {
if (!instance[assoc.name] || typeof instance[assoc.name] !== "object") return;
if (assoc.reversed) {
// reversed hasOne associations should behave like hasMany
if (!Array.isArray(instance[assoc.name])) {
instance[assoc.name] = [ instance[assoc.name] ];
}
for (var i = 0; i < instance[assoc.name].length; i++) {
if (!instance[assoc.name][i].isInstance) {
instance[assoc.name][i] = new assoc.model(instance[assoc.name][i]);
}
saveAssociation(assoc.setAccessor, instance[assoc.name][i]);
}
return;
}
if (!instance[assoc.name].isInstance) {
instance[assoc.name] = new assoc.model(instance[assoc.name]);
}
saveAssociation(assoc.setAccessor, instance[assoc.name]);
};
for (i = 0; i < opts.one_associations.length; i++) {
(function (assoc) {
if (!instance[assoc.name] || typeof instance[assoc.name] != "object") return;
if (assoc.reversed) {
// reversed hasOne associations should behave like hasMany
if (!Array.isArray(instance[assoc.name])) {
instance[assoc.name] = [ instance[assoc.name] ];
}
for (var i = 0; i < instance[assoc.name].length; i++) {
if (!instance[assoc.name][i].isInstance) {
instance[assoc.name][i] = new assoc.model(instance[assoc.name][i]);
}
saveAssociation(assoc.setAccessor, instance[assoc.name][i]);
}
return;
}
if (!instance[assoc.name].isInstance) {
instance[assoc.name] = new assoc.model(instance[assoc.name]);
}
saveAssociation(assoc.setAccessor, instance[assoc.name]);
})(opts.one_associations[i]);
_saveOneAssociation(opts.one_associations[i]);
}
for (i = 0; i < opts.many_associations.length; i++) {
(function (assoc) {
if (!instance.hasOwnProperty(assoc.name)) return;
if (!Array.isArray(instance[assoc.name])) {
instance[assoc.name] = [ instance[assoc.name] ];
}
for (j = 0; j < instance[assoc.name].length; j++) {
if (!instance[assoc.name][j].isInstance) {
instance[assoc.name][j] = new assoc.model(instance[assoc.name][j]);
}
}
var _saveManyAssociation = function (assoc) {
if (!instance.hasOwnProperty(assoc.name)) return;
if (!Array.isArray(instance[assoc.name])) {
instance[assoc.name] = [ instance[assoc.name] ];
}
return saveAssociation(assoc.setAccessor, instance[assoc.name]);
})(opts.many_associations[i]);
for (j = 0; j < instance[assoc.name].length; j++) {
if (!instance[assoc.name][j].isInstance) {
instance[assoc.name][j] = new assoc.model(instance[assoc.name][j]);
}
}
return saveAssociation(assoc.setAccessor, instance[assoc.name]);
};
for (i = 0; i < opts.many_associations.length; i++) {
_saveManyAssociation(opts.many_associations[i]);
}
if (pending === 0) {
if (--pending === 0) {
return cb();

@@ -314,3 +326,3 @@ }

for (i = 0; i < opts.extra_info.id; i++) {
for (i = 0; i < opts.extra_info.id.length; i++) {
conditions[opts.extra_info.id_prop[i]] = opts.extra_info.id[i];

@@ -337,3 +349,3 @@ conditions[opts.extra_info.assoc_prop[i]] = opts.data[opts.id[i]];

emitEvent("remove", err, instance);
if (typeof cb == "function") {
if (typeof cb === "function") {
cb(err, instance);

@@ -351,3 +363,3 @@ }

if (typeof cb == "function") {
if (typeof cb === "function") {
cb(err, instance);

@@ -391,5 +403,34 @@ }

};
var setInstanceProperty = function (key, value) {
var prop = Model.allProperties[key] || opts.extra[key];
if (prop) {
if ('valueToProperty' in opts.driver) {
value = opts.driver.valueToProperty(value, prop);
}
if (opts.data[key] !== value) {
opts.data[key] = value;
return true;
}
}
return false;
}
var addInstanceProperty = function (key) {
// if (instance.hasOwnProperty(key)) return;
var defaultValue = null;
var prop = Model.allProperties[key];
// This code was first added, and then commented out in a later commit.
// Its presence doesn't affect tests, so I'm just gonna log if it ever gets called.
// If someone complains about noise, we know it does something, and figure it out then.
if (instance.hasOwnProperty(key)) console.log("Overwriting instance property");
if (key in opts.data) {
defaultValue = opts.data[key];
} else if (prop && 'defaultValue' in prop) {
defaultValue = prop.defaultValue;
}
setInstanceProperty(key, defaultValue);
Object.defineProperty(instance, key, {

@@ -400,12 +441,13 @@ get: function () {

set: function (val) {
if (opts.id.indexOf(key) >= 0 && opts.data.hasOwnProperty(key)) {
return;
if (Model.allProperties[key].key === true && opts.data[key] != null) {
return;
}
if (opts.data[key] === val) return;
opts.data[key] = val;
if (!setInstanceProperty(key, val)) {
return;
}
if (opts.autoSave) {
saveInstanceProperty(key, val);
} else if (opts.changes.indexOf(key) == -1) {
} else if (opts.changes.indexOf(key) === -1) {
opts.changes.push(key);

@@ -426,7 +468,7 @@ }

set: function (val) {
opts.data[key] = val;
setInstanceProperty(key, val);
/*if (opts.autoSave) {
saveInstanceProperty(key, val);
}*/if (opts.extrachanges.indexOf(key) == -1) {
}*/if (opts.extrachanges.indexOf(key) === -1) {
opts.extrachanges.push(key);

@@ -439,30 +481,8 @@ }

for (var i = 0; i < opts.id.length; i++) {
if (!opts.data.hasOwnProperty(opts.id[i])) {
addInstanceProperty(opts.id[i]);
}
}
var i, k;
for (var k in Model.properties) {
if (Model.properties.hasOwnProperty(k) && !opts.data.hasOwnProperty(k) && opts.id.indexOf(k) == -1) {
opts.data[k] = null;
}
for (k in Model.allProperties) {
addInstanceProperty(k);
}
for (k in opts.data) {
if (!opts.data.hasOwnProperty(k)) continue;
if (!Model.properties.hasOwnProperty(k) && opts.id.indexOf(k) == -1 && opts.association_properties.indexOf(k) == -1) {
if (!opts.extra.hasOwnProperty(k)) continue;
if (opts.driver.valueToProperty) {
opts.data[k] = opts.driver.valueToProperty(opts.data[k], opts.extra[k]);
}
addInstanceExtraProperty(k);
continue;
}
if (Model.properties[k] && opts.driver.valueToProperty) {
opts.data[k] = opts.driver.valueToProperty(opts.data[k], Model.properties[k]);
}
for (k in opts.extra) {
addInstanceProperty(k);

@@ -478,2 +498,6 @@ }

for (k in opts.extra) {
addInstanceExtraProperty(k);
}
Object.defineProperty(instance, "on", {

@@ -513,3 +537,5 @@ value: function (event, cb) {

default:
throw new Error("Unknown parameter type '" + (typeof arg) + "' in Instance.save()");
var err = new Error("Unknown parameter type '" + (typeof arg) + "' in Instance.save()");
err.model = Model.table;
throw err;
}

@@ -574,4 +600,10 @@ }

});
Object.defineProperty(instance, "model", {
value: function (cb) {
return Model;
},
enumerable: false
});
for (var i = 0; i < opts.id.length; i++) {
for (i = 0; i < opts.id.length; i++) {
if (!opts.data.hasOwnProperty(opts.id[i])) {

@@ -583,3 +615,3 @@ opts.changes = Object.keys(opts.data);

for (i = 0; i < opts.one_associations.length; i++) {
var asc = opts.one_associations[i]
var asc = opts.one_associations[i];

@@ -623,5 +655,5 @@ if (!asc.reversed && !asc.extension) {

Hook.wait(instance, opts.hooks.afterLoad, function () {
Hook.wait(instance, opts.hooks.afterLoad, function (err) {
process.nextTick(function () {
emitEvent("ready");
emitEvent("ready", err);
});

@@ -628,0 +660,0 @@ });

@@ -28,3 +28,3 @@ var ChainFind = require("./ChainFind");

if (!Array.isArray(opts.id)) {
opts.id = [opts.id];
opts.id = [ opts.id ];
}

@@ -37,10 +37,7 @@

var model_fields = [];
var allProperties = {};
for (var i = 0; i < opts.id.length; i++) {
model_fields.push(opts.id[i]);
}
var createHookHelper = function (hook) {
return function (cb) {
if (typeof cb != "function") {
if (typeof cb !== "function") {
delete opts.hooks[hook];

@@ -61,3 +58,3 @@ } else {

for (k in data) {
if (k == "extra_field") continue;
if (k === "extra_field") continue;
if (opts.properties.hasOwnProperty(k)) continue;

@@ -69,3 +66,3 @@ if (inst_opts.extra && inst_opts.extra.hasOwnProperty(k)) continue;

for (i = 0; i < one_associations.length; i++) {
if (one_associations[i].name == k) {
if (one_associations[i].name === k) {
found_assoc = true;

@@ -77,3 +74,3 @@ break;

for (i = 0; i < many_associations.length; i++) {
if (many_associations[i].name == k) {
if (many_associations[i].name === k) {
found_assoc = true;

@@ -114,6 +111,6 @@ break;

});
instance.on("ready", function () {
instance.on("ready", function (err) {
if (--pending > 0) return;
if (typeof cb == "function") {
return cb(instance);
if (typeof cb === "function") {
return cb(err, instance);
}

@@ -133,4 +130,4 @@ });

if (--pending > 0) return;
if (typeof cb == "function") {
return cb(instance);
if (typeof cb === "function") {
return cb(err, instance);
}

@@ -144,59 +141,55 @@ });

var model = function (data) {
var instance;
var model = function () {
var instance, i;
//TODO: Should be smarter about how this is handled.
// ideally we should check the type of ID used
// by the model, and accept that type of data.
if (typeof data == "number" || typeof data == "string") {
var data2 = {};
data2[opts.id] = data;
var data = arguments.length > 1 ? arguments : arguments[0];
return createInstance(data2, { isShell: true });
} else if (typeof data == "undefined") {
data = {};
}
if (Array.isArray(opts.id) && Array.isArray(data)) {
if (data.length == opts.id.length) {
var data2 = {};
for (i = 0; i < opts.id.length; i++) {
data2[opts.id[i]] = data[i++];
}
var isNew = false;
return createInstance(data2, { isShell: true });
}
else {
var err = new Error('Model requires ' + opts.id.length + ' keys, only ' + data.length + ' were provided');
err.model = opts.table;
for (var k in opts.id) {
isNew |= !data.hasOwnProperty(k);
}
throw err;
}
}
else if (typeof data === "number" || typeof data === "string") {
var data2 = {};
data2[opts.id[0]] = data;
return createInstance(data, {
is_new : isNew,
autoSave : opts.autoSave,
cascadeRemove : opts.cascadeRemove
});
};
return createInstance(data2, { isShell: true });
} else if (typeof data === "undefined") {
data = {};
}
// Standardize validations
for (var k in opts.validations) {
if (typeof opts.validations[k] == 'function') {
opts.validations[k] = [ opts.validations[k] ];
}
}
var isNew = false;
for (k in opts.properties) {
opts.properties[k] = Property.normalize(opts.properties[k], opts.settings);
for (i = 0; i < opts.id.length; i++) {
if (!data.hasOwnProperty(opts.id[i])) {
isNew = true;
break;
}
}
if (opts.properties[k].lazyload !== true) {
model_fields.push(k);
}
if (opts.properties[k].required) {
// Prepend `required` validation
if(opts.validations.hasOwnProperty(k)) {
opts.validations[k].splice(0, 0, Validators.required());
} else {
opts.validations[k] = [Validators.required()];
}
}
}
if (opts.id.length === 1 && opts.id[0] != 'id') {
isNew = true; //Dubiously assume that it is a new instance if we're using custom keys
}
for (k in AvailableHooks) {
model[AvailableHooks[k]] = createHookHelper(AvailableHooks[k]);
}
return createInstance(data, {
is_new: isNew,
autoSave: opts.autoSave,
cascadeRemove: opts.cascadeRemove
});
};
model.properties = opts.properties;
model.settings = opts.settings;
model.allProperties = allProperties;
model.properties = opts.properties;
model.settings = opts.settings;

@@ -207,3 +200,3 @@ model.drop = function (cb) {

}
if (typeof opts.driver.drop == "function") {
if (typeof opts.driver.drop === "function") {
opts.driver.drop({

@@ -219,3 +212,3 @@ table : opts.table,

return cb(ErrorCodes.generateError(ErrorCodes.NO_SUPPORT, "Driver does not support Model.drop()"));
return cb(ErrorCodes.generateError(ErrorCodes.NO_SUPPORT, "Driver does not support Model.drop()", { model: opts.table }));
};

@@ -227,3 +220,3 @@

}
if (typeof opts.driver.sync == "function") {
if (typeof opts.driver.sync === "function") {
try {

@@ -235,3 +228,5 @@ opts.driver.sync({

properties : opts.properties,
allProperties : allProperties,
indexes : opts.indexes || [],
customTypes : opts.db.customTypes,
one_associations : one_associations,

@@ -248,3 +243,3 @@ many_associations : many_associations,

return cb(ErrorCodes.generateError(ErrorCodes.NO_SUPPORT, "Driver does not support Model.sync()"));
return cb(ErrorCodes.generateError(ErrorCodes.NO_SUPPORT, "Driver does not support Model.sync()", { model: opts.table }));
};

@@ -258,16 +253,16 @@

if (typeof cb != "function") {
throw ErrorCodes.generateError(ErrorCodes.MISSING_CALLBACK, "Missing Model.get() callback");
if (typeof cb !== "function") {
throw ErrorCodes.generateError(ErrorCodes.MISSING_CALLBACK, "Missing Model.get() callback", { model: opts.table });
}
if (typeof ids[ids.length - 1] == "object" && !Array.isArray(ids[ids.length - 1])) {
if (typeof ids[ids.length - 1] === "object" && !Array.isArray(ids[ids.length - 1])) {
options = ids.pop();
}
if (ids.length == 1 && Array.isArray(ids[0])) {
if (ids.length === 1 && Array.isArray(ids[0])) {
ids = ids[0];
}
if (ids.length != opts.id.length) {
throw ErrorCodes.generateError(ErrorCodes.PARAM_MISSMATCH, "Model.get() IDs number missmatch (" + opts.id.length + " needed, " + ids.length + " passed)");
if (ids.length !== opts.id.length) {
throw ErrorCodes.generateError(ErrorCodes.PARAM_MISMATCH, "Model.get() IDs number mismatch (" + opts.id.length + " needed, " + ids.length + " passed)", { model: opts.table });
}

@@ -294,3 +289,3 @@

if (data.length === 0) {
return cb(ErrorCodes.generateError(ErrorCodes.NOT_FOUND, "Not found"));
return cb(ErrorCodes.generateError(ErrorCodes.NOT_FOUND, "Not found", { model: opts.table }));
}

@@ -311,5 +306,3 @@

}, cb);
}, function (instance) {
return cb(null, instance);
});
}, cb);
});

@@ -362,3 +355,3 @@

case "string":
if (arguments[i][0] == "-") {
if (arguments[i][0] === "-") {
order = [ arguments[i].substr(1), "Z" ];

@@ -385,2 +378,5 @@ } else {

}
if (conditions) {
conditions = Utilities.checkConditions(conditions, one_associations);
}

@@ -417,9 +413,7 @@ var chain = new ChainFind(model, {

}, cb);
}, function (instance) {
return cb(null, instance);
});
}, cb);
}
});
if (typeof cb != "function") {
if (typeof cb !== "function") {
return chain;

@@ -441,3 +435,3 @@ }

for (var i = 0; i < args.length; i++) {
if (typeof args[i] == "function") {
if (typeof args[i] === "function") {
cb = args.splice(i, 1)[0];

@@ -449,3 +443,3 @@ break;

if (cb === null) {
throw ErrorCodes.generateError(ErrorCodes.MISSING_CALLBACK, "Missing Model.one() callback");
throw ErrorCodes.generateError(ErrorCodes.MISSING_CALLBACK, "Missing Model.one() callback", { model: opts.table });
}

@@ -480,6 +474,10 @@

if (typeof cb != "function") {
throw ErrorCodes.generateError(ErrorCodes.MISSING_CALLBACK, "Missing Model.count() callback");
if (typeof cb !== "function") {
throw ErrorCodes.generateError(ErrorCodes.MISSING_CALLBACK, "Missing Model.count() callback", { model: opts.table });
}
if (conditions) {
conditions = Utilities.checkConditions(conditions, one_associations);
}
opts.driver.count(opts.table, conditions, {}, function (err, data) {

@@ -499,3 +497,3 @@ if (err || data.length === 0) {

for (var i = 0; i < arguments.length; i++) {
if (typeof arguments[i] == "object") {
if (typeof arguments[i] === "object") {
if (Array.isArray(arguments[i])) {

@@ -509,2 +507,6 @@ properties = arguments[i];

if (conditions) {
conditions = Utilities.checkConditions(conditions, one_associations);
}
return new require("./AggregateFunctions")({

@@ -523,4 +525,4 @@ table : opts.table,

if (typeof cb != "function") {
throw ErrorCodes.generateError(ErrorCodes.MISSING_CALLBACK, "Missing Model.exists() callback");
if (typeof cb !== "function") {
throw ErrorCodes.generateError(ErrorCodes.MISSING_CALLBACK, "Missing Model.exists() callback", { model: opts.table });
}

@@ -530,3 +532,3 @@

if (ids.length === 1 && typeof ids[0] == "object") {
if (ids.length === 1 && typeof ids[0] === "object") {
if (Array.isArray(ids[0])) {

@@ -545,2 +547,6 @@ for (i = 0; i < opts.id.length; i++) {

if (conditions) {
conditions = Utilities.checkConditions(conditions, one_associations);
}
opts.driver.count(opts.table, conditions, {}, function (err, data) {

@@ -568,3 +574,8 @@ if (err || data.length === 0) {

autoFetch : false
}, function () {
}, function (err) {
if (err) {
err.index = idx;
err.instance = Instances[idx];
return cb(err);
}
Instances[idx].save(function (err) {

@@ -589,3 +600,3 @@ if (err) {

Instances = Instances.concat(arguments[i]);
} else if (i == 0) {
} else if (i === 0) {
single = true;

@@ -610,3 +621,3 @@ Instances.push(arguments[i]);

opts.driver.clear(opts.table, function (err) {
if (typeof cb == "function") cb(err);
if (typeof cb === "function") cb(err);
});

@@ -629,3 +640,50 @@

});
Object.defineProperty(model, "uid", {
value: opts.driver.uid + "/" + opts.table + "/" + opts.id.join("/"),
enumerable: false
});
// Standardize validations
for (var k in opts.validations) {
if (!Array.isArray(opts.validations[k])) {
opts.validations[k] = [ opts.validations[k] ];
}
}
// standardize properties
for (k in opts.properties) {
opts.properties[k] = Property.normalize(opts.properties[k], opts.db.customTypes, opts.settings);
opts.properties[k].klass = 'primary';
allProperties[k] = opts.properties[k];
if (opts.id.indexOf(k) != -1) {
opts.properties[k].key = true;
}
if (opts.properties[k].lazyload !== true && model_fields.indexOf(k) == -1) {
model_fields.push(k);
}
if (opts.properties[k].required) {
// Prepend `required` validation
if(opts.validations.hasOwnProperty(k)) {
opts.validations[k].splice(0, 0, Validators.required());
} else {
opts.validations[k] = [Validators.required()];
}
}
}
for (var i = 0; i < opts.id.length; i++) {
k = opts.id[i];
allProperties[k] = opts.properties[k] || {
type: 'serial', rational: 'false', key: true, klass: 'key'
};
}
model_fields = opts.id.concat(model_fields);
// setup hooks
for (k in AvailableHooks) {
model[AvailableHooks[k]] = createHookHelper(AvailableHooks[k]);
}
OneAssociation.prepare(model, one_associations, association_properties, model_fields);

@@ -632,0 +690,0 @@ ManyAssociation.prepare(model, many_associations);

@@ -8,2 +8,3 @@ var util = require("util");

var enforce = require("enforce");
var _ = require("lodash");

@@ -46,3 +47,3 @@ var Model = require("./Model").Model;

}
if (typeof opts == "function") {
if (typeof opts === "function") {
cb = opts;

@@ -56,3 +57,3 @@ opts = {};

var driver = new Driver(null, connection, {
debug : (opts.query && opts.query.debug == 'true'),
debug : (opts.query && opts.query.debug === 'true'),
settings : settings

@@ -69,9 +70,12 @@ });

if (arguments.length === 0 || !opts) {
return ORM_Error(ErrorCodes.generateError(ErrorCodes.PARAM_MISSMATCH, "CONNECTION_URL_EMPTY"), cb);
return ORM_Error(ErrorCodes.generateError(ErrorCodes.PARAM_MISMATCH, "CONNECTION_URL_EMPTY"), cb);
}
if (typeof opts == "string") {
if (typeof opts === "string") {
if (opts.replace(/\s+/, "").length === 0) {
return ORM_Error(ErrorCodes.generateError(ErrorCodes.PARAM_MISSMATCH, "CONNECTION_URL_EMPTY"), cb);
return ORM_Error(ErrorCodes.generateError(ErrorCodes.PARAM_MISMATCH, "CONNECTION_URL_EMPTY"), cb);
}
opts = url.parse(opts, true);
for(var k in opts.query) {
opts[k] = opts.query[k];
}
}

@@ -85,3 +89,3 @@ if (!opts.database) {

if (!opts.protocol) {
return ORM_Error(ErrorCodes.generateError(ErrorCodes.PARAM_MISSMATCH, "CONNECTION_URL_NO_PROTOCOL"), cb);
return ORM_Error(ErrorCodes.generateError(ErrorCodes.PARAM_MISMATCH, "CONNECTION_URL_NO_PROTOCOL"), cb);
}

@@ -110,8 +114,8 @@ // if (!opts.host) {

var Driver = require("./Drivers/DML/" + proto).Driver;
var debug = Boolean(extractOption(opts, "debug"));
var pool = Boolean(extractOption(opts, "pool"));
var settings = new Settings.Container(exports.settings.get('*'));
var debug = extractOption(opts, "debug");
var pool = extractOption(opts, "pool");
var driver = new Driver(opts, null, {
debug : debug,
pool : pool,
debug : (debug !== null ? Boolean(debug) : settings.get("connection.debug")),
pool : (pool !== null ? Boolean(pool) : settings.get("connection.pool")),
settings : settings

@@ -123,3 +127,3 @@ });

driver.connect(function (err) {
if (typeof cb == "function") {
if (typeof cb === "function") {
if (err) {

@@ -135,3 +139,3 @@ return cb(err);

} catch (ex) {
if (ex.code == "MODULE_NOT_FOUND" || ex.message.indexOf('find module')) {
if (ex.code === "MODULE_NOT_FOUND" || ex.message.indexOf('find module')) {
return ORM_Error(ErrorCodes.generateError(ErrorCodes.NO_SUPPORT, "CONNECTION_PROTOCOL_NOT_SUPPORTED"), cb);

@@ -155,2 +159,3 @@ }

this.plugins = [];
this.customTypes = {};

@@ -165,3 +170,3 @@ for (var k in Query.Comparators) {

if (this.settings.get("connection.reconnect")) {
if (typeof this.driver.reconnect == "undefined") {
if (typeof this.driver.reconnect === "undefined") {
return this.emit("error", ErrorCodes.generateError(ErrorCodes.CONNECTION_LOST, "Connection lost - driver does not support reconnection"));

@@ -188,3 +193,3 @@ }

ORM.prototype.use = function (plugin_const, opts) {
if (typeof plugin_const == "string") {
if (typeof plugin_const === "string") {
try {

@@ -199,3 +204,3 @@ plugin_const = require(Utilities.getRealPath(plugin_const));

if (typeof plugin.define == "function") {
if (typeof plugin.define === "function") {
for (var k in this.models) {

@@ -211,7 +216,9 @@ plugin.define(this.models[k]);

ORM.prototype.define = function (name, properties, opts) {
var i;
properties = properties || {};
opts = opts || {};
for (var i = 0; i < this.plugins.length; i++) {
if (typeof this.plugins[i].beforeDefine == "function") {
for (i = 0; i < this.plugins.length; i++) {
if (typeof this.plugins[i].beforeDefine === "function") {
this.plugins[i].beforeDefine(name, properties, opts);

@@ -241,4 +248,4 @@ }

for (var i = 0; i < this.plugins.length; i++) {
if (typeof this.plugins[i].define == "function") {
for (i = 0; i < this.plugins.length; i++) {
if (typeof this.plugins[i].define === "function") {
this.plugins[i].define(this.models[name], this);

@@ -250,2 +257,7 @@ }

};
ORM.prototype.defineType = function (name, opts) {
this.customTypes[name] = opts;
this.driver.customTypes[name] = opts;
return this;
}
ORM.prototype.ping = function (cb) {

@@ -261,8 +273,29 @@ this.driver.ping(cb);

};
ORM.prototype.load = function (file, cb) {
try {
return require(Utilities.getRealPath(file))(this, cb);
} catch (ex) {
return cb(ex);
ORM.prototype.load = function () {
var files = _.flatten(Array.prototype.slice.apply(arguments));
var cb = function () {};
if (typeof files[files.length - 1] == "function") {
cb = files.pop();
}
var loadNext = function () {
if (files.length === 0) {
return cb();
}
var file = files.shift();
try {
return require(Utilities.getRealPath(file, 4))(this, function (err) {
if (err) return cb(err);
return loadNext();
});
} catch (ex) {
return cb(ex);
}
}.bind(this);
return loadNext();
};

@@ -360,3 +393,3 @@ ORM.prototype.sync = function (cb) {

if (typeof cb == "function") {
if (typeof cb === "function") {
cb(err);

@@ -363,0 +396,0 @@ }

var ErrorCodes = require("./ErrorCodes");
exports.normalize = function (prop, Settings) {
if (typeof prop == "function") {
exports.normalize = function (prop, customTypes, Settings) {
if (typeof prop === "function") {
switch (prop.name) {

@@ -25,3 +25,3 @@ case "String":

}
} else if (typeof prop == "string") {
} else if (typeof prop === "string") {
var tmp = prop;

@@ -34,4 +34,6 @@ prop = {};

if ([ "text", "number", "boolean", "date", "enum", "object", "binary", "point" ].indexOf(prop.type) == -1) {
throw ErrorCodes.generateError(ErrorCodes.NO_SUPPORT, "Unknown property type: " + prop.type);
if ([ "text", "number", "boolean", "date", "enum", "object", "binary", "point" ].indexOf(prop.type) === -1) {
if (!(prop.type in customTypes)) {
throw ErrorCodes.generateError(ErrorCodes.NO_SUPPORT, "Unknown property type: " + prop.type);
}
}

@@ -38,0 +40,0 @@

@@ -18,3 +18,5 @@ var _ = require('lodash');

connection : {
reconnect : true
reconnect : true,
poll : false,
debug : false
}

@@ -42,3 +44,3 @@ };

for (var i = 0; i < arguments.length; i++) {
if (typeof arguments[i] == "string") {
if (typeof arguments[i] === "string") {
unset(arguments[i], settings);

@@ -56,3 +58,3 @@ }

if (p == -1) {
if (p === -1) {
return obj[key] = value;

@@ -71,4 +73,4 @@ }

if (p == -1) {
if (key == '*') {
if (p === -1) {
if (key === '*') {
return obj;

@@ -89,4 +91,4 @@ }

if (p == -1) {
if (key == '*') {
if (p === -1) {
if (key === '*') {
return 'reset';

@@ -103,5 +105,5 @@ } else {

if (unset(key.substr(p + 1), obj[key.substr(0, p)]) == 'reset') {
if (unset(key.substr(p + 1), obj[key.substr(0, p)]) === 'reset') {
obj[key.substr(0, p)] = {};
}
}
var map = {};
exports.clear = function (key) {
if (typeof key == "string") {
if (typeof key === "string") {
delete map[key];

@@ -17,3 +17,3 @@ } else {

if (map.hasOwnProperty(key)) {
if (opts && opts.save_check && typeof map[key].o.saved == "function" && !map[key].o.saved()) {
if (opts && opts.save_check && typeof map[key].o.saved === "function" && !map[key].o.saved()) {
// if not saved, don't return it, fetch original from db

@@ -24,13 +24,15 @@ return createCb(returnCb);

} else {
return returnCb(map[key].o);
return returnCb(null, map[key].o);
}
}
createCb(function (value) {
createCb(function (err, value) {
if (err) return returnCb(err);
map[key] = { // object , timeout
o : value,
t : (opts && typeof opts.cache == "number" ? Date.now() + (opts.cache * 1000) : null)
t : (opts && typeof opts.cache === "number" ? Date.now() + (opts.cache * 1000) : null)
};
return returnCb(map[key].o);
return returnCb(null, map[key].o);
});
};

@@ -19,4 +19,4 @@ /**

exports.standardizeOrder = function (order) {
if (typeof order == "string") {
if (order[0] == "-") {
if (typeof order === "string") {
if (order[0] === "-") {
return [ [ order.substr(1), "Z" ] ];

@@ -30,3 +30,3 @@ }

for (var i = 0; i < order.length; i++) {
minus = (order[i][0] == "-");
minus = (order[i][0] === "-");

@@ -50,2 +50,62 @@ if (i < order.length - 1 && [ "A", "Z" ].indexOf(order[i + 1].toUpperCase()) >= 0) {

/**
* Operations
* A) Build an index of associations, with their name as the key
* B) Check for any conditions with a key in the association index
* C) Ensure that our condition supports array values
* D) Remove original condition (not DB compatible)
* E) Convert our association fields into an array, indexes are the same as model.id
* F) Itterate through values for the condition, only accept instances of the same type as the association
*/
exports.checkConditions = function (conditions, one_associations) {
var k, i, j;
// A)
var associations = {};
for (i = 0; i < one_associations.length; i++) {
associations[one_associations[i].name] = one_associations[i];
}
for (k in conditions) {
// B)
if (!associations.hasOwnProperty(k)) continue;
// C)
var values = conditions[k];
if (!Array.isArray(values)) values = [values];
// D)
delete conditions[k];
// E)
var association_fields = Object.keys(associations[k].field);
var model = associations[k].model;
// F)
for (i = 0; i < values.length; i++) {
if (values[i].isInstance && values[i].model().uid === model.uid) {
if (association_fields.length === 1) {
if (typeof conditions[association_fields[0]] === 'undefined') {
conditions[association_fields[0]] = values[i][model.id[0]];
} else if(Array.isArray(conditions[association_fields[0]])) {
conditions[association_fields[0]].push(values[i][model.id[0]]);
} else {
conditions[association_fields[0]] = [conditions[association_fields[0]], values[i][model.id[0]]];
}
} else {
var _conds = {};
for (j = 0; j < association_fields.length; i++) {
_conds[association_fields[j]] = values[i][model.id[j]];
}
conditions.or = conditions.or || [];
conditions.or.push(_conds);
}
}
}
}
return conditions;
};
/**
* Gets all the values within an object or array, optionally

@@ -84,3 +144,3 @@ * using a keys array to get only specific values

for (var i = 0; i < model.id.length; i++) {
if (typeof target[fields[i]] == 'undefined' || overwrite !== false) {
if (typeof target[fields[i]] === 'undefined' || overwrite !== false) {
target[fields[i]] = source[model.id[i]];

@@ -93,3 +153,3 @@ } else if (Array.isArray(target[fields[i]])) {

}
}
};

@@ -102,3 +162,3 @@ exports.getConditions = function (model, fields, from) {

return conditions;
}
};

@@ -109,7 +169,7 @@ exports.wrapFieldObject = function (obj, model, altName, alternatives) {

if (typeof assoc_key == "function") {
obj = assoc_key(altName.toLowerCase(), 'id');
if (typeof assoc_key === "function") {
obj = assoc_key(altName.toLowerCase(), model.id[0]);
} else {
obj = assoc_key.replace("{name}", altName.toLowerCase())
.replace("{field}", 'id');
.replace("{field}", model.id[0]);
}

@@ -139,3 +199,3 @@ }

field_name = keys[i];
} else if (typeof assoc_key == "function") {
} else if (typeof assoc_key === "function") {
field_name = assoc_key(name.toLowerCase(), keys[i]);

@@ -180,3 +240,3 @@ } else {

var err = new Error();
var tmp = err.stack.split(/\r?\n/)[typeof stack_index != "undefined" ? stack_index : 3], m;
var tmp = err.stack.split(/\r?\n/)[typeof stack_index !== "undefined" ? stack_index : 3], m;

@@ -191,6 +251,6 @@ if ((m = tmp.match(/^\s*at\s+(.+):\d+:\d+$/)) !== null) {

if (path_str[0] != path.sep) {
if (path_str[0] !== path.sep) {
path_str = cwd + "/" + path_str;
}
if (path_str.substr(-1) == path.sep) {
if (path_str.substr(-1) === path.sep) {
path_str += "index";

@@ -197,0 +257,0 @@ }

var enforce = require("enforce");
var util = require("util");
var validators = {

@@ -27,3 +27,3 @@ required : enforce.required,

return function (v, next, ctx) {
if (v == this[name]) return next();
if (v === this[name]) return next();
return next(msg || 'not-equal-to-property');

@@ -35,13 +35,50 @@ };

* Check if a property is unique in the collection.
* This can take a while because a query has to be
* made against the Model, but if you use this
* always you should not have not unique values
* on this property so this should not worry you.
* This can take a while because a query has to be made against the Model.
*
* Due to the async nature of node, and concurrent web server environments,
* an index on the database column is the only way to gurantee uniqueness.
*
* For sensibility's sake, undefined and null values are ignored for uniqueness
* checks.
*
* Options:
* ignoreCase: for postgres; mysql ignores case by default.
* scope: (Array) scope uniqueness to listed properties
**/
validators.unique = function (msg) {
validators.unique = function () {
var arg, k;
var msg = null, opts = {};
for (k in arguments) {
arg = arguments[k];
if (typeof arg === 'string') msg = arg;
else if (typeof arg === 'object') opts = arg;
}
return function (v, next, ctx) {
var query = {};
query[ctx.property] = v;
var s, scopeProp;
ctx.model.find(query, function (err, records) {
if (typeof v === 'undefined' || v === null) return next();
//Cannot process on database engines which don't support SQL syntax
if (!ctx.driver.isSql) return next('not-supported');
var chain = ctx.model.find();
var chainQuery = function (prop, value) {
var query = null;
if (opts.ignoreCase === true && ctx.model.properties[prop].type === 'text') {
query = util.format('LOWER(%s.%s) LIKE LOWER(?)',
ctx.model.table, ctx.driver.query.escapeId(prop)
);
chain.where(query, [value]);
} else {
query = {};
query[prop] = value;
chain.where(query);
}
};
var handler = function (err, records) {
if (err) {

@@ -53,7 +90,24 @@ return next();

}
if (records.length == 1 && records[0][ctx.model.id] === this[ctx.model.id]) {
if (records.length === 1 && records[0][ctx.model.id] === this[ctx.model.id]) {
return next();
}
return next(msg || 'not-unique');
}.bind(this));
}.bind(this);
chainQuery(ctx.property, v);
if (opts.scope) {
for (s in opts.scope) {
scopeProp = opts.scope[s];
// In SQL unique index land, NULL values are not considered equal.
if (typeof ctx.instance[scopeProp] == 'undefined' || ctx.instance[scopeProp] === null) {
return next();
}
chainQuery(scopeProp, ctx.instance[scopeProp]);
}
}
chain.all(handler);
};

@@ -60,0 +114,0 @@ };

@@ -12,5 +12,6 @@ {

"redshift",
"sqlite"
"sqlite",
"mongodb"
],
"version" : "2.1.0",
"version" : "2.1.1",
"license" : "MIT",

@@ -26,3 +27,3 @@ "homepage" : "http://dresende.github.io/node-orm2",

{ "name" : "David Kosub" },
{ "name" : "Arek W" },
{ "name" : "Arek W", "email" : "arek01@gmail.com" },
{ "name" : "Joseph Gilley", "email" : "joe.gilley@gmail.com" },

@@ -40,17 +41,17 @@ { "name" : "Benjamin Pannell", "email" : "admin@sierrasoftworks.com" }

"dependencies": {
"enforce" : "0.1.1",
"sql-query" : "0.1.9",
"enforce" : "0.1.2",
"sql-query" : "0.1.15",
"hat" : "0.0.3",
"lodash" : "1.3.1"
"lodash" : "2.0.0"
},
"devDependencies": {
"mysql" : "2.0.0-alpha7",
"mysql" : "2.0.0-alpha9",
"pg" : "1.0.0",
"sqlite3" : "2.1.7",
"async" : "*",
"mocha" : "1.12.0",
"should" : "1.2.2",
"mongodb" : "1.3.11"
"mocha" : "1.12.1",
"should" : "1.3.0",
"mongodb" : "1.3.19"
},
"optionalDependencies": {}
}

@@ -94,3 +94,3 @@ ## Object Relational Mapping

app.use(orm.express("mysql://username:password@host/database", {
define: function (db, models) {
define: function (db, models, next) {
models.person = db.define("person", { ... });

@@ -111,82 +111,14 @@ }

## Settings
## Documentation
Settings are used to store key value pairs. A settings object is stored on the global orm object and on each database connection.
Documentation is moving to the [wiki](https://github.com/dresende/node-orm2/wiki/).
```js
var orm = require("orm");
## Settings
orm.settings.set("some.deep.value", 123);
See information in the [wiki](https://github.com/dresende/node-orm2/wiki/Settings).
orm.connect("....", function (err, db) {
// db.settings is a snapshot of the settings at the moment
// of orm.connect(). changes to it don't affect orm.settings
console.log(db.settings.get("some.deep.value")); // 123
console.log(db.settings.get("some.deep")); // { value: 123 }
});
```
More in the [wiki](https://github.com/dresende/node-orm2/wiki/Settings).
## Connecting
First, add the correct driver to your `package.json`:
See information in the [wiki](https://github.com/dresende/node-orm2/wiki/Connecting-to-Database).
driver | npm package | version
:----------------------|:---------------------------|:-----
mysql | mysql | 2.0.0-alpha7
postgres<br/>redshift | pg | ~1.0.0
sqlite | sqlite3 | 2.1.7
mongodb | mongodb | 1.3.11
These are the versions tested. Use others (older or newer) at your own risk.
### Options
You can pass in connection options either as a string:
```js
var orm = require("orm");
orm.connect("mysql://username:password@host/database?pool=true", function (err, db) {
// ...
});
```
**Note:** `pool` is only supported by mysql & postgres. When 'pool' is set to true, your database connections are cached so that connections can be reused, optimizing performance.
**Note:** `strdates` is only supported by sqlite. When true, date fields are saved as strings, compatible with django
Or as an object:
```js
var opts = {
database : "dbname",
protocol : "[mysql|postgres|redshift|sqlite]",
host : "127.0.0.1",
port : 3306, // optional, defaults to database default
user : "..",
password : "..",
query : {
pool : true|false, // optional, false by default
debug : true|false, // optional, false by default
strdates : true|false // optional, false by default
}
};
orm.connect(opts, function (err, db) {
// ...
});
```
You can also avoid passing a callback and just listen for the connect event:
```js
var orm = require("orm");
var db = orm.connect("mysql://username:password@host/database");
db.on("connect", function (err, db) {
// ...
});
```
## Models

@@ -200,47 +132,8 @@

Call `define` on the database connection to setup a model. The name of the table and model is used as an identifier for the model on the database connection, so you can easily access the model later using the connection.
See information in the [wiki](https://github.com/dresende/node-orm2/wiki/Defining-Models).
```js
var Person = db.define('person', { // 'person' will be the table in the database as well as the model id
// properties
name : String, // you can use native objects to define the property type
surname : { type: "text", size: 50 } // or you can be specific and define aditional options
}, {
// options (optional)
});
```
### Properties
#### Types
See information in the [wiki](https://github.com/dresende/node-orm2/wiki/Model-Properties).
Native | String | Native | String
:--------|:-----------|:---------|:---------
String | 'text' | Date | 'date '
Number | 'number' | Object | 'object'
Boolean | 'boolean' | Buffer | 'binary'
| | --- | 'enum'
#### Options
##### [all types]
* `required`: true marks the column as `NOT NULL`, false (default)
* `unique`: true marks the column with a `UNIQUE` index
* `defaultValue`: sets the default value for the field
##### string
* `size`: max length of the string
* `big`: true to make (LONG)TEXT columns instead of VARCHAR(size)
##### number
* `rational`: true (default) creates a FLOAT/REAL, false an INTEGER
* `size`: byte size of number, default is 4. Note that 8 byte numbers [have limitations](http://stackoverflow.com/questions/307179/what-is-javascripts-max-int-whats-the-highest-integer-value-a-number-can-go-t)
* `unsigned`: true to make INTEGER unsigned, default is false
##### date
* `time`: true (default) creates a DATETIME/TIMESTAMP, false a DATE
Note that these may vary accross drivers.
### Instance Methods

@@ -390,41 +283,4 @@

If you want to listen for a type of event than occurs in instances of a Model, you can attach a function that
will be called when that event happens.
See information in the [wiki](https://github.com/dresende/node-orm2/wiki/Model-Hooks).
Currently the following events are supported:
- `afterLoad` : (no parameters) Right after loading and preparing an instance to be used;
- `afterAutoFetch` : (no parameters) Right after auto-fetching associations (if any), it will trigger regardless of having associations or not;
- `beforeSave` : (no parameters) Right before trying to save;
- `afterSave` : (bool success) Right after saving;
- `beforeCreate` : (no parameters) Right before trying to save a new instance (prior to `beforeSave`);
- `afterCreate` : (bool success) Right after saving a new instance;
- `beforeRemove` : (no parameters) Right before trying to remove an instance;
- `afterRemove` : (bool success) Right after removing an instance;
- `beforeValidation` : (no parameters) Before all validations and prior to `beforeCreate` and `beforeSave`;
All hook function are called with `this` as the instance so you can access anything you want related to it.
For all `before*` hooks, you can add an additional parameter to the hook function. This parameter will be a function that
must be called to tell if the hook allows the execution to continue or to break. You might be familiar with this workflow
already from Express. Here's an example:
```js
var Person = db.define("person", {
name : String,
surname : String
}, {
hooks: {
beforeCreate: function (next) {
if (this.surname == "Doe") {
return next(new Error("No Does allowed"));
}
return next();
}
}
});
```
This workflow allows you to make asynchronous work before calling `next`.
## Finding Items

@@ -551,2 +407,9 @@

You can also `order` or `orderRaw`:
```js
Person.find({ age: 18 }).order('-name').all( ... );
// see the 'Raw queries' section below for more details
Person.find({ age: 18 }).orderRaw("?? DESC", ['age']).all( ... );
```
You can also chain and just get the count in the end. In this case, offset, limit and order are ignored.

@@ -624,2 +487,24 @@

#### Raw queries
```js
db.driver.execQuery("SELECT id, email FROM user", function (err, data) { ... })
// You can escape identifiers and values.
// For identifier substitution use: ??
// For value substitution use: ?
db.driver.execQuery(
"SELECT user.??, user.?? FROM user WHERE user.?? LIKE ? AND user.?? > ?",
['id', 'name', 'name', 'john', 'id', 55],
function (err, data) { ... }
)
// Identifiers don't need to be scaped most of the time
db.driver.execQuery(
"SELECT user.id, user.name FROM user WHERE user.name LIKE ? AND user.id > ?",
['john', 55],
function (err, data) { ... }
)
```
### Caching & Integrity

@@ -718,64 +603,4 @@

The module [Enforce](http://github.com/dresende/node-enforce) is used for validations. For people using previous validators,
they're still present, some as links to enforce, others not. We advise you to start using `orm.enforce` instead of `orm.validators`.
For a list of possible validations, consult the [module](http://github.com/dresende/node-enforce).
See information in the [wiki](https://github.com/dresende/node-orm2/wiki/Model-Validations).
You can define validations for every property of a Model. You can have one or more validations for each property.
You can also use the predefined validations or create your own.
```js
var Person = db.define("person", {
name : String,
age : Number
}, {
validations : {
name : orm.enforce.ranges.length(1, undefined, "missing"), // "missing" is a name given to this validation, instead of default
age : [ orm.enforce.ranges.number(0, 10), orm.enforce.lists.inside([ 1, 3, 5, 7, 9 ]) ]
}
});
```
The code above defines that the `name` length must be between 1 and undefined (undefined means any) and `age`
must be a number between 0 and 10 (inclusive) but also one of the listed values. The example might not make sense
but you get the point.
When saving an item, if it fails to validate any of the defined validations you'll get an `error` object with the property
name and validation error description. This description should help you identify what happened.
```js
var John = new Person({
name : "",
age : 20
});
John.save(function (err) {
// err.field = "name" , err.value = "" , err.msg = "missing"
});
```
The validation stops after the first validation error. If you want it to validate every property and return all validation
errors, you can change this behavior on global or local settings:
```js
var orm = require("orm");
orm.settings.set("instance.returnAllErrors", true); // global or..
orm.connect("....", function (err, db) {
db.settings.set("instance.returnAllErrors", true); // .. local
// ...
var John = new Person({
name : "",
age : 15
});
John.save(function (err) {
assert(Array.isArray(err));
// err[0].property = "name" , err[0].value = "" , err[0].msg = "missing"
// err[1].property = "age" , err[1].value = 15 , err[1].msg = "out-of-range-number"
// err[2].property = "age" , err[2].value = 15 , err[2].msg = "outside-list"
});
});
```
## Associations

@@ -782,0 +607,0 @@

@@ -1,5 +0,8 @@

var common = exports;
var path = require('path');
var async = require('async');
var ORM = require('../');
var common = exports;
var path = require('path');
var async = require('async');
var _ = require('lodash');
var util = require('util');
var querystring = require('querystring');
var ORM = require('../');

@@ -16,4 +19,4 @@ common.ORM = ORM;

common.createConnection = function(cb) {
ORM.connect(this.getConnectionString(), cb);
common.createConnection = function(opts, cb) {
ORM.connect(this.getConnectionString(opts), cb);
};

@@ -24,2 +27,4 @@

if (common.isTravis()) return 'found';
try {

@@ -54,54 +59,101 @@ config = require("./config");

common.getConnectionString = function () {
var url;
common.getConnectionString = function (opts) {
var config, query;
var protocol = this.protocol();
if (common.isTravis()) {
switch (this.protocol()) {
case 'mysql':
return 'mysql://root@localhost/orm_test';
case 'postgres':
case 'redshift':
return 'postgres://postgres@localhost/orm_test';
case 'sqlite':
return 'sqlite://';
case 'mongodb':
return 'mongodb://localhost/test';
default:
throw new Error("Unknown protocol");
}
} else {
var config = require("./config")[this.protocol()];
if (common.isTravis()) {
config = {};
} else {
config = require("./config")[protocol];
}
switch (this.protocol()) {
case 'mysql':
return 'mysql://' +
(config.user || 'root') +
(config.password ? ':' + config.password : '') +
'@' + (config.host || 'localhost') +
'/' + (config.database || 'orm_test');
case 'postgres':
return 'postgres://' +
(config.user || 'postgres') +
(config.password ? ':' + config.password : '') +
'@' + (config.host || 'localhost') +
'/' + (config.database || 'orm_test');
case 'redshift':
return 'redshift://' +
(config.user || 'postgres') +
(config.password ? ':' + config.password : '') +
'@' + (config.host || 'localhost') +
'/' + (config.database || 'orm_test');
case 'mongodb':
return 'mongodb://' +
(config.user || '') +
(config.password ? ':' + config.password : '') +
'@' + (config.host || 'localhost') +
'/' + (config.database || 'test');
case 'sqlite':
return 'sqlite://' + (config.pathname || "");
default:
throw new Error("Unknown protocol");
}
}
return url;
opts = opts || {};
_.defaults(config, {
user : { postgres: 'postgres', redshift: 'postgres', mongodb: '' }[protocol] || 'root',
database : { mongodb: 'test' }[protocol] || 'orm_test',
password : '',
host : 'localhost',
pathname : '',
query : {}
});
_.merge(config, opts);
query = querystring.stringify(config.query);
switch (protocol) {
case 'mysql':
case 'postgres':
case 'redshift':
case 'mongodb':
if (common.isTravis()) {
if (protocol == 'redshift') protocol = 'postgres';
return util.format("%s://%s@%s/%s?%s",
protocol, config.user, config.host, config.database, query
);
} else {
return util.format("%s://%s:%s@%s/%s?%s",
protocol, config.user, config.password,
config.host, config.database, query
).replace(':@','@');
}
case 'sqlite':
return util.format("%s://%s?%s", protocol, config.pathname, query);
default:
throw new Error("Unknown protocol " + protocol);
}
};
common.retry = function (before, run, until, done, args) {
if (typeof until === "number") {
var countDown = until;
until = function (err) {
if (err && --countDown > 0) return false;
return true;
};
}
if (typeof args === "undefined") args = [];
var handler = function (err) {
if (until(err)) return done.apply(this, arguments);
return runNext();
};
args.push(handler);
var runCurrent = function () {
if (run.length == args.length) {
return run.apply(this, args);
} else {
run.apply(this, args);
handler();
}
};
var runNext = function () {
try {
if (before.length > 0) {
before(function (err) {
if (until(err)) return done(err);
return runCurrent();
});
} else {
before();
runCurrent();
}
}
catch (e) {
handler(e);
}
};
if (before.length > 0) {
before(function (err) {
if (err) return done(err);
runNext();
});
}
else {
before();
runNext();
}
};

@@ -20,1 +20,5 @@ // To test, rename this file to config.js and update

};
exports.mongodb = {
host: "localhost",
database: "test"
};

@@ -223,2 +223,28 @@ var should = require('should');

it("should accept arguments in different orders", function (done) {
Pet.find({ name: "Mutt" }, function (err, pets) {
Person.find({ name: "John" }, function (err, people) {
should.equal(err, null);
people[0].removePets(function (err) {
should.equal(err, null);
people[0].getPets(function (err, pets) {
should.equal(err, null);
should(Array.isArray(pets));
pets.length.should.equal(1);
pets[0].name.should.equal("Deco");
return done();
});
}, pets[0]);
});
});
});
});
describe("delAccessor", function () {
before(setup());
it("should remove specific associations if passed", function (done) {

@@ -225,0 +251,0 @@ Pet.find({ name: "Mutt" }, function (err, pets) {

@@ -1,25 +0,27 @@

var ORM = require('../../');
var ORM = require('../../');
var helper = require('../support/spec_helper');
var should = require('should');
var async = require('async');
var _ = require('lodash');
var async = require('async');
var common = require('../common');
var _ = require('lodash');
describe("hasOne", function () {
var db = null;
var Person = null;
var db = null;
var Person = null;
var setup = function () {
return function (done) {
Person = db.define('person', {
name : String
});
Pet = db.define('pet', {
name : String
});
Person.hasOne('pet', Pet, {
reverse : 'owner'
});
var setup = function () {
return function (done) {
Person = db.define('person', {
name: String
});
Pet = db.define('pet', {
name: String
});
Person.hasOne('pet', Pet, {
reverse: 'owner',
field: 'pet_id'
});
return helper.dropSync([ Person, Pet ], function () {
async.parallel([
return helper.dropSync([Person, Pet], function () {
async.parallel([
Person.create.bind(Person, { name: "John Doe" }),

@@ -29,87 +31,180 @@ Person.create.bind(Person, { name: "Jane Doe" }),

Pet.create.bind(Pet, { name: "Fido" }),
], done);
});
};
};
], done);
});
};
};
before(function(done) {
helper.connect(function (connection) {
db = connection;
done();
});
});
before(function (done) {
helper.connect(function (connection) {
db = connection;
done();
});
});
describe("reverse", function () {
before(setup());
describe("reverse", function () {
before(setup());
it("should create methods in both models", function (done) {
var person = Person(1);
var pet = Pet(1);
it("should create methods in both models", function (done) {
var person = Person(1);
var pet = Pet(1);
person.getPet.should.be.a("function");
person.setPet.should.be.a("function");
person.removePet.should.be.a("function");
person.hasPet.should.be.a("function");
person.getPet.should.be.a("function");
person.setPet.should.be.a("function");
person.removePet.should.be.a("function");
person.hasPet.should.be.a("function");
pet.getOwner.should.be.a("function");
pet.setOwner.should.be.a("function");
pet.hasOwner.should.be.a("function");
pet.getOwner.should.be.a("function");
pet.setOwner.should.be.a("function");
pet.hasOwner.should.be.a("function");
return done();
});
return done();
});
it("should be able to fetch model from reverse model", function (done) {
Person.find({ name: "John Doe" }).first(function (err, John) {
Pet.find({ name: "Deco" }).first(function (err, Deco) {
Deco.hasOwner(function (err, has_owner) {
should.not.exist(err);
has_owner.should.be.false;
it("should be able to fetch model from reverse model", function (done) {
Person.find({ name: "John Doe" }).first(function (err, John) {
Pet.find({ name: "Deco" }).first(function (err, Deco) {
Deco.hasOwner(function (err, has_owner) {
should.not.exist(err);
has_owner.should.be.false;
Deco.setOwner(John, function (err) {
should.not.exist(err);
Deco.setOwner(John, function (err) {
should.not.exist(err);
Deco.getOwner(function (err, JohnCopy) {
should.not.exist(err);
should(Array.isArray(JohnCopy));
John.should.eql(JohnCopy[0]);
Deco.getOwner(function (err, JohnCopy) {
should.not.exist(err);
should(Array.isArray(JohnCopy));
John.should.eql(JohnCopy[0]);
return done();
});
});
});
});
});
});
return done();
});
});
});
});
});
});
it("should be able to set an array of people as the owner", function (done) {
Person.find({ name: ["John Doe", "Jane Doe"] }, function (err, owners) {
Pet.find({ name: "Fido" }).first(function (err, Fido) {
Fido.hasOwner(function (err, has_owner) {
should.not.exist(err);
has_owner.should.be.false;
it("should be able to set an array of people as the owner", function (done) {
Person.find({ name: ["John Doe", "Jane Doe"] }, function (err, owners) {
Pet.find({ name: "Fido" }).first(function (err, Fido) {
Fido.hasOwner(function (err, has_owner) {
should.not.exist(err);
has_owner.should.be.false;
Fido.setOwner(owners, function (err) {
should.not.exist(err);
Fido.setOwner(owners, function (err) {
should.not.exist(err);
Fido.getOwner(function (err, ownersCopy) {
should.not.exist(err);
should(Array.isArray(owners));
owners.length.should.equal(2);
Fido.getOwner(function (err, ownersCopy) {
should.not.exist(err);
should(Array.isArray(owners));
owners.length.should.equal(2);
if (owners[0] == ownersCopy[0]) {
owners[0].should.eql(ownersCopy[0]);
owners[1].should.eql(ownersCopy[1]);
} else {
owners[0].should.eql(ownersCopy[1]);
owners[1].should.eql(ownersCopy[0]);
}
if (owners[0] == ownersCopy[0]) {
owners[0].should.eql(ownersCopy[0]);
owners[1].should.eql(ownersCopy[1]);
} else {
owners[0].should.eql(ownersCopy[1]);
owners[1].should.eql(ownersCopy[0]);
}
return done();
});
});
});
});
});
});
});
return done();
});
});
});
});
});
});
});
describe("reverse find", function () {
it("should be able to find given an association id", function (done) {
common.retry(setup(), function (done) {
Person.find({ name: "John Doe" }).first(function (err, John) {
should.not.exist(err);
should.exist(John);
Pet.find({ name: "Deco" }).first(function (err, Deco) {
should.not.exist(err);
should.exist(Deco);
Deco.hasOwner(function (err, has_owner) {
should.not.exist(err);
has_owner.should.be.false;
Deco.setOwner(John, function (err) {
should.not.exist(err);
Person.find({ pet_id: Deco[Pet.id[0]] }).first(function (err, owner) {
should.not.exist(err);
should.exist(owner);
should.equal(owner.name, John.name);
done();
});
});
});
});
});
}, 3, done);
});
it("should be able to find given an association instance", function (done) {
common.retry(setup(), function (done) {
Person.find({ name: "John Doe" }).first(function (err, John) {
should.not.exist(err);
should.exist(John);
Pet.find({ name: "Deco" }).first(function (err, Deco) {
should.not.exist(err);
should.exist(Deco);
Deco.hasOwner(function (err, has_owner) {
should.not.exist(err);
has_owner.should.be.false;
Deco.setOwner(John, function (err) {
should.not.exist(err);
Person.find({ pet: Deco }).first(function (err, owner) {
should.not.exist(err);
should.exist(owner);
should.equal(owner.name, John.name);
done();
});
});
});
});
});
}, 3, done);
});
it("should be able to find given a number of association instances with a single primary key", function (done) {
common.retry(setup(), function (done) {
Person.find({ name: "John Doe" }).first(function (err, John) {
should.not.exist(err);
should.exist(John);
Pet.all(function (err, pets) {
should.not.exist(err);
should.exist(pets);
should.equal(pets.length, 2);
pets[0].hasOwner(function (err, has_owner) {
should.not.exist(err);
has_owner.should.be.false;
pets[0].setOwner(John, function (err) {
should.not.exist(err);
Person.find({ pet: pets }, function (err, owners) {
should.not.exist(err);
should.exist(owners);
owners.length.should.equal(1);
should.equal(owners[0].name, John.name);
done();
});
});
});
});
});
}, 3, done);
});
});
});

@@ -232,2 +232,24 @@ var ORM = require('../../');

});
it("shouldn't cause an infinite loop when getting and saving with no changes", function (done) {
Leaf.get(leafId, function (err, leaf) {
should.not.exist(err);
leaf.save( function (err) {
should.not.exist(err);
done();
});
});
});
it("shouldn't cause an infinite loop when getting and saving with changes", function (done) {
Leaf.get(leafId, function (err, leaf) {
should.not.exist(err);
leaf.save({ size: 14 }, function (err) {
should.not.exist(err);
done();
});
});
});
});

@@ -234,0 +256,0 @@ });

@@ -1,4 +0,5 @@

var should = require('should');
var helper = require('../support/spec_helper');
var ORM = require('../../');
var should = require('should');
var helper = require('../support/spec_helper');
var ORM = require('../../');
var common = require('../common');

@@ -131,2 +132,52 @@ describe("db.use()", function () {

describe("db.load()", function () {
var db = null;
before(function (done) {
helper.connect(function (connection) {
db = connection;
return done();
});
});
after(function () {
return db.close();
});
it("should be able to load more than one file", function (done) {
db.load("../support/spec_load_second", "../support/spec_load_third", function () {
db.models.should.have.property("person");
db.models.should.have.property("pet");
return done();
});
});
});
describe("db.load()", function () {
var db = null;
before(function (done) {
helper.connect(function (connection) {
db = connection;
return done();
});
});
after(function () {
return db.close();
});
it("should be able to load more than one file passed as Array", function (done) {
db.load([ "../support/spec_load_second", "../support/spec_load_third" ], function () {
db.models.should.have.property("person");
db.models.should.have.property("pet");
return done();
});
});
});
describe("db.serial()", function () {

@@ -180,1 +231,65 @@ var db = null;

});
describe("db.driver", function () {
var db = null;
before(function (done) {
helper.connect(function (connection) {
db = connection;
var Log = db.define('log', {
what : { type: 'text' },
when : { type: 'date', time: true },
who : { type: 'text' }
});
helper.dropSync(Log, function (err) {
if (err) return done(err);
Log.create([
{ what: "password reset", when: new Date('2013/04/07 12:33:05'), who: "jane" },
{ what: "user login", when: new Date('2013/04/07 13:01:44'), who: "jane" },
{ what: "user logout", when: new Date('2013/05/12 04:09:31'), who: "john" }
], done);
});
});
});
after(function () {
return db.close();
});
it("should be available", function () {
should.exist(db.driver);
});
if (common.protocol() == "mongodb") return;
describe("query", function () {
it("should be available", function () {
should.exist(db.driver.query);
});
describe("#execQuery", function () {
it("should execute sql queries", function (done) {
db.driver.execQuery("SELECT id FROM log", function (err, data) {
should.not.exist(err);
should(JSON.stringify(data) == JSON.stringify([{ id: 1 }, { id: 2 }, { id: 3 }]));
done();
});
});
it("should escape sql queries", function (done) {
var query = "SELECT log.?? FROM log WHERE log.?? LIKE ? AND log.?? > ?";
var args = ['what', 'who', 'jane', 'when', new Date('2013/04/07 12:40:00')];
db.driver.execQuery(query, args, function (err, data) {
should.not.exist(err);
should(JSON.stringify(data) == JSON.stringify([{ "what": "user login" }]));
done();
});
});
});
});
});

@@ -489,2 +489,21 @@ var _ = require('lodash');

});
describe("if hook returns an error", function () {
before(setup({
afterLoad : function (next) {
return next(new Error("AFTERLOAD_FAIL"));
}
}));
it("should return error", function (done) {
this.timeout(500);
Person.create([{ name: "John Doe" }], function (err, items) {
err.should.exist;
err.message.should.equal("AFTERLOAD_FAIL");
return done();
});
});
});
});

@@ -491,0 +510,0 @@ });

@@ -1,3 +0,4 @@

var should = require('should');
var helper = require('../support/spec_helper');
var should = require('should');
var helper = require('../support/spec_helper');
var common = require('../common');
var ORM = require('../../');

@@ -8,2 +9,3 @@

var Person = null;
var protocol = common.protocol();

@@ -15,5 +17,8 @@ var setup = function () {

Person = db.define("person", {
name : String,
age : { type: 'number', rational: false, required: false }
name : String,
age : { type: 'number', rational: false, required: false },
height : { type: 'number', rational: false, required: false },
weight : { type: 'number', required: false }
}, {
cache: false,
validations: {

@@ -127,2 +132,110 @@ age: ORM.validators.rangeNumber(0, 150)

});
describe("properties", function () {
describe("Number", function () {
it("should be saved for valid numbers, using both save & create", function (done) {
var person1 = new Person({ height: 190 });
person1.save(function (err) {
should.not.exist(err);
Person.create({ height: 170 }, function (err, person2) {
should.not.exist(err);
Person.get(person1[Person.id], function (err, item) {
should.not.exist(err);
should.equal(item.height, 190);
Person.get(person2[Person.id], function (err, item) {
should.not.exist(err);
should.equal(item.height, 170);
done();
});
});
});
});
});
if (protocol == 'postgres') {
// Only postgres raises propper errors.
// Sqlite & Mysql fail silently and insert nulls.
it("should raise an error for NaN integers", function (done) {
var person = new Person({ height: NaN });
person.save(function (err) {
should.exist(err);
var msg = {
postgres : 'invalid input syntax for integer: "NaN"'
}[protocol];
should.equal(err.message, msg);
done();
});
});
it("should raise an error for Infinity integers", function (done) {
var person = new Person({ height: Infinity });
person.save(function (err) {
should.exist(err);
var msg = {
postgres : 'invalid input syntax for integer: "Infinity"'
}[protocol];
should.equal(err.message, msg);
done();
});
});
it("should raise an error for nonsensical integers, for both save & create", function (done) {
var person = new Person({ height: 'bugz' });
person.save(function (err) {
should.exist(err);
var msg = {
postgres : 'invalid input syntax for integer: "bugz"'
}[protocol];
should.equal(err.message, msg);
Person.create({ height: 'bugz' }, function (err, instance) {
should.exist(err);
should.equal(err.message, msg);
done();
});
});
});
}
if (protocol != 'mysql') {
// Mysql doesn't support IEEE floats (NaN, Infinity, -Infinity)
it("should store NaN & Infinite floats", function (done) {
var person = new Person({ weight: NaN });
person.save(function (err) {
should.not.exist(err);
Person.get(person[Person.id], function (err, person) {
should.not.exist(err);
should(isNaN(person.weight));
person.save({ weight: Infinity, name: 'black hole' }, function (err) {
should.not.exist(err);
Person.get(person[Person.id], function (err, person) {
should.not.exist(err);
should.strictEqual(person.weight, Infinity);
done();
});
});
});
});
});
}
});
});
});

@@ -92,6 +92,6 @@ var should = require('should');

describe(".order('property')", function () {
describe("order", function () {
before(setup());
it("should order by that property ascending", function (done) {
it("('property') should order by that property ascending", function (done) {
Person.find().order("age").run(function (err, instances) {

@@ -106,8 +106,4 @@ should.equal(err, null);

});
});
describe(".order('-property')", function () {
before(setup());
it("should order by that property descending", function (done) {
it("('-property') should order by that property descending", function (done) {
Person.find().order("-age").run(function (err, instances) {

@@ -122,9 +118,22 @@ should.equal(err, null);

});
it("('property', 'Z') should order by that property descending", function (done) {
Person.find().order("age", "Z").run(function (err, instances) {
should.equal(err, null);
instances.should.have.property("length", 3);
instances[0].age.should.equal(20);
instances[2].age.should.equal(18);
return done();
});
});
});
describe(".order('property', 'Z')", function () {
describe("orderRaw", function () {
if (common.protocol() == 'mongodb') return;
before(setup());
it("should order by that property descending", function (done) {
Person.find().order("age", "Z").run(function (err, instances) {
it("should allow ordering by SQL", function (done) {
Person.find().orderRaw("age DESC").run(function (err, instances) {
should.equal(err, null);

@@ -138,8 +147,19 @@ instances.should.have.property("length", 3);

});
it("should allow ordering by SQL with escaping", function (done) {
Person.find().orderRaw("?? DESC", ['age']).run(function (err, instances) {
should.equal(err, null);
instances.should.have.property("length", 3);
instances[0].age.should.equal(20);
instances[2].age.should.equal(18);
return done();
});
});
});
describe(".only('property', ...)", function () {
describe("only", function () {
before(setup());
it("should return only those properties, others null", function (done) {
it("('property', ...) should return only those properties, others null", function (done) {
Person.find().only("age", "surname").order("-age").run(function (err, instances) {

@@ -155,8 +175,5 @@ should.equal(err, null);

});
});
describe(".only('property1', ...)", function () {
before(setup());
it("should return only those properties, others null", function (done) {
// This works if cache is disabled. I suspect a cache bug.
xit("(['property', ...]) should return only those properties, others null", function (done) {
Person.find().only([ "age", "surname" ]).order("-age").run(function (err, instances) {

@@ -469,2 +486,30 @@ should.equal(err, null);

});
describe(".success()", function () {
before(setup());
it("should return a Promise with .fail() method", function (done) {
Person.find().success(function (people) {
should(Array.isArray(people));
return done();
}).fail(function (err) {
// never called..
});
});
});
describe(".fail()", function () {
before(setup());
it("should return a Promise with .success() method", function (done) {
Person.find().fail(function (err) {
// never called..
}).success(function (people) {
should(Array.isArray(people));
return done();
});
});
});
});
var should = require('should');
var helper = require('../support/spec_helper');
var common = require('../common');
var ORM = require('../../');

@@ -285,2 +286,4 @@

describe("with a point property type", function() {
if (common.protocol() == 'sqlite' || common.protocol() == 'mongodb') return;
it("should deserialize the point to an array", function (done) {

@@ -296,7 +299,3 @@ db.settings.set('properties.primary_key', 'id');

return helper.dropSync(Person, function (err) {
if (err) {
return done(); // not supported
}
return helper.dropSync(Person, function () {
Person.create({

@@ -303,0 +302,0 @@ name : "John Doe",

@@ -56,2 +56,5 @@ var should = require('should');

DoorAccessHistory = db.define("door_access_history", {
year : { type: 'number', rational: false },
month : { type: 'number', rational: false },
day : { type: 'number', rational: false },
user : String,

@@ -106,11 +109,3 @@ action : [ "in", "out" ]

});
it("should throw if defining hasMany association", function (done) {
(function () {
DoorAccessHistory.hasMany("...");
}).should.throw();
return done();
});
});
});
var should = require('should');
var helper = require('../support/spec_helper');
var common = require('../common');
var ORM = require('../../');

@@ -201,8 +202,6 @@

describe("with a point property", function () {
if (common.protocol() == 'sqlite' || common.protocol() == 'mongodb') return;
it("should save the instance as a geospatial point", function (done) {
setup({ type: "point" }, null)(function (err) {
if (err) {
return done(); // not supported
}
setup({ type: "point" }, null)(function () {
var John = new Person({

@@ -209,0 +208,0 @@ name: { x: 51.5177, y: -0.0968 }

@@ -201,3 +201,3 @@ var sqlite = require('sqlite3');

ORM.use(db, "sqlite", function (err) {
should.equal(err, null);
should.not.exist(err);

@@ -204,0 +204,0 @@ return done();

var should = require('should');
var helper = require('../support/spec_helper');
var validators = require('../../').validators;
var common = require('../common');
var protocol = common.protocol().toLowerCase();
var undef = undefined;

@@ -29,2 +31,4 @@

describe("unique()", function () {
if (protocol === "mongodb") return;
var db = null;

@@ -31,0 +35,0 @@ var Person = null;

@@ -8,3 +8,3 @@ var should = require('should');

it("should return type: 'text'", function (done) {
Property.normalize(String, ORM.settings).type.should.equal("text");
Property.normalize(String, {}, ORM.settings).type.should.equal("text");

@@ -16,3 +16,3 @@ return done();

it("should return type: 'number'", function (done) {
Property.normalize(Number, ORM.settings).type.should.equal("number");
Property.normalize(Number, {}, ORM.settings).type.should.equal("number");

@@ -24,3 +24,3 @@ return done();

it("should return type: 'boolean'", function (done) {
Property.normalize(Boolean, ORM.settings).type.should.equal("boolean");
Property.normalize(Boolean, {}, ORM.settings).type.should.equal("boolean");

@@ -32,3 +32,3 @@ return done();

it("should return type: 'date'", function (done) {
Property.normalize(Date, ORM.settings).type.should.equal("date");
Property.normalize(Date, {}, ORM.settings).type.should.equal("date");

@@ -40,3 +40,3 @@ return done();

it("should return type: 'object'", function (done) {
Property.normalize(Object, ORM.settings).type.should.equal("object");
Property.normalize(Object, {}, ORM.settings).type.should.equal("object");

@@ -48,3 +48,3 @@ return done();

it("should return type: 'binary'", function (done) {
Property.normalize(Buffer, ORM.settings).type.should.equal("binary");
Property.normalize(Buffer, {}, ORM.settings).type.should.equal("binary");

@@ -56,3 +56,3 @@ return done();

it("should return type: 'enum' with list of items", function (done) {
var prop = Property.normalize([ 1, 2, 3 ], ORM.settings);
var prop = Property.normalize([ 1, 2, 3 ], {}, ORM.settings);

@@ -67,3 +67,3 @@ prop.type.should.equal("enum");

it("should return type: <type>", function (done) {
Property.normalize("text", ORM.settings).type.should.equal("text");
Property.normalize("text", {}, ORM.settings).type.should.equal("text");

@@ -73,4 +73,4 @@ return done();

it("should accept: 'point'", function(done) {
Property.normalize("point", ORM.settings).type.should.equal("point");
Property.normalize("point", {}, ORM.settings).type.should.equal("point");
return done();

@@ -82,3 +82,3 @@ });

(function () {
Property.normalize("string", ORM.settings);
Property.normalize("string", {}, ORM.settings);
}).should.throw();

@@ -85,0 +85,0 @@

@@ -5,2 +5,4 @@ var _ = require('lodash');

var async = require('async');
var common = require('../common');
var protocol = common.protocol().toLowerCase();
var ORM = require('../../');

@@ -60,23 +62,165 @@

it("unique validator should work", function(done) {
var Product = db.define("person_unique", { name: String }, {
validations: { name: ORM.validators.unique() }
});
var create = function (cb) {
var p = new Product({ name: 'broom' });
describe("unique", function () {
if (protocol === "mongodb") return;
return p.save(cb);
var Product = null;
var setupUnique = function (ignoreCase, scope, msg) {
return function (done) {
Product = db.define("product_unique", {
instock : { type: 'boolean', required: true, defaultValue: false },
name : String,
category : String
}, {
cache: false,
validations: {
name : ORM.validators.unique({ ignoreCase: ignoreCase, scope: scope }, msg),
instock : ORM.validators.required(),
productId : ORM.validators.unique() // this must be straight after a required & validated row.
}
});
Product.hasOne('product', Product, { field: 'productId', required: false, autoFetch: true });
return helper.dropSync(Product, done);
};
};
helper.dropSync(Product, function() {
create(function (err) {
should.equal(err, null);
create(function (err) {
should.deepEqual(err, _.extend(new Error(),{
property: 'name', value: 'broom', msg: 'not-unique'
}));
return done();
describe("simple", function () {
before(setupUnique(false, false));
it("should return validation error for duplicate name", function (done) {
Product.create({name: 'fork'}, function (err, product) {
should.not.exist(err);
Product.create({name: 'fork'}, function (err, product) {
should.exist(err);
return done();
});
});
});
it("should pass with different names", function (done) {
Product.create({name: 'spatula'}, function (err, product) {
should.not.exist(err);
Product.create({name: 'plate'}, function (err, product) {
should.not.exist(err);
return done();
});
});
});
// Technically this is covered by the tests above, but I'm putting it here for clarity's sake. 3 HOURS WASTED *sigh.
it("should not leak required state from previous validation for association properties [regression test]", function (done) {
Product.create({ name: 'pencil', productId: null}, function (err, product) {
should.not.exist(err);
Product.create({ name: 'pencilcase', productId: null }, function (err, product) {
should.not.exist(err);
return done();
});
});
});
});
describe("scope", function () {
describe("to other property", function () {
before(setupUnique(false, ['category']));
it("should return validation error if other property also matches", function(done) {
Product.create({name: 'red', category: 'chair'}, function (err, product) {
should.not.exist(err);
Product.create({name: 'red', category: 'chair'}, function (err, product) {
should.exist(err);
should.equal(err.msg, 'not-unique');
return done();
});
});
});
it("should pass if other peroperty is different", function (done) {
Product.create({name: 'blue', category: 'chair'}, function (err, product) {
should.not.exist(err);
Product.create({name: 'blue', category: 'pen'}, function (err, product) {
should.not.exist(err);
return done();
});
});
});
// In SQL unique index land, NULL values are not considered equal.
it("should pass if other peroperty is null", function (done) {
Product.create({name: 'blue', category: null}, function (err, product) {
should.not.exist(err);
Product.create({name: 'blue', category: null}, function (err, product) {
should.not.exist(err);
return done();
});
});
});
});
});
describe("ignoreCase", function () {
if (protocol != 'mysql') {
it("false should do a case sensitive comparison", function (done) {
setupUnique(false, false)(function (err) {
should.not.exist(err);
Product.create({name: 'spork'}, function (err, product) {
should.not.exist(err);
Product.create({name: 'spOrk'}, function (err, product) {
should.not.exist(err);
return done();
});
});
});
});
}
it("true should do a case insensitive comparison", function (done) {
setupUnique(true, false)(function (err) {
should.not.exist(err);
Product.create({name: 'stapler'}, function (err, product) {
should.not.exist(err);
Product.create({name: 'staplER'}, function (err, product) {
should.exist(err);
should.equal(err.msg, 'not-unique');
return done();
});
});
});
});
it("true should do a case insensitive comparison on scoped properties too", function (done) {
setupUnique(true, ['category'], "name already taken for this category")(function (err) {
should.not.exist(err);
Product.create({name: 'black', category: 'pen'}, function (err, product) {
should.not.exist(err);
Product.create({name: 'Black', category: 'Pen'}, function (err, product) {
should.exist(err);
should.equal(err.msg, "name already taken for this category");
return done();
});
});
});
});
});
});

@@ -83,0 +227,0 @@ });

var common = require('../common');
var async = require('async');
var should = require('should');
module.exports.connect = function(cb) {
common.createConnection(function (err, conn) {
var opts = {};
if (1 in arguments) {
opts = arguments[0];
cb = arguments[1];
}
common.createConnection(opts, function (err, conn) {
if (err) throw err;

@@ -22,3 +29,8 @@ cb(conn);

});
}, done);
}, function (err) {
if (common.protocol() != 'sqlite') {
should.not.exist(err);
}
done(err);
});
};
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc