不要怂,就是干,撸起袖子干!

Commit e37243cb by Mick Hansen

merge conflict resolve

2 parents df76b18a 8462350b
...@@ -120,8 +120,9 @@ module.exports = (function() { ...@@ -120,8 +120,9 @@ module.exports = (function() {
} }
}) })
this.primaryKeyCount = Object.keys(this.primaryKeys).length; this.primaryKeyAttributes = Object.keys(this.primaryKeys)
this.options.hasPrimaryKeys = this.hasPrimaryKeys = this.primaryKeyCount > 0; this.primaryKeyCount = this.primaryKeyAttributes.length
this.options.hasPrimaryKeys = this.hasPrimaryKeys = this.primaryKeyCount > 0
if (typeof this.options.defaultScope === "object") { if (typeof this.options.defaultScope === "object") {
Utils.injectScope.call(this, this.options.defaultScope) Utils.injectScope.call(this, this.options.defaultScope)
...@@ -141,6 +142,26 @@ module.exports = (function() { ...@@ -141,6 +142,26 @@ module.exports = (function() {
this.DAO.prototype.rawAttributes = this.rawAttributes; this.DAO.prototype.rawAttributes = this.rawAttributes;
this.DAO.prototype._hasPrimaryKeys = this.options.hasPrimaryKeys
this.DAO.prototype._isPrimaryKey = Utils._.memoize(function (key) {
return self.primaryKeyAttributes.indexOf(key) !== -1
})
if (this.options.timestamps) {
this.DAO.prototype._timestampAttributes = {
createdAt: Utils._.underscoredIf(this.options.createdAt, this.options.underscored),
updatedAt: Utils._.underscoredIf(this.options.updatedAt, this.options.underscored),
deletedAt: Utils._.underscoredIf(this.options.deletedAt, this.options.underscored)
}
this.DAO.prototype._readOnlyAttributes = Object.keys(this.DAO.prototype._timestampAttributes)
}
this.DAO.prototype._hasReadOnlyAttributes = this.DAO.prototype._readOnlyAttributes && this.DAO.prototype._readOnlyAttributes.length
this.DAO.prototype._isReadOnlyAttribute = Utils._.memoize(function (key) {
return self.DAO.prototype._hasReadOnlyAttributes && self.DAO.prototype._readOnlyAttributes.indexOf(key) !== -1
})
if (this.options.instanceMethods) { if (this.options.instanceMethods) {
Utils._.each(this.options.instanceMethods, function(fct, name) { Utils._.each(this.options.instanceMethods, function(fct, name) {
self.DAO.prototype[name] = fct self.DAO.prototype[name] = fct
...@@ -150,6 +171,7 @@ module.exports = (function() { ...@@ -150,6 +171,7 @@ module.exports = (function() {
this.refreshAttributes(); this.refreshAttributes();
this.DAO.prototype.booleanValues = [] this.DAO.prototype.booleanValues = []
this.DAO.prototype.dateAttributes = []
this.DAO.prototype.defaultValues = {} this.DAO.prototype.defaultValues = {}
this.DAO.prototype.validators = {} this.DAO.prototype.validators = {}
...@@ -157,6 +179,9 @@ module.exports = (function() { ...@@ -157,6 +179,9 @@ module.exports = (function() {
if (((definition === DataTypes.BOOLEAN) || (definition.type === DataTypes.BOOLEAN))) { if (((definition === DataTypes.BOOLEAN) || (definition.type === DataTypes.BOOLEAN))) {
self.DAO.prototype.booleanValues.push(name); self.DAO.prototype.booleanValues.push(name);
} }
if (((definition === DataTypes.DATE) || (definition.type === DataTypes.DATE) || (definition.originalType === DataTypes.DATE))) {
self.DAO.prototype.dateAttributes.push(name);
}
if (definition.hasOwnProperty('defaultValue')) { if (definition.hasOwnProperty('defaultValue')) {
self.DAO.prototype.defaultValues[name] = Utils._.partial( self.DAO.prototype.defaultValues[name] = Utils._.partial(
Utils.toDefaultValue, definition.defaultValue) Utils.toDefaultValue, definition.defaultValue)
...@@ -167,6 +192,16 @@ module.exports = (function() { ...@@ -167,6 +192,16 @@ module.exports = (function() {
} }
}) })
this.DAO.prototype._hasBooleanAttributes = !!this.DAO.prototype.booleanValues.length
this.DAO.prototype._isBooleanAttribute = Utils._.memoize(function (key) {
return self.DAO.prototype.booleanValues.indexOf(key) !== -1
})
this.DAO.prototype._hasDateAttributes = !!this.DAO.prototype.dateAttributes.length
this.DAO.prototype._isDateAttribute = Utils._.memoize(function (key) {
return self.DAO.prototype.dateAttributes.indexOf(key) !== -1
})
this.DAO.prototype.__factory = this this.DAO.prototype.__factory = this
this.DAO.prototype.daoFactory = this this.DAO.prototype.daoFactory = this
this.DAO.prototype.Model = this this.DAO.prototype.Model = this
...@@ -180,31 +215,42 @@ module.exports = (function() { ...@@ -180,31 +215,42 @@ module.exports = (function() {
var self = this var self = this
, attributeManipulation = {}; , attributeManipulation = {};
this.DAO.prototype._customGetters = {}
this.DAO.prototype._customSetters = {}
Utils._.each(['get', 'set'], function(type) { Utils._.each(['get', 'set'], function(type) {
var opt = type + 'terMethods' var opt = type + 'terMethods'
, funcs = Utils._.isObject(self.options[opt]) ? self.options[opt] : {} , funcs = Utils._.clone(Utils._.isObject(self.options[opt]) ? self.options[opt] : {})
, _custom = type === 'get' ? self.DAO.prototype._customGetters : self.DAO.prototype._customSetters
Utils._.each(funcs, function (method, attribute) {
_custom[attribute] = method
if (type === 'get') {
funcs[attribute] = function() {
return this.get(attribute)
}
}
if (type === 'set') {
funcs[attribute] = function(value) {
return this.set(attribute, value)
}
}
})
Utils._.each(self.rawAttributes, function(options, attribute) { Utils._.each(self.rawAttributes, function(options, attribute) {
if (options.hasOwnProperty(type)) { if (options.hasOwnProperty(type)) {
funcs[attribute] = options[type] _custom[attribute] = options[type]
} else if (typeof funcs[attribute] === "undefined") { }
if (type === 'get') {
funcs[attribute] = function() { return this.dataValues[attribute]; } if (type === 'get') {
funcs[attribute] = function() {
return this.get(attribute)
} }
if (type === 'set') { }
funcs[attribute] = function(value) { if (type === 'set') {
if (Utils.hasChanged(this.dataValues[attribute], value)) { funcs[attribute] = function(value) {
//Only dirty the object if the change is not due to id, touchedAt, createdAt or updatedAt being initiated return this.set(attribute, value)
var updatedAtAttr = Utils._.underscoredIf(this.__options.updatedAt, this.__options.underscored)
, createdAtAttr = Utils._.underscoredIf(this.__options.createdAt, this.__options.underscored)
, touchedAtAttr = Utils._.underscoredIf(this.__options.touchedAt, this.__options.underscored)
if (this.dataValues[attribute] || (attribute != 'id' && attribute != touchedAtAttr && attribute != createdAtAttr && attribute != updatedAtAttr)) {
this.isDirty = true
}
}
this.dataValues[attribute] = value
}
} }
} }
}) })
...@@ -219,6 +265,9 @@ module.exports = (function() { ...@@ -219,6 +265,9 @@ module.exports = (function() {
}) })
}) })
this.DAO.prototype._hasCustomGetters = Object.keys(this.DAO.prototype._customGetters).length
this.DAO.prototype._hasCustomSetters = Object.keys(this.DAO.prototype._customSetters).length
Object.defineProperties(this.DAO.prototype, attributeManipulation) Object.defineProperties(this.DAO.prototype, attributeManipulation)
this.DAO.prototype.attributes = Object.keys(this.DAO.prototype.rawAttributes) this.DAO.prototype.attributes = Object.keys(this.DAO.prototype.rawAttributes)
} }
...@@ -373,7 +422,7 @@ module.exports = (function() { ...@@ -373,7 +422,7 @@ module.exports = (function() {
options = optClone(options) options = optClone(options)
if (typeof options === 'object') { if (typeof options === 'object') {
if (options.hasOwnProperty('include')) { if (options.hasOwnProperty('include') && options.include) {
hasJoin = true hasJoin = true
validateIncludedElements.call(this, options) validateIncludedElements.call(this, options)
...@@ -456,7 +505,7 @@ module.exports = (function() { ...@@ -456,7 +505,7 @@ module.exports = (function() {
} else if (typeof options === 'object') { } else if (typeof options === 'object') {
options = Utils._.clone(options) options = Utils._.clone(options)
if (options.hasOwnProperty('include')) { if (options.hasOwnProperty('include') && options.include) {
hasJoin = true hasJoin = true
validateIncludedElements.call(this, options) validateIncludedElements.call(this, options)
...@@ -554,13 +603,8 @@ module.exports = (function() { ...@@ -554,13 +603,8 @@ module.exports = (function() {
DAOFactory.prototype.build = function(values, options) { DAOFactory.prototype.build = function(values, options) {
options = options || { isNewRecord: true, isDirty: true } options = options || { isNewRecord: true, isDirty: true }
if (options.hasOwnProperty('include') && options.include && (!options.includeValidated || !options.includeNames)) { if (options.hasOwnProperty('include') && options.include && !options.includeValidated) {
options.includeNames = [] validateIncludedElements.call(this, options)
options.include = options.include.map(function(include) {
include = validateIncludedElement.call(this, include)
options.includeNames.push(include.as)
return include
}.bind(this))
} }
if (options.includeNames) { if (options.includeNames) {
...@@ -1163,7 +1207,9 @@ module.exports = (function() { ...@@ -1163,7 +1207,9 @@ module.exports = (function() {
var addDefaultAttributes = function() { var addDefaultAttributes = function() {
var self = this var self = this
, defaultAttributes = { , head = {}
, tail = {}
, head = {
id: { id: {
type: DataTypes.INTEGER, type: DataTypes.INTEGER,
allowNull: false, allowNull: false,
...@@ -1173,18 +1219,29 @@ module.exports = (function() { ...@@ -1173,18 +1219,29 @@ module.exports = (function() {
} }
if (this.hasPrimaryKeys) { if (this.hasPrimaryKeys) {
defaultAttributes = {} head = {}
} }
if (this.options.timestamps) { if (this.options.timestamps) {
defaultAttributes[Utils._.underscoredIf(this.options.createdAt, this.options.underscored)] = {type: DataTypes.DATE, allowNull: false} tail[Utils._.underscoredIf(this.options.createdAt, this.options.underscored)] = {type: DataTypes.DATE, allowNull: false}
defaultAttributes[Utils._.underscoredIf(this.options.updatedAt, this.options.underscored)] = {type: DataTypes.DATE, allowNull: false} tail[Utils._.underscoredIf(this.options.updatedAt, this.options.underscored)] = {type: DataTypes.DATE, allowNull: false}
if (this.options.paranoid) if (this.options.paranoid)
defaultAttributes[Utils._.underscoredIf(this.options.deletedAt, this.options.underscored)] = {type: DataTypes.DATE} tail[Utils._.underscoredIf(this.options.deletedAt, this.options.underscored)] = {type: DataTypes.DATE}
} }
Utils._.each(defaultAttributes, function(value, attr) { var existingAttributes = Utils._.clone(self.rawAttributes)
self.rawAttributes = {}
Utils._.each(head, function(value, attr) {
self.rawAttributes[attr] = value
})
Utils._.each(existingAttributes, function(value, attr) {
self.rawAttributes[attr] = value
})
Utils._.each(tail, function(value, attr) {
if (Utils._.isUndefined(self.rawAttributes[attr])) { if (Utils._.isUndefined(self.rawAttributes[attr])) {
self.rawAttributes[attr] = value self.rawAttributes[attr] = value
} }
......
...@@ -93,9 +93,8 @@ var validateAttributes = function() { ...@@ -93,9 +93,8 @@ var validateAttributes = function() {
var self = this var self = this
, errors = {} , errors = {}
// for each field and value Utils._.each(this.model.rawAttributes, function(rawAttribute, field) {
Utils._.each(this.model.dataValues, function(value, field) { var value = self.model.dataValues[field] || undefined
var rawAttribute = self.model.rawAttributes[field]
, hasAllowedNull = ((rawAttribute === undefined || rawAttribute.allowNull === true) && ((value === null) || (value === undefined))) , hasAllowedNull = ((rawAttribute === undefined || rawAttribute.allowNull === true) && ((value === null) || (value === undefined)))
, isSkipped = self.options.skip.length > 0 && self.options.skip.indexOf(field) === -1 , isSkipped = self.options.skip.length > 0 && self.options.skip.indexOf(field) === -1
......
...@@ -8,17 +8,16 @@ var Utils = require("./utils") ...@@ -8,17 +8,16 @@ var Utils = require("./utils")
module.exports = (function() { module.exports = (function() {
var DAO = function(values, options) { var DAO = function(values, options) {
this.dataValues = {} this.dataValues = {}
this._previousDataValues = {}
this.__options = this.__factory.options this.__options = this.__factory.options
this.options = options this.options = options
this.hasPrimaryKeys = this.__factory.options.hasPrimaryKeys this.hasPrimaryKeys = this.__factory.options.hasPrimaryKeys
// What is selected values even used for? // What is selected values even used for?
this.selectedValues = options.include ? _.omit(values, options.includeNames) : values this.selectedValues = options.include ? _.omit(values, options.includeNames) : values
this.__eagerlyLoadedAssociations = [] this.__eagerlyLoadedAssociations = []
this.isNewRecord = options.isNewRecord this.isNewRecord = options.isNewRecord
initAttributes.call(this, values, options) initValues.call(this, values, options)
this.isDirty = options.isDirty
} }
Utils._.extend(DAO.prototype, Mixin.prototype) Utils._.extend(DAO.prototype, Mixin.prototype)
...@@ -42,7 +41,13 @@ module.exports = (function() { ...@@ -42,7 +41,13 @@ module.exports = (function() {
Object.defineProperty(DAO.prototype, 'values', { Object.defineProperty(DAO.prototype, 'values', {
get: function() { get: function() {
return this.dataValues return this.get()
}
})
Object.defineProperty(DAO.prototype, 'isDirty', {
get: function() {
return !!this.changed()
} }
}) })
...@@ -77,18 +82,180 @@ module.exports = (function() { ...@@ -77,18 +82,180 @@ module.exports = (function() {
} }
}) })
DAO.prototype.getDataValue = function(name) { DAO.prototype.getDataValue = function(key) {
return this.dataValues && this.dataValues.hasOwnProperty(name) ? this.dataValues[name] : this[name] return this.dataValues[key]
}
DAO.prototype.setDataValue = function(key, value) {
this.dataValues[key] = value
}
DAO.prototype.get = function (key) {
if (key) {
if (this._customGetters[key]) {
return this._customGetters[key].call(this, key)
}
return this.dataValues[key]
}
if (this._hasCustomGetters) {
var values = {}
, key
for (key in this._customGetters) {
if (this._customGetters.hasOwnProperty(key)) {
values[key] = this.get(key)
}
}
for (key in this.dataValues) {
if (!values.hasOwnProperty(key) && this.dataValues.hasOwnProperty(key)) {
values[key] = this.dataValues[key]
}
}
return values
}
return this.dataValues
}
DAO.prototype.set = function (key, value, options) {
var values
, originalValue
if (typeof key === "object") {
values = key
options = value
options || (options = {})
if (options.reset) {
this.dataValues = {}
}
// If raw, and we're not dealing with includes, just set it straight on the dataValues object
if (options.raw && !(this.options && this.options.include) && !this._hasBooleanAttributes) {
if (Object.keys(this.dataValues).length) {
this.dataValues = _.extend(this.dataValues, values)
} else {
this.dataValues = values
}
// If raw, .changed() shouldn't be true
this._previousDataValues = _.clone(this.dataValues)
} else {
// Loop and call set
for (key in values) {
this.set(key, values[key], options)
}
if (options.raw) {
// If raw, .changed() shouldn't be true
this._previousDataValues = _.clone(this.dataValues)
}
}
} else {
options || (options = {})
originalValue = this.dataValues[key]
// If not raw, and there's a customer setter
if (!options.raw && this._customSetters[key]) {
this._customSetters[key].call(this, value, key)
} else {
// Check if we have included models, and if this key matches the include model names/aliases
if (this.options && this.options.include && this.options.includeNames.indexOf(key) !== -1) {
// Pass it on to the include handler
this._setInclude(key, value, options)
return
} else {
// If attempting to set primary key and primary key is already defined, return
if (this._hasPrimaryKeys && originalValue && this._isPrimaryKey(key)) {
return
}
// If attempting to set read only attributes, return
if (!options.raw && this._hasReadOnlyAttributes && this._isReadOnlyAttribute(key)) {
return
}
// Convert boolean-ish values to booleans
if (this._hasBooleanAttributes && this._isBooleanAttribute(key) && value !== null && value !== undefined) {
value = !!value
}
// Convert date fields to real date objects
if (this._hasDateAttributes && this._isDateAttribute(key) && value !== null && !(value instanceof Date)) {
value = new Date(value)
}
if (originalValue !== value) this._previousDataValues[key] = originalValue
this.dataValues[key] = value
}
}
}
} }
DAO.prototype.get = DAO.prototype.getDataValue
DAO.prototype.setDataValue = function(name, value) { DAO.prototype.changed = function(key) {
if (Utils.hasChanged(this.dataValues[name], value)) { if (key) {
this.isDirty = true if (this._isDateAttribute(key) && this._previousDataValues[key] && this.dataValues[key]) {
return this._previousDataValues[key].valueOf() !== this.dataValues[key].valueOf()
}
return this._previousDataValues[key] !== this.dataValues[key]
} }
this.dataValues[name] = value var changed = Object.keys(this.dataValues).filter(function (key) {
return this.changed(key)
}.bind(this))
return changed.length ? changed : false
} }
DAO.prototype.set = DAO.prototype.setDataValue
DAO.prototype.previous = function(key) {
return this._previousDataValues[key]
}
DAO.prototype._setInclude = function(key, value, options) {
if (!Array.isArray(value)) value = [value]
if (value[0] instanceof DAO) {
value = value.map(function (instance) {
return instance.dataValues
})
}
var include = _.find(this.options.include, function (include) {
return include.as === key || (include.as.slice(0,1).toLowerCase() + include.as.slice(1)) === key
})
var association = include.association
, self = this
var accessor = Utils._.camelize(key)
// downcase the first char
accessor = accessor.slice(0,1).toLowerCase() + accessor.slice(1)
value.forEach(function(data) {
var daoInstance = include.daoFactory.build(data, {
isNewRecord: false,
isDirty: false,
include: include.include,
includeNames: include.includeNames,
includeMap: include.includeMap,
includeValidated: true,
raw: options.raw
})
, isEmpty = !Utils.firstValueOfHash(daoInstance.identifiers)
if (association.isSingleAssociation) {
accessor = Utils.singularize(accessor, self.sequelize.language)
self.dataValues[accessor] = isEmpty ? null : daoInstance
self[accessor] = self.dataValues[accessor]
} else {
if (!self.dataValues[accessor]) {
self.dataValues[accessor] = []
self[accessor] = self.dataValues[accessor]
}
if (!isEmpty) {
self.dataValues[accessor].push(daoInstance)
}
}
}.bind(this))
};
// if an array with field names is passed to save() // if an array with field names is passed to save()
// only those fields will be updated // only those fields will be updated
...@@ -99,10 +266,14 @@ module.exports = (function() { ...@@ -99,10 +266,14 @@ module.exports = (function() {
options = Utils._.extend({}, options, fieldsOrOptions) options = Utils._.extend({}, options, fieldsOrOptions)
var self = this if (!options.fields) {
, values = options.fields ? {} : (this.options.includeNames ? _.omit(this.dataValues, this.options.includeNames) : this.dataValues) options.fields = this.attributes
, updatedAtAttr = Utils._.underscoredIf(this.__options.updatedAt, this.__options.underscored) }
, createdAtAttr = Utils._.underscoredIf(this.__options.createdAt, this.__options.underscored)
var self = this
, values = {}
, updatedAtAttr = Utils._.underscoredIf(this.__options.updatedAt, this.__options.underscored)
, createdAtAttr = Utils._.underscoredIf(this.__options.createdAt, this.__options.underscored)
if (options.fields) { if (options.fields) {
if (self.__options.timestamps) { if (self.__options.timestamps) {
...@@ -114,20 +285,17 @@ module.exports = (function() { ...@@ -114,20 +285,17 @@ module.exports = (function() {
options.fields.push(createdAtAttr) options.fields.push(createdAtAttr)
} }
} }
var tmpVals = self.dataValues
options.fields.forEach(function(field) {
if (tmpVals[field] !== undefined) {
values[field] = tmpVals[field]
}
})
} }
return new Utils.CustomEventEmitter(function(emitter) { return new Utils.CustomEventEmitter(function(emitter) {
self.hookValidate().error(function (err) { self.hookValidate().error(function (err) {
return emitter.emit('error', err) return emitter.emit('error', err)
}).success(function() { }).success(function() {
options.fields.forEach(function(field) {
if (self.dataValues[field] !== undefined) {
values[field] = self.dataValues[field]
}
})
for (var attrName in self.daoFactory.rawAttributes) { for (var attrName in self.daoFactory.rawAttributes) {
if (self.daoFactory.rawAttributes.hasOwnProperty(attrName)) { if (self.daoFactory.rawAttributes.hasOwnProperty(attrName)) {
...@@ -157,8 +325,8 @@ module.exports = (function() { ...@@ -157,8 +325,8 @@ module.exports = (function() {
} }
} }
if (self.__options.timestamps && self.dataValues.hasOwnProperty(updatedAtAttr)) { if (self.__options.timestamps) {
self.dataValues[updatedAtAttr] = values[updatedAtAttr] = ( values[updatedAtAttr] = (
( (
self.isNewRecord self.isNewRecord
&& !!self.daoFactory.rawAttributes[updatedAtAttr] && !!self.daoFactory.rawAttributes[updatedAtAttr]
...@@ -166,6 +334,16 @@ module.exports = (function() { ...@@ -166,6 +334,16 @@ module.exports = (function() {
) )
? self.daoFactory.rawAttributes[updatedAtAttr].defaultValue ? self.daoFactory.rawAttributes[updatedAtAttr].defaultValue
: Utils.now(self.sequelize.options.dialect)) : Utils.now(self.sequelize.options.dialect))
if (self.isNewRecord && !values[createdAtAttr]) {
values[createdAtAttr] = (
(
!!self.daoFactory.rawAttributes[createdAtAttr]
&& !!self.daoFactory.rawAttributes[createdAtAttr].defaultValue
)
? self.daoFactory.rawAttributes[createdAtAttr].defaultValue
: values[updatedAtAttr])
}
} }
var query = null var query = null
...@@ -173,7 +351,6 @@ module.exports = (function() { ...@@ -173,7 +351,6 @@ module.exports = (function() {
, hook = '' , hook = ''
if (self.isNewRecord) { if (self.isNewRecord) {
self.isDirty = false
query = 'insert' query = 'insert'
args = [self, self.QueryInterface.QueryGenerator.addSchema(self.__factory), values, options] args = [self, self.QueryInterface.QueryGenerator.addSchema(self.__factory), values, options]
hook = 'Create' hook = 'Create'
...@@ -184,7 +361,6 @@ module.exports = (function() { ...@@ -184,7 +361,6 @@ module.exports = (function() {
identifier = self.__options.whereCollection; identifier = self.__options.whereCollection;
} }
self.isDirty = false
query = 'update' query = 'update'
args = [self, self.QueryInterface.QueryGenerator.addSchema(self.__factory), values, identifier, options] args = [self, self.QueryInterface.QueryGenerator.addSchema(self.__factory), values, identifier, options]
hook = 'Update' hook = 'Update'
...@@ -195,23 +371,30 @@ module.exports = (function() { ...@@ -195,23 +371,30 @@ module.exports = (function() {
return emitter.emit('error', err) return emitter.emit('error', err)
} }
// redeclare our new values args[2] = values = newValues || values
args[2] = newValues || args[2] // Newest values need to be on the dao since the dao is returned from the query interface
self.dataValues = _.extend(self.dataValues, values)
self.QueryInterface[query].apply(self.QueryInterface, args) self.QueryInterface[query].apply(self.QueryInterface, args)
.on('sql', function(sql) { .proxy(emitter, {events: ['sql', 'error']})
emitter.emit('sql', sql)
})
.error(function(err) {
emitter.emit('error', err)
})
.success(function(result) { .success(function(result) {
self.__factory.runHooks('after' + hook, result.values, function(err, newValues) { // Transfer database generated values (defaults, autoincrement, etc)
values = _.extend(values, result.values)
// Ensure new values are on DAO, and reset previousDataValues
result.dataValues = _.extend(result.dataValues, values)
result._previousDataValues = _.clone(result.dataValues)
self.__factory.runHooks('after' + hook, values, function(err, newValues) {
if (!!err) { if (!!err) {
return emitter.emit('error', err) return emitter.emit('error', err)
} }
result.dataValues = _.extend(result.dataValues, newValues) if (newValues) {
// Repeat value assignment incase afterHook changed anything
result.dataValues = _.extend(result.dataValues, newValues)
result._previousDataValues = _.clone(result.dataValues)
}
emitter.emit('success', result) emitter.emit('success', result)
}) })
}) })
...@@ -237,16 +420,12 @@ module.exports = (function() { ...@@ -237,16 +420,12 @@ module.exports = (function() {
this.__factory.find({ this.__factory.find({
where: where, where: where,
limit: 1, limit: 1,
include: this.options.include || [] include: this.options.include || null
}, options) }, options)
.on('sql', function(sql) { emitter.emit('sql', sql) }) .on('sql', function(sql) { emitter.emit('sql', sql) })
.on('error', function(error) { emitter.emit('error', error) }) .on('error', function(error) { emitter.emit('error', error) })
.on('success', function(obj) { .on('success', function(obj) {
for (var valueName in obj.values) { this.set(obj.dataValues, {raw: true, reset: true})
if (obj.values.hasOwnProperty(valueName)) {
this[valueName] = obj.values[valueName]
}
}
this.isDirty = false this.isDirty = false
emitter.emit('success', this) emitter.emit('success', this)
}.bind(this)) }.bind(this))
...@@ -284,42 +463,7 @@ module.exports = (function() { ...@@ -284,42 +463,7 @@ module.exports = (function() {
} }
DAO.prototype.setAttributes = function(updates) { DAO.prototype.setAttributes = function(updates) {
var self = this this.set(updates)
var readOnlyAttributes = Object.keys(this.__factory.primaryKeys)
readOnlyAttributes.push('id')
if (this.isNewRecord !== true) {
readOnlyAttributes.push(Utils._.underscoredIf(this.daoFactory.options.createdAt, this.daoFactory.options.underscored))
}
// readOnlyAttributes.push(this.daoFactory.options.underscored === true ? 'updated_at' : 'updatedAt')
readOnlyAttributes.push(this.daoFactory.options.deletedAt, this.daoFactory.options.underscored)
var isDirty = this.isDirty
Utils._.each(updates, function(value, attr) {
var updateAllowed = (
(readOnlyAttributes.indexOf(attr) === -1) &&
(readOnlyAttributes.indexOf(Utils._.underscored(attr)) === -1) &&
(self.attributes.indexOf(attr) > -1)
)
if (updateAllowed) {
if (Utils.hasChanged(self[attr], value)) {
isDirty = true
}
self[attr] = value
}
})
// since we're updating the record, we should be updating the updatedAt column..
if (this.daoFactory.options.timestamps === true) {
isDirty = true
self[Utils._.underscoredIf(this.daoFactory.options.updatedAt, this.daoFactory.options.underscored)] = new Date()
}
this.isDirty = isDirty
} }
DAO.prototype.destroy = function(options) { DAO.prototype.destroy = function(options) {
...@@ -468,18 +612,17 @@ module.exports = (function() { ...@@ -468,18 +612,17 @@ module.exports = (function() {
} }
DAO.prototype.toJSON = function() { DAO.prototype.toJSON = function() {
return this.dataValues; return this.get();
} }
// private // private
var initAttributes = function(values, options) { var initValues = function(values, options) {
// set id to null if not passed as value, a newly created dao has no id // set id to null if not passed as value, a newly created dao has no id
var defaults = this.hasPrimaryKeys ? {} : { id: null }, var defaults = this.hasPrimaryKeys ? {} : { id: null },
attrs = {},
key; key;
// add all passed values to the dao and store the attribute names in this.attributes values = values && _.clone(values) || {}
if (options.isNewRecord) { if (options.isNewRecord) {
if (this.hasDefaultValues) { if (this.hasDefaultValues) {
...@@ -491,85 +634,31 @@ module.exports = (function() { ...@@ -491,85 +634,31 @@ module.exports = (function() {
} }
if (this.__options.timestamps) { if (this.__options.timestamps) {
if (!this.defaultValues[Utils._.underscoredIf(this.__options.createdAt, this.__options.underscored)]) { if (defaults[this._timestampAttributes.createdAt]) {
defaults[Utils._.underscoredIf(this.__options.createdAt, this.__options.underscored)] = Utils.now(this.sequelize.options.dialect) this.dataValues[this._timestampAttributes.createdAt] = Utils.toDefaultValue(defaults[this._timestampAttributes.createdAt]);
delete defaults[this._timestampAttributes.createdAt];
} }
if (!this.defaultValues[Utils._.underscoredIf(this.__options.updatedAt, this.__options.underscored)]) { if (defaults[this._timestampAttributes.updatedAt]) {
defaults[Utils._.underscoredIf(this.__options.updatedAt, this.__options.underscored)] = Utils.now(this.sequelize.options.dialect) this.dataValues[this._timestampAttributes.updatedAt] = Utils.toDefaultValue(defaults[this._timestampAttributes.updatedAt]);
delete defaults[this._timestampAttributes.updatedAt];
} }
if (this.__options.paranoid && !this.defaultValues[Utils._.underscoredIf(this.__options.deletedAt, this.__options.underscored)]) { if (defaults[this._timestampAttributes.deletedAt]) {
defaults[Utils._.underscoredIf(this.__options.deletedAt, this.__options.underscored)] = null this.dataValues[this._timestampAttributes.deletedAt] = Utils.toDefaultValue(defaults[this._timestampAttributes.deletedAt]);
delete defaults[this._timestampAttributes.deletedAt];
} }
} }
} }
if (Object.keys(defaults).length) {
if (Utils._.size(defaults)) {
for (key in defaults) { for (key in defaults) {
attrs[key] = Utils.toDefaultValue(defaults[key]) if (!values.hasOwnProperty(key)) {
} values[key] = Utils.toDefaultValue(defaults[key])
}
Utils._.each(this.attributes, function(key) {
if (!attrs.hasOwnProperty(key)) {
attrs[key] = undefined
}
})
if (values) {
for (key in values) {
if (values.hasOwnProperty(key)) {
attrs[key] = values[key]
} }
} }
} }
for (key in attrs) { this.set(values, options)
if (options.include && options.includeNames.indexOf(key) !== -1) {
if (!Array.isArray(attrs[key])) attrs[key] = [attrs[key]];
var include = _.find(options.include, function (include) {
return include.as === key || (include.as.slice(0,1).toLowerCase() + include.as.slice(1)) === key
})
var association = include.association
, self = this
var accessor = Utils._.camelize(key)
// downcase the first char
accessor = accessor.slice(0,1).toLowerCase() + accessor.slice(1)
attrs[key].forEach(function(data) {
var daoInstance = include.daoFactory.build(data, {
isNewRecord: false,
isDirty: false,
include: include.include,
includeNames: include.includeNames,
includeMap: include.includeMap,
includeValidated: true
})
, isEmpty = !Utils.firstValueOfHash(daoInstance.identifiers)
if (association.isSingleAssociation) {
accessor = Utils.singularize(accessor, self.sequelize.language)
self.dataValues[accessor] = isEmpty ? null : daoInstance
self[accessor] = self.dataValues[accessor]
} else {
if (!self.dataValues[accessor]) {
self.dataValues[accessor] = []
self[accessor] = self.dataValues[accessor]
}
if (!isEmpty) {
self.dataValues[accessor].push(daoInstance)
}
}
}.bind(this))
} else {
this.addAttribute(key, attrs[key])
}
}
} }
return DAO return DAO
......
var AbstractDialect = function() {
}
AbstractDialect.prototype.supports = {
'RETURNING': false,
'DEFAULT': true,
'DEFAULT VALUES': false,
'VALUES ()': false
}
module.exports = AbstractDialect
\ No newline at end of file
...@@ -35,8 +35,12 @@ module.exports = (function() { ...@@ -35,8 +35,12 @@ module.exports = (function() {
- originalTableName: Name of the table before execution. - originalTableName: Name of the table before execution.
- futureTableName: Name of the table after execution. - futureTableName: Name of the table after execution.
*/ */
renameTableQuery: function(originalTableName, futureTableName) { renameTableQuery: function(before, after) {
throwMethodUndefined('renameTableQuery') var query = "ALTER TABLE <%= before %> RENAME TO <%= after %>;"
return Utils._.template(query)({
before: this.quoteTable(before),
after: this.quoteTable(after)
})
}, },
/* /*
...@@ -100,10 +104,56 @@ module.exports = (function() { ...@@ -100,10 +104,56 @@ module.exports = (function() {
/* /*
Returns an insert into command. Parameters: table name + hash of attribute-value-pairs. Returns an insert into command. Parameters: table name + hash of attribute-value-pairs.
*/ */
insertQuery: function(tableName, attrValueHash) { insertQuery: function(table, valueHash, modelAttributes) {
throwMethodUndefined('insertQuery') var query
}, , valueQuery = "INSERT INTO <%= table %> (<%= attributes %>) VALUES (<%= values %>)"
, emptyQuery = "INSERT INTO <%= table %>"
, fields = []
, values = []
, key
, value
if (this._dialect.supports['DEFAULT VALUES']) {
emptyQuery += " DEFAULT VALUES"
} else if (this._dialect.supports['VALUES ()']) {
emptyQuery += " VALUES ()"
}
if (this._dialect.supports['RETURNING']) {
valueQuery += " RETURNING *"
emptyQuery += " RETURNING *"
}
valueHash = Utils.removeNullValuesFromHash(valueHash, this.options.omitNull)
for (key in valueHash) {
if (valueHash.hasOwnProperty(key)) {
value = valueHash[key]
fields.push(this.quoteIdentifier(key))
// SERIALS' can't be NULL in postgresql, use DEFAULT where supported
if (modelAttributes && modelAttributes[key] && modelAttributes[key].autoIncrement === true && !value) {
if (this._dialect.supports['DEFAULT']) {
values.push('DEFAULT')
} else {
values.push(this.escape(null))
}
} else {
values.push(this.escape(value, (modelAttributes && modelAttributes[key]) || undefined))
}
}
}
var replacements = {
table: this.quoteTable(table),
attributes: fields.join(","),
values: values.join(",")
}
query = (replacements.attributes.length ? valueQuery : emptyQuery) + ";"
return Utils._.template(query)(replacements)
},
/* /*
Returns an insert into command for multiple values. Returns an insert into command for multiple values.
Parameters: table name + list of hashes of attribute-value-pairs. Parameters: table name + list of hashes of attribute-value-pairs.
...@@ -122,8 +172,29 @@ module.exports = (function() { ...@@ -122,8 +172,29 @@ module.exports = (function() {
OR a string with conditions (e.g. 'name="foo"'). OR a string with conditions (e.g. 'name="foo"').
If you use a string, you have to escape it on your own. If you use a string, you have to escape it on your own.
*/ */
updateQuery: function(tableName, values, where) { updateQuery: function(tableName, attrValueHash, where, options, attributes) {
throwMethodUndefined('updateQuery') attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull, options)
var query
, values = []
query = "UPDATE <%= table %> SET <%= values %> WHERE <%= where %>"
if (this._dialect.supports['RETURNING']) {
query += " RETURNING *"
}
for (var key in attrValueHash) {
var value = attrValueHash[key]
values.push(this.quoteIdentifier(key) + "=" + this.escape(value, (!!attributes && !!attributes[key] ? attributes[key] : undefined)))
}
var replacements = {
table: this.quoteTable(tableName),
values: values.join(","),
where: this.getWhereConditions(where)
}
return Utils._.template(query)(replacements)
}, },
/* /*
...@@ -169,8 +240,35 @@ module.exports = (function() { ...@@ -169,8 +240,35 @@ module.exports = (function() {
OR a string with conditions (e.g. 'name="foo"'). OR a string with conditions (e.g. 'name="foo"').
If you use a string, you have to escape it on your own. If you use a string, you have to escape it on your own.
*/ */
incrementQuery: function(tableName, values, where) { incrementQuery: function(tableName, attrValueHash, where, options) {
throwMethodUndefined('incrementQuery') attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull)
var query
, values = []
query = "UPDATE <%= table %> SET <%= values %> WHERE <%= where %>"
if (this._dialect.supports['RETURNING']) {
query += " RETURNING *"
}
for (var key in attrValueHash) {
var value = attrValueHash[key]
values.push(this.quoteIdentifier(key) + "=" + this.quoteIdentifier(key) + " + " + this.escape(value))
}
options = options || {}
for (var key in options) {
var value = options[key];
values.push(this.quoteIdentifier(key) + "=" + this.escape(value))
}
var replacements = {
table: this.quoteIdentifiers(tableName),
values: values.join(","),
where: this.getWhereConditions(where)
}
return Utils._.template(query)(replacements)
}, },
/* /*
...@@ -489,11 +587,11 @@ module.exports = (function() { ...@@ -489,11 +587,11 @@ module.exports = (function() {
* @return {String} The generated sql query. * @return {String} The generated sql query.
*/ */
setAutocommitQuery: function(value) { setAutocommitQuery: function(value) {
throwMethodUndefined('setAutocommitQuery') return "SET autocommit = " + (!!value ? 1 : 0) + ";"
}, },
setIsolationLevelQuery: function(value) { setIsolationLevelQuery: function(value) {
throwMethodUndefined('setIsolationLevelQuery') return "SET SESSION TRANSACTION ISOLATION LEVEL " + value + ";"
}, },
/** /**
...@@ -503,7 +601,7 @@ module.exports = (function() { ...@@ -503,7 +601,7 @@ module.exports = (function() {
* @return {String} The generated sql query. * @return {String} The generated sql query.
*/ */
startTransactionQuery: function(options) { startTransactionQuery: function(options) {
throwMethodUndefined('startTransactionQuery') return "START TRANSACTION;"
}, },
/** /**
...@@ -513,7 +611,7 @@ module.exports = (function() { ...@@ -513,7 +611,7 @@ module.exports = (function() {
* @return {String} The generated sql query. * @return {String} The generated sql query.
*/ */
commitTransactionQuery: function(options) { commitTransactionQuery: function(options) {
throwMethodUndefined('commitTransactionQuery') return "COMMIT;"
}, },
/** /**
...@@ -523,7 +621,7 @@ module.exports = (function() { ...@@ -523,7 +621,7 @@ module.exports = (function() {
* @return {String} The generated sql query. * @return {String} The generated sql query.
*/ */
rollbackTransactionQuery: function(options) { rollbackTransactionQuery: function(options) {
throwMethodUndefined('rollbackTransactionQuery') return "ROLLBACK;"
}, },
addLimitAndOffset: function(options, query){ addLimitAndOffset: function(options, query){
......
...@@ -227,7 +227,8 @@ module.exports = (function() { ...@@ -227,7 +227,8 @@ module.exports = (function() {
include:this.options.include, include:this.options.include,
includeNames: this.options.includeNames, includeNames: this.options.includeNames,
includeMap: this.options.includeMap, includeMap: this.options.includeMap,
includeValidated: true includeValidated: true,
raw: true
}) })
}.bind(this)) }.bind(this))
} else if (this.options.hasJoinTableModel === true) { } else if (this.options.hasJoinTableModel === true) {
...@@ -249,7 +250,7 @@ module.exports = (function() { ...@@ -249,7 +250,7 @@ module.exports = (function() {
// Regular queries // Regular queries
} else { } else {
result = results.map(function(result) { result = results.map(function(result) {
return this.callee.build(result, { isNewRecord: false, isDirty: false }) return this.callee.build(result, { isNewRecord: false, isDirty: false, raw: true })
}.bind(this)) }.bind(this))
} }
......
var _ = require('lodash')
, MySQL = require('./mysql')
var MariaDialect = function(sequelize) {
this.sequelize = sequelize
}
MariaDialect.prototype = _.extend({
}, MySQL.prototype)
module.exports = MariaDialect
\ No newline at end of file
var _ = require('lodash')
, Abstract = require('./abstract/dialect')
var MysqlDialect = function(sequelize) {
this.sequelize = sequelize
}
MysqlDialect.prototype.supports = _.extend(Abstract.prototype.supports, {
'VALUES ()': true
})
module.exports = MysqlDialect
\ No newline at end of file
...@@ -99,11 +99,6 @@ module.exports = (function() { ...@@ -99,11 +99,6 @@ module.exports = (function() {
}) })
}, },
renameTableQuery: function(before, after) {
var query = "RENAME TABLE `<%= before %>` TO `<%= after %>`;"
return Utils._.template(query)({ before: before, after: after })
},
showTablesQuery: function() { showTablesQuery: function() {
return 'SHOW TABLES;' return 'SHOW TABLES;'
}, },
...@@ -162,52 +157,34 @@ module.exports = (function() { ...@@ -162,52 +157,34 @@ module.exports = (function() {
return Utils._.template(query)({ tableName: tableName, attributes: attrString.join(', ') }) return Utils._.template(query)({ tableName: tableName, attributes: attrString.join(', ') })
}, },
insertQuery: function(tableName, attrValueHash) {
attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull)
var table = this.quoteIdentifier(tableName)
var attributes = Object.keys(attrValueHash).map(function(attr){return this.quoteIdentifier(attr)}.bind(this)).join(",")
var values = Utils._.values(attrValueHash).map(function(v) { return this.escape(v) }.bind(this)).join(",")
var query = "INSERT INTO " + table + " (" + attributes + ") VALUES (" + values + ");"
return query
},
bulkInsertQuery: function(tableName, attrValueHashes) { bulkInsertQuery: function(tableName, attrValueHashes) {
var tuples = [] var query = "INSERT INTO <%= table %> (<%= attributes %>) VALUES <%= tuples %>;"
, tuples = []
, allAttributes = []
Utils._.forEach(attrValueHashes, function(attrValueHash, i) {
Utils._.forEach(attrValueHash, function(value, key, hash) {
if (allAttributes.indexOf(key) === -1) allAttributes.push(key)
})
})
Utils._.forEach(attrValueHashes, function(attrValueHash) { Utils._.forEach(attrValueHashes, function(attrValueHash, i) {
tuples.push("(" + tuples.push("(" +
Utils._.values(attrValueHash).map(function(v) { return this.escape(v) }.bind(this)).join(",") + allAttributes.map(function (key) {
return this.escape(attrValueHash[key])
}.bind(this)).join(",") +
")") ")")
}.bind(this)) }.bind(this))
var table = this.quoteIdentifier(tableName) var replacements = {
var attributes = Object.keys(attrValueHashes[0]).map(function(attr){return this.quoteIdentifier(attr)}.bind(this)).join(",") table: this.quoteIdentifier(tableName),
attributes: allAttributes.map(function(attr){
var query = "INSERT INTO " + table + " (" + attributes + ") VALUES " + tuples.join(",") + ";" return this.quoteIdentifier(attr)
}.bind(this)).join(","),
return query tuples: tuples
},
updateQuery: function(tableName, attrValueHash, where, options) {
attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull, options)
var values = []
for (var key in attrValueHash) {
var value = attrValueHash[key]
, _value = this.escape(value)
values.push(this.quoteIdentifier(key) + "=" + _value)
} }
var query = "UPDATE " + this.quoteIdentifier(tableName) + return Utils._.template(query)(replacements)
" SET " + values.join(",") +
" WHERE " + this.getWhereConditions(where)
return query
}, },
deleteQuery: function(tableName, where, options) { deleteQuery: function(tableName, where, options) {
...@@ -244,33 +221,6 @@ module.exports = (function() { ...@@ -244,33 +221,6 @@ module.exports = (function() {
return query return query
}, },
incrementQuery: function (tableName, attrValueHash, where, options) {
attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull)
var values = []
for (var key in attrValueHash) {
var value = attrValueHash[key]
, _value = this.escape(value)
values.push(this.quoteIdentifier(key) + "=" + this.quoteIdentifier(key) + " + " + _value)
}
options = options || {}
for (var key in options) {
var value = options[key];
values.push(this.quoteIdentifier(key) + "=" + this.escape(value))
}
var table = this.quoteIdentifier(tableName)
values = values.join(",")
where = this.getWhereConditions(where)
var query = "UPDATE " + table + " SET " + values + " WHERE " + where
return query
},
addIndexQuery: function(tableName, attributes, options) { addIndexQuery: function(tableName, attributes, options) {
var transformedAttributes = attributes.map(function(attribute) { var transformedAttributes = attributes.map(function(attribute) {
if(typeof attribute === 'string') { if(typeof attribute === 'string') {
...@@ -333,38 +283,6 @@ module.exports = (function() { ...@@ -333,38 +283,6 @@ module.exports = (function() {
return Utils._.template(sql)({ tableName: tableName, indexName: indexName }) return Utils._.template(sql)({ tableName: tableName, indexName: indexName })
}, },
/**
* Returns a query that starts a transaction.
*
* @param {Boolean} value A boolean that states whether autocommit shall be done or not.
* @return {String} The generated sql query.
*/
setAutocommitQuery: function(value) {
return "SET autocommit = " + (!!value ? 1 : 0) + ";"
},
setIsolationLevelQuery: function(value) {
return "SET SESSION TRANSACTION ISOLATION LEVEL " + value + ";"
},
/**
* Returns a query that starts a transaction.
*
* @param {Object} options An object with options.
* @return {String} The generated sql query.
*/
startTransactionQuery: function(options) {
return "START TRANSACTION;"
},
commitTransactionQuery: function(options) {
return "COMMIT;"
},
rollbackTransactionQuery: function(options) {
return "ROLLBACK;"
},
attributesToSQL: function(attributes) { attributesToSQL: function(attributes) {
var result = {} var result = {}
...@@ -476,6 +394,10 @@ module.exports = (function() { ...@@ -476,6 +394,10 @@ module.exports = (function() {
return identifiers.split('.').map(function(v) { return this.quoteIdentifier(v, force) }.bind(this)).join('.') return identifiers.split('.').map(function(v) { return this.quoteIdentifier(v, force) }.bind(this)).join('.')
}, },
quoteTable: function(table) {
return this.quoteIdentifier(table)
},
/** /**
* Generates an SQL query that returns all foreign keys of a table. * Generates an SQL query that returns all foreign keys of a table.
* *
......
var _ = require('lodash')
, Abstract = require('./abstract/dialect')
var PostgresDialect = function(sequelize) {
this.sequelize = sequelize
}
PostgresDialect.prototype.supports = _.extend(Abstract.prototype.supports, {
'RETURNING': true,
'DEFAULT VALUES': true
})
module.exports = PostgresDialect
\ No newline at end of file
...@@ -97,14 +97,6 @@ module.exports = (function() { ...@@ -97,14 +97,6 @@ module.exports = (function() {
}) })
}, },
renameTableQuery: function(before, after) {
var query = "ALTER TABLE <%= before %> RENAME TO <%= after %>;"
return Utils._.template(query)({
before: this.quoteIdentifier(before),
after: this.quoteIdentifier(after)
})
},
showTablesQuery: function() { showTablesQuery: function() {
return "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public';" return "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public';"
}, },
...@@ -255,72 +247,38 @@ module.exports = (function() { ...@@ -255,72 +247,38 @@ module.exports = (function() {
}) })
}, },
insertQuery: function(tableName, attrValueHash, attributes) {
var query
, valueQuery = "INSERT INTO <%= table %> (<%= attributes %>) VALUES (<%= values %>) RETURNING *;"
, emptyQuery = "INSERT INTO <%= table %> DEFAULT VALUES RETURNING *;"
attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull)
// Remove serials that are null or undefined, which causes an error in PG
Utils._.forEach(attrValueHash, function(value, key, hash) {
if (tables[tableName]) {
switch (tables[tableName][key]) {
case 'bigserial':
case 'serial':
if ([null, undefined].indexOf(hash[key]) !== -1) delete hash[key]
break
}
}
});
var rowValues = []
Object.keys(attrValueHash).forEach(function(attr) {
rowValues[rowValues.length] = this.escape(attrValueHash[attr], (!!attributes && !!attributes[attr] ? attributes[attr] : undefined))
}.bind(this))
var replacements = {
table: this.quoteIdentifiers(tableName)
, attributes: Object.keys(attrValueHash).map(function(attr){
return this.quoteIdentifier(attr)
}.bind(this)).join(",")
, values: rowValues.join(",")
}
query = replacements.attributes.length ? valueQuery : emptyQuery
return Utils._.template(query)(replacements)
},
bulkInsertQuery: function(tableName, attrValueHashes) { bulkInsertQuery: function(tableName, attrValueHashes) {
var query = "INSERT INTO <%= table %> (<%= attributes %>) VALUES <%= tuples %> RETURNING *;" var query = "INSERT INTO <%= table %> (<%= attributes %>) VALUES <%= tuples %> RETURNING *;"
, tuples = [] , tuples = []
, serials = [] , serials = []
, allAttributes = []
Utils._.forEach(attrValueHashes, function(attrValueHash, i) { Utils._.forEach(attrValueHashes, function(attrValueHash, i) {
if (i === 0) { Utils._.forEach(attrValueHash, function(value, key, hash) {
Utils._.forEach(attrValueHash, function(value, key, hash) { if (allAttributes.indexOf(key) === -1) allAttributes.push(key)
if (tables[tableName] && tables[tableName][key]) {
if (['bigserial', 'serial'].indexOf(tables[tableName][key]) !== -1) { if (tables[tableName] && tables[tableName][key]) {
serials.push(key) if (['bigserial', 'serial'].indexOf(tables[tableName][key]) !== -1 && serials.indexOf(key) === -1) {
} serials.push(key)
} }
}) }
} })
})
Utils._.forEach(attrValueHashes, function(attrValueHash, i) {
tuples.push("(" + tuples.push("(" +
Utils._.map(attrValueHash, function(value, key){ allAttributes.map(function (key) {
if (serials.indexOf(key) !== -1) { if (serials.indexOf(key) !== -1) {
return value || 'DEFAULT'; return attrValueHash[key] || 'DEFAULT';
} }
return this.escape(value) return this.escape(attrValueHash[key])
}.bind(this)).join(",") + }.bind(this)).join(",") +
")") ")")
}.bind(this)) }.bind(this))
var replacements = { var replacements = {
table: this.quoteIdentifiers(tableName) table: this.quoteIdentifiers(tableName)
, attributes: Object.keys(attrValueHashes[0]).map(function(attr){ , attributes: allAttributes.map(function(attr){
return this.quoteIdentifier(attr) return this.quoteIdentifier(attr)
}.bind(this)).join(",") }.bind(this)).join(",")
, tuples: tuples.join(",") , tuples: tuples.join(",")
...@@ -329,26 +287,6 @@ module.exports = (function() { ...@@ -329,26 +287,6 @@ module.exports = (function() {
return Utils._.template(query)(replacements) return Utils._.template(query)(replacements)
}, },
updateQuery: function(tableName, attrValueHash, where, options, attributes) {
attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull, options)
var query = "UPDATE <%= table %> SET <%= values %> WHERE <%= where %> RETURNING *"
, values = []
for (var key in attrValueHash) {
var value = attrValueHash[key]
values.push(this.quoteIdentifier(key) + "=" + this.escape(value, (!!attributes && !!attributes[key] ? attributes[key] : undefined)))
}
var replacements = {
table: this.quoteIdentifiers(tableName),
values: values.join(","),
where: this.getWhereConditions(where)
}
return Utils._.template(query)(replacements)
},
deleteQuery: function(tableName, where, options, factory) { deleteQuery: function(tableName, where, options, factory) {
options = options || {} options = options || {}
...@@ -388,33 +326,6 @@ module.exports = (function() { ...@@ -388,33 +326,6 @@ module.exports = (function() {
return Utils._.template(query)(replacements) return Utils._.template(query)(replacements)
}, },
incrementQuery: function(tableName, attrValueHash, where, options) {
attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull)
var query = "UPDATE <%= table %> SET <%= values %> WHERE <%= where %> RETURNING *"
, values = []
for (var key in attrValueHash) {
var value = attrValueHash[key]
values.push(this.quoteIdentifier(key) + "=" + this.quoteIdentifier(key) + " + " + this.escape(value))
}
options = options || {}
for (var key in options) {
var value = options[key];
values.push(this.quoteIdentifier(key) + "=" + this.escape(value))
}
var replacements = {
table: this.quoteIdentifiers(tableName),
values: values.join(","),
where: this.getWhereConditions(where)
}
return Utils._.template(query)(replacements)
},
addIndexQuery: function(tableName, attributes, options) { addIndexQuery: function(tableName, attributes, options) {
var transformedAttributes = attributes.map(function(attribute) { var transformedAttributes = attributes.map(function(attribute) {
if (typeof attribute === 'string') { if (typeof attribute === 'string') {
...@@ -516,6 +427,7 @@ module.exports = (function() { ...@@ -516,6 +427,7 @@ module.exports = (function() {
} }
if (dataType.type === "DATETIME") { if (dataType.type === "DATETIME") {
dataType.originalType = "DATETIME"
dataType.type = 'TIMESTAMP WITH TIME ZONE' dataType.type = 'TIMESTAMP WITH TIME ZONE'
} }
...@@ -878,6 +790,10 @@ module.exports = (function() { ...@@ -878,6 +790,10 @@ module.exports = (function() {
return identifiers.split('.').map(function(t) { return this.quoteIdentifier(t, force) }.bind(this)).join('.') return identifiers.split('.').map(function(t) { return this.quoteIdentifier(t, force) }.bind(this)).join('.')
}, },
quoteTable: function(table) {
return this.quoteIdentifiers(table)
},
/** /**
* Generates an SQL query that returns all foreign keys of a table. * Generates an SQL query that returns all foreign keys of a table.
* *
...@@ -898,39 +814,7 @@ module.exports = (function() { ...@@ -898,39 +814,7 @@ module.exports = (function() {
*/ */
dropForeignKeyQuery: function(tableName, foreignKey) { dropForeignKeyQuery: function(tableName, foreignKey) {
return 'ALTER TABLE ' + this.quoteIdentifier(tableName) + ' DROP CONSTRAINT ' + this.quoteIdentifier(foreignKey) + ';' return 'ALTER TABLE ' + this.quoteIdentifier(tableName) + ' DROP CONSTRAINT ' + this.quoteIdentifier(foreignKey) + ';'
}, }
/**
* Returns a query that starts a transaction.
*
* @param {Boolean} value A boolean that states whether autocommit shall be done or not.
* @return {String} The generated sql query.
*/
setAutocommitQuery: function(value) {
return "SET autocommit = " + (!!value ? 1 : 0) + ";"
},
setIsolationLevelQuery: function(value) {
return "SET SESSION TRANSACTION ISOLATION LEVEL " + value + ";"
},
/**
* Returns a query that starts a transaction.
*
* @param {Object} options An object with options.
* @return {String} The generated sql query.
*/
startTransactionQuery: function(options) {
return "START TRANSACTION;"
},
commitTransactionQuery: function(options) {
return "COMMIT;"
},
rollbackTransactionQuery: function(options) {
return "ROLLBACK;"
},
} }
// Private // Private
......
var _ = require('lodash')
, Abstract = require('./abstract/dialect')
var SqliteDialect = function(sequelize) {
this.sequelize = sequelize
}
SqliteDialect.prototype.supports = _.extend(Abstract.prototype.supports, {
'DEFAULT': false,
'DEFAULT VALUES': true
})
module.exports = SqliteDialect
\ No newline at end of file
...@@ -153,66 +153,36 @@ module.exports = (function() { ...@@ -153,66 +153,36 @@ module.exports = (function() {
return "SELECT name FROM sqlite_master WHERE type='table' and name!='sqlite_sequence';" return "SELECT name FROM sqlite_master WHERE type='table' and name!='sqlite_sequence';"
}, },
insertQuery: function(tableName, attrValueHash) {
attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull)
var query
, valueQuery = "INSERT INTO <%= table %> (<%= attributes %>) VALUES (<%= values %>);"
, emptyQuery = "INSERT INTO <%= table %> DEFAULT VALUES;"
var replacements = {
table: this.quoteIdentifier(tableName),
attributes: Object.keys(attrValueHash).map(function(attr){return this.quoteIdentifier(attr)}.bind(this)).join(","),
values: Utils._.values(attrValueHash).map(function(value){
return this.escape(value)
}.bind(this)).join(",")
}
query = replacements.attributes.length ? valueQuery : emptyQuery
return Utils._.template(query)(replacements)
},
bulkInsertQuery: function(tableName, attrValueHashes) { bulkInsertQuery: function(tableName, attrValueHashes) {
var query = "INSERT INTO <%= table %> (<%= attributes %>) VALUES <%= tuples %>;" var query = "INSERT INTO <%= table %> (<%= attributes %>) VALUES <%= tuples %>;"
, tuples = [] , tuples = []
, allAttributes = []
Utils._.forEach(attrValueHashes, function(attrValueHash, i) {
Utils._.forEach(attrValueHash, function(value, key, hash) {
if (allAttributes.indexOf(key) === -1) allAttributes.push(key)
})
})
Utils._.forEach(attrValueHashes, function(attrValueHash) { Utils._.forEach(attrValueHashes, function(attrValueHash, i) {
tuples.push("(" + tuples.push("(" +
Utils._.values(attrValueHash).map(function(value){ allAttributes.map(function (key) {
return this.escape(value) return this.escape(attrValueHash[key])
}.bind(this)).join(",") + }.bind(this)).join(",") +
")") ")")
}.bind(this)) }.bind(this))
var replacements = { var replacements = {
table: this.quoteIdentifier(tableName), table: this.quoteIdentifier(tableName),
attributes: Object.keys(attrValueHashes[0]).map(function(attr){return this.quoteIdentifier(attr)}.bind(this)).join(","), attributes: allAttributes.map(function(attr){
return this.quoteIdentifier(attr)
}.bind(this)).join(","),
tuples: tuples tuples: tuples
} }
return Utils._.template(query)(replacements) return Utils._.template(query)(replacements)
}, },
updateQuery: function(tableName, attrValueHash, where, options) {
attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull, options)
var query = "UPDATE <%= table %> SET <%= values %> WHERE <%= where %>"
, values = []
for (var key in attrValueHash) {
var value = attrValueHash[key]
values.push(this.quoteIdentifier(key) + "=" + this.escape(value))
}
var replacements = {
table: this.quoteIdentifier(tableName),
values: values.join(","),
where: this.getWhereConditions(where)
}
return Utils._.template(query)(replacements)
},
deleteQuery: function(tableName, where, options) { deleteQuery: function(tableName, where, options) {
options = options || {} options = options || {}
...@@ -225,32 +195,6 @@ module.exports = (function() { ...@@ -225,32 +195,6 @@ module.exports = (function() {
return Utils._.template(query)(replacements) return Utils._.template(query)(replacements)
}, },
incrementQuery: function(tableName, attrValueHash, where, options) {
attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull)
var query = "UPDATE <%= table %> SET <%= values %> WHERE <%= where %>"
, values = []
for (var key in attrValueHash) {
var value = attrValueHash[key]
values.push(this.quoteIdentifier(key) + "=" + this.quoteIdentifier(key) + "+ " + this.escape(value))
}
options = options || {}
for (var key in options) {
var value = options[key];
values.push(this.quoteIdentifier(key) + "=" + this.escape(value))
}
var replacements = {
table: this.quoteIdentifier(tableName),
values: values.join(","),
where: this.getWhereConditions(where)
}
return Utils._.template(query)(replacements)
},
attributesToSQL: function(attributes) { attributesToSQL: function(attributes) {
var result = {} var result = {}
...@@ -365,11 +309,6 @@ module.exports = (function() { ...@@ -365,11 +309,6 @@ module.exports = (function() {
return Utils._.template(sql, { tableName: this.addSchema({tableName: tableName, options: options})}) return Utils._.template(sql, { tableName: this.addSchema({tableName: tableName, options: options})})
}, },
renameTableQuery: function(before, after) {
var query = "ALTER TABLE `<%= before %>` RENAME TO `<%= after %>`;"
return Utils._.template(query, { before: before, after: after })
},
removeColumnQuery: function(tableName, attributes) { removeColumnQuery: function(tableName, attributes) {
attributes = this.attributesToSQL(attributes) attributes = this.attributesToSQL(attributes)
...@@ -448,6 +387,10 @@ module.exports = (function() { ...@@ -448,6 +387,10 @@ module.exports = (function() {
return identifiers.split('.').map(function(v) { return this.quoteIdentifier(v, force) }.bind(this)).join('.') return identifiers.split('.').map(function(v) { return this.quoteIdentifier(v, force) }.bind(this)).join('.')
}, },
quoteTable: function(table) {
return this.quoteIdentifier(table)
},
/** /**
* Generates an SQL query that returns all foreign keys of a table. * Generates an SQL query that returns all foreign keys of a table.
* *
......
...@@ -8,6 +8,7 @@ module.exports = (function() { ...@@ -8,6 +8,7 @@ module.exports = (function() {
this.sequelize = sequelize this.sequelize = sequelize
this.QueryGenerator = require('./dialects/' + this.sequelize.options.dialect + '/query-generator') this.QueryGenerator = require('./dialects/' + this.sequelize.options.dialect + '/query-generator')
this.QueryGenerator.options = this.sequelize.options this.QueryGenerator.options = this.sequelize.options
this.QueryGenerator._dialect = this.sequelize.dialect
} }
Utils.addEventEmitter(QueryInterface) Utils.addEventEmitter(QueryInterface)
......
...@@ -112,6 +112,11 @@ module.exports = (function() { ...@@ -112,6 +112,11 @@ module.exports = (function() {
dialectOptions: this.options.dialectOptions, dialectOptions: this.options.dialectOptions,
} }
try {
this.dialect = new (require("./dialects/" + this.getDialect()))(this)
} catch(err) {
throw new Error("The dialect " + this.getDialect() + " is not supported.")
}
this.daoFactoryManager = new DAOFactoryManager(this) this.daoFactoryManager = new DAOFactoryManager(this)
this.transactionManager = new TransactionManager(this) this.transactionManager = new TransactionManager(this)
......
...@@ -645,7 +645,7 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -645,7 +645,7 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
self.Task.create({ title: 'task2' }).success(function(task2) { self.Task.create({ title: 'task2' }).success(function(task2) {
user.setTasks([task1, task2]).on('sql', spy).on('sql', _.after(2, function (sql) { user.setTasks([task1, task2]).on('sql', spy).on('sql', _.after(2, function (sql) {
expect(sql).to.have.string("INSERT INTO") expect(sql).to.have.string("INSERT INTO")
expect(sql).to.have.string("VALUES (1,1),(2,1)") expect(sql).to.have.string("VALUES (1,1),(1,2)")
})).success(function () { })).success(function () {
expect(spy.calledTwice).to.be.ok expect(spy.calledTwice).to.be.ok
done() done()
......
...@@ -28,6 +28,7 @@ describe(Support.getTestDialectTeaser("Multiple Level Filters"), function() { ...@@ -28,6 +28,7 @@ describe(Support.getTestDialectTeaser("Multiple Level Filters"), function() {
UserId: 1, UserId: 1,
title: 'republic' title: 'republic'
},{ },{
UserId: 2,
title: 'empire' title: 'empire'
}]).success(function() { }]).success(function() {
Task.bulkCreate([{ Task.bulkCreate([{
......
...@@ -265,6 +265,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -265,6 +265,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
foobar: {type: Sequelize.TEXT, defaultValue: 'asd'}, foobar: {type: Sequelize.TEXT, defaultValue: 'asd'},
flag: {type: Sequelize.BOOLEAN, defaultValue: false} flag: {type: Sequelize.BOOLEAN, defaultValue: false}
}) })
expect(Task.build().title).to.equal('a task!') expect(Task.build().title).to.equal('a task!')
expect(Task.build().foo).to.equal(2) expect(Task.build().foo).to.equal(2)
expect(Task.build().bar).to.not.be.ok expect(Task.build().bar).to.not.be.ok
...@@ -548,12 +549,12 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -548,12 +549,12 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
Support.prepareTransactionTest(this.sequelize, function(sequelize) { Support.prepareTransactionTest(this.sequelize, function(sequelize) {
var User = sequelize.define('User', { username: Sequelize.STRING }) var User = sequelize.define('User', { username: Sequelize.STRING })
User.sync({ force: true }).success(function() { User.sync({ force: true }).done(function() {
User.create({ username: 'foo' }).success(function() { User.create({ username: 'foo' }).done(function() {
sequelize.transaction(function(t) { sequelize.transaction(function(t) {
User.update({ username: 'bar' }, {}, { transaction: t }).success(function() { User.update({ username: 'bar' }, {}, { transaction: t }).done(function(err) {
User.all().success(function(users1) { User.all().done(function(err, users1) {
User.all({ transaction: t }).success(function(users2) { User.all({ transaction: t }).done(function(err, users2) {
expect(users1[0].username).to.equal('foo') expect(users1[0].username).to.equal('foo')
expect(users2[0].username).to.equal('bar') expect(users2[0].username).to.equal('bar')
t.rollback().success(function(){ done() }) t.rollback().success(function(){ done() })
...@@ -669,7 +670,9 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -669,7 +670,9 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
{ username: 'Bob', secretValue: '43' }] { username: 'Bob', secretValue: '43' }]
this.User.bulkCreate(data).success(function() { this.User.bulkCreate(data).success(function() {
self.User.update({username: 'Bill'}, {secretValue: '42'}).success(function() { self.User.update({username: 'Bill'}, {secretValue: '42'}).done(function(err) {
console.log(err)
expect(err).not.to.be.ok
self.User.findAll({order: 'id'}).success(function(users) { self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).to.equal(3) expect(users.length).to.equal(3)
...@@ -1252,7 +1255,8 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -1252,7 +1255,8 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
it("should be able to create and update records under any valid schematic", function(done){ it("should be able to create and update records under any valid schematic", function(done){
var self = this var self = this
self.UserPublic.sync({ force: true }).success(function(UserPublicSync){ self.UserPublic.sync({ force: true }).done(function(err, UserPublicSync){
expect(err).not.to.be.ok
UserPublicSync.create({age: 3}).on('sql', function(UserPublic){ UserPublicSync.create({age: 3}).on('sql', function(UserPublic){
self.UserSpecialSync.schema('special').create({age: 3}) self.UserSpecialSync.schema('special').create({age: 3})
.on('sql', function(UserSpecial){ .on('sql', function(UserSpecial){
...@@ -1272,7 +1276,8 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -1272,7 +1276,8 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
expect(UserPublic.indexOf('INSERT INTO `UserPublics`')).to.be.above(-1) expect(UserPublic.indexOf('INSERT INTO `UserPublics`')).to.be.above(-1)
} }
}) })
.success(function(UserSpecial){ .done(function(err, UserSpecial){
expect(err).not.to.be.ok
UserSpecial.updateAttributes({age: 5}) UserSpecial.updateAttributes({age: 5})
.on('sql', function(user){ .on('sql', function(user){
expect(user).to.exist expect(user).to.exist
...@@ -1282,8 +1287,12 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -1282,8 +1287,12 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
expect(user.indexOf('UPDATE `special.UserSpecials`')).to.be.above(-1) expect(user.indexOf('UPDATE `special.UserSpecials`')).to.be.above(-1)
} }
done() done()
}).error(function (err) {
expect(err).not.to.be.ok
}) })
}) })
}).error(function (err) {
expect(err).not.to.be.ok
}) })
}) })
}) })
...@@ -1609,4 +1618,4 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -1609,4 +1618,4 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
}) })
}) })
}) })
}) })
\ No newline at end of file
...@@ -598,7 +598,18 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -598,7 +598,18 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
it('should allow blank creates (with timestamps: false)', function (done) { it('should allow blank creates (with timestamps: false)', function (done) {
var Worker = this.sequelize.define('Worker', {}, {timestamps: false}) var Worker = this.sequelize.define('Worker', {}, {timestamps: false})
Worker.sync().done(function(err) { Worker.sync().done(function(err) {
Worker.create().done(function (err, worker) { Worker.create({}, {fields: []}).done(function (err, worker) {
expect(err).not.to.be.ok
expect(worker).to.be.ok
done()
})
})
})
it('should allow truly blank creates', function (done) {
var Worker = this.sequelize.define('Worker', {}, {timestamps: false})
Worker.sync().done(function(err) {
Worker.create({}, {fields: []}).done(function (err, worker) {
expect(err).not.to.be.ok expect(err).not.to.be.ok
expect(worker).to.be.ok expect(worker).to.be.ok
done() done()
......
...@@ -124,7 +124,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -124,7 +124,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
where: { id: 1 }, where: { id: 1 },
attributes: ['id', ['username', 'name']] attributes: ['id', ['username', 'name']]
}).success(function(user) { }).success(function(user) {
expect(user.name).to.equal('barfooz') expect(user.dataValues.name).to.equal('barfooz')
done() done()
}) })
}) })
......
...@@ -153,7 +153,8 @@ describe(Support.getTestDialectTeaser("DAO"), function () { ...@@ -153,7 +153,8 @@ describe(Support.getTestDialectTeaser("DAO"), function () {
}) })
}) })
it("returns false for two empty attributes", function(done) { // In my opinion this is bad logic, null is different from an empty string
xit("returns false for two empty attributes", function(done) {
this.User.create({ username: null }).success(function(user) { this.User.create({ username: null }).success(function(user) {
user.username = '' user.username = ''
expect(user.isDirty).to.be.false expect(user.isDirty).to.be.false
...@@ -618,10 +619,12 @@ describe(Support.getTestDialectTeaser("DAO"), function () { ...@@ -618,10 +619,12 @@ describe(Support.getTestDialectTeaser("DAO"), function () {
include: [Page] include: [Page]
}).success(function(leBook) { }).success(function(leBook) {
page.updateAttributes({ content: 'something totally different' }).success(function(page) { page.updateAttributes({ content: 'something totally different' }).success(function(page) {
expect(leBook.pages.length).to.equal(1)
expect(leBook.pages[0].content).to.equal('om nom nom') expect(leBook.pages[0].content).to.equal('om nom nom')
expect(page.content).to.equal('something totally different') expect(page.content).to.equal('something totally different')
leBook.reload().success(function(leBook) { leBook.reload().success(function(leBook) {
expect(leBook.pages.length).to.equal(1)
expect(leBook.pages[0].content).to.equal('something totally different') expect(leBook.pages[0].content).to.equal('something totally different')
expect(page.content).to.equal('something totally different') expect(page.content).to.equal('something totally different')
done() done()
...@@ -792,15 +795,13 @@ describe(Support.getTestDialectTeaser("DAO"), function () { ...@@ -792,15 +795,13 @@ describe(Support.getTestDialectTeaser("DAO"), function () {
// timeout is needed, in order to check the update of the timestamp // timeout is needed, in order to check the update of the timestamp
var build = function(callback) { var build = function(callback) {
user = User.build({ username: 'user' }) user = User.build({ username: 'user' })
updatedAt = user.updatedAt
expect(updatedAt.getTime()).to.be.above(now) var save = user.save()
setTimeout(function() { save.success(function() {
user.save().success(function() { expect(now).to.be.below(user.updatedAt.getTime())
expect(updatedAt.getTime()).to.be.below(user.updatedAt.getTime()) callback()
callback() })
})
}, 1000)
} }
// closures are fun :) // closures are fun :)
...@@ -1079,7 +1080,7 @@ describe(Support.getTestDialectTeaser("DAO"), function () { ...@@ -1079,7 +1080,7 @@ describe(Support.getTestDialectTeaser("DAO"), function () {
it('returns a response that can be stringified', function(done) { it('returns a response that can be stringified', function(done) {
var user = this.User.build({ username: 'test.user', age: 99, isAdmin: true }) var user = this.User.build({ username: 'test.user', age: 99, isAdmin: true })
expect(JSON.stringify(user)).to.deep.equal('{"id":null,"username":"test.user","age":99,"isAdmin":true}') expect(JSON.stringify(user)).to.deep.equal('{"username":"test.user","age":99,"isAdmin":true,"id":null}')
done() done()
}) })
...@@ -1235,7 +1236,7 @@ describe(Support.getTestDialectTeaser("DAO"), function () { ...@@ -1235,7 +1236,7 @@ describe(Support.getTestDialectTeaser("DAO"), function () {
this.ParanoidUser.create({ username: 'fnord' }).success(function() { this.ParanoidUser.create({ username: 'fnord' }).success(function() {
self.ParanoidUser.findAll().success(function(users) { self.ParanoidUser.findAll().success(function(users) {
users[0].updateAttributes({username: 'newFnord'}).success(function(user) { users[0].updateAttributes({username: 'newFnord'}).success(function(user) {
expect(user.deletedAt).to.be.null expect(user.deletedAt).not.to.exist
done() done()
}) })
}) })
...@@ -1248,7 +1249,7 @@ describe(Support.getTestDialectTeaser("DAO"), function () { ...@@ -1248,7 +1249,7 @@ describe(Support.getTestDialectTeaser("DAO"), function () {
self.ParanoidUser.findAll().success(function(users) { self.ParanoidUser.findAll().success(function(users) {
self.ParanoidUser.create({ username: 'linkedFnord' }).success(function(linkedUser) { self.ParanoidUser.create({ username: 'linkedFnord' }).success(function(linkedUser) {
users[0].setParanoidUser( linkedUser ).success(function(user) { users[0].setParanoidUser( linkedUser ).success(function(user) {
expect(user.deletedAt).to.be.null expect(user.deletedAt).not.to.exist
done() done()
}) })
}) })
......
/* jshint camelcase: false */
var chai = require('chai')
, Sequelize = require('../../index')
, expect = chai.expect
, Support = require(__dirname + '/../support')
, DataTypes = require(__dirname + "/../../lib/data-types")
, dialect = Support.getTestDialect()
, config = require(__dirname + "/../config/config")
, sinon = require('sinon')
, datetime = require('chai-datetime')
, uuid = require('node-uuid')
, _ = require('lodash')
chai.use(datetime)
chai.Assertion.includeStack = true
describe(Support.getTestDialectTeaser("DAO"), function () {
describe('Values', function () {
describe('set', function () {
it('doesn\'t overwrite primary keys', function () {
var User = this.sequelize.define('User', {
identifier: {type: DataTypes.STRING, primaryKey: true}
})
var user = User.build({identifier: 'identifier'})
expect(user.get('identifier')).to.equal('identifier')
user.set('identifier', 'another identifier')
expect(user.get('identifier')).to.equal('identifier')
})
it('doesn\'t set timestamps', function () {
var User = this.sequelize.define('User', {
identifier: {type: DataTypes.STRING, primaryKey: true}
})
var user = User.build()
user.set({
createdAt: new Date(2000, 1, 1),
updatedAt: new Date(2000, 1, 1)
})
expect(user.get('createdAt')).not.to.be.ok
expect(user.get('updatedAt')).not.to.be.ok
})
describe('includes', function () {
it('should support basic includes', function () {
var Product = this.sequelize.define('Product', {
title: Sequelize.STRING
})
var Tag = this.sequelize.define('Tag', {
name: Sequelize.STRING
})
var User = this.sequelize.define('User', {
first_name: Sequelize.STRING,
last_name: Sequelize.STRING
})
Product.hasMany(Tag)
Product.belongsTo(User)
var product
product = Product.build({}, {
include: [
User,
Tag
]
})
product.set({
id: 1,
title: 'Chair',
tags: [
{id: 1, name: 'Alpha'},
{id: 2, name: 'Beta'}
],
user: {
id: 1,
first_name: 'Mick',
last_name: 'Hansen'
}
})
expect(product.tags).to.be.ok
expect(product.tags.length).to.equal(2)
expect(product.tags[0].Model).to.equal(Tag)
expect(product.user).to.be.ok
expect(product.user.Model).to.equal(User)
})
it('should support basic includes (with raw: true)', function () {
var Product = this.sequelize.define('Product', {
title: Sequelize.STRING
})
var Tag = this.sequelize.define('Tag', {
name: Sequelize.STRING
})
var User = this.sequelize.define('User', {
first_name: Sequelize.STRING,
last_name: Sequelize.STRING
})
Product.hasMany(Tag)
Product.belongsTo(User)
var product
product = Product.build({}, {
include: [
User,
Tag
]
})
product.set({
id: 1,
title: 'Chair',
tags: [
{id: 1, name: 'Alpha'},
{id: 2, name: 'Beta'}
],
user: {
id: 1,
first_name: 'Mick',
last_name: 'Hansen'
}
}, {raw: true})
expect(product.tags).to.be.ok
expect(product.tags.length).to.equal(2)
expect(product.tags[0].Model).to.equal(Tag)
expect(product.user).to.be.ok
expect(product.user.Model).to.equal(User)
})
})
})
describe('get', function () {
it('should use custom attribute getters in get(key)', function () {
var Product = this.sequelize.define('Product', {
price: {
type: Sequelize.FLOAT,
get: function() {
return this.dataValues['price'] * 100
}
}
})
var product = Product.build({
price: 10
})
expect(product.get('price')).to.equal(1000)
})
it('should custom virtual getters in get(key)', function () {
var Product = this.sequelize.define('Product', {
priceInCents: {
type: Sequelize.FLOAT
}
}, {
getterMethods: {
price: function() {
return this.dataValues['priceInCents'] / 100
}
}
})
var product = Product.build({
priceInCents: 1000
})
expect(product.get('price')).to.equal(10)
})
it('should use custom getters in toJSON', function () {
var Product = this.sequelize.define('Product', {
price: {
type: Sequelize.STRING,
get: function() {
return this.dataValues['price'] * 100
}
}
}, {
getterMethods: {
withTaxes: function() {
return this.get('price') * 1.25
}
}
})
var product = Product.build({
price: 10
})
expect(product.toJSON()).to.deep.equal({withTaxes: 1250, price: 1000, id: null})
})
})
describe('changed', function () {
it('should return false if object was built from database', function (done) {
var User = this.sequelize.define('User', {
name: {type: DataTypes.STRING}
})
User.sync().done(function (err) {
User.create({name: 'Jan Meier'}).done(function (err, user) {
expect(err).not.to.be.ok
expect(user.changed('name')).to.be.false
expect(user.changed()).not.to.be.ok
expect(user.isDirty).to.be.false
done()
});
})
})
it('should return true if previous value is different', function () {
var User = this.sequelize.define('User', {
name: {type: DataTypes.STRING}
})
var user = User.build({
name: 'Jan Meier'
})
user.set('name', 'Mick Hansen')
expect(user.changed('name')).to.be.true
expect(user.changed()).to.be.ok
expect(user.isDirty).to.be.true
})
it('should return false immediately after saving', function (done) {
var User = this.sequelize.define('User', {
name: {type: DataTypes.STRING}
})
User.sync().done(function (err) {
var user = User.build({
name: 'Jan Meier'
})
user.set('name', 'Mick Hansen')
expect(user.changed('name')).to.be.true
expect(user.changed()).to.be.ok
expect(user.isDirty).to.be.true
user.save().done(function (err) {
expect(err).not.to.be.ok
expect(user.changed('name')).to.be.false
expect(user.changed()).not.to.be.ok
expect(user.isDirty).to.be.false
done()
})
})
})
it('setting the same value twice should not impact the result', function () {
var User = this.sequelize.define('User', {
name: {type: DataTypes.STRING}
})
var user = User.build({
name: 'Jan Meier'
})
user.set('name', 'Mick Hansen')
user.set('name', 'Mick Hansen')
expect(user.changed('name')).to.be.true
expect(user.changed()).to.be.ok
expect(user.isDirty).to.be.true
expect(user.previous('name')).to.equal('Jan Meier')
})
})
describe('previous', function () {
it('should return the previous value', function () {
var User = this.sequelize.define('User', {
name: {type: DataTypes.STRING}
})
var user = User.build({
name: 'Jan Meier'
})
user.set('name', 'Mick Hansen')
expect(user.previous('name')).to.equal('Jan Meier')
expect(user.get('name')).to.equal('Mick Hansen')
})
})
})
})
\ No newline at end of file
...@@ -23,6 +23,7 @@ describe(Support.getTestDialectTeaser("Hooks"), function () { ...@@ -23,6 +23,7 @@ describe(Support.getTestDialectTeaser("Hooks"), function () {
}, { }, {
hooks: { hooks: {
beforeValidate: function(user, fn) { beforeValidate: function(user, fn) {
console.log("beforeValidate")
user.mood = 'happy' user.mood = 'happy'
fn(null, user) fn(null, user)
}, },
...@@ -111,7 +112,7 @@ describe(Support.getTestDialectTeaser("Hooks"), function () { ...@@ -111,7 +112,7 @@ describe(Support.getTestDialectTeaser("Hooks"), function () {
self.User.all().success(function(users) { self.User.all().success(function(users) {
expect(users[0].mood).to.equal('happy') expect(users[0].mood).to.equal('happy')
expect(users[0].mood).to.equal('happy') expect(users[1].mood).to.equal('happy')
done() done()
}) })
}) })
...@@ -1105,7 +1106,7 @@ describe(Support.getTestDialectTeaser("Hooks"), function () { ...@@ -1105,7 +1106,7 @@ describe(Support.getTestDialectTeaser("Hooks"), function () {
}) })
describe('#create', function() { describe('#create', function() {
describe('via deifne', function() { describe('via define', function() {
describe('on success', function() { describe('on success', function() {
describe('with a single hook', function() { describe('with a single hook', function() {
it('should return the user from the callback', function(done) { it('should return the user from the callback', function(done) {
...@@ -1869,7 +1870,7 @@ describe(Support.getTestDialectTeaser("Hooks"), function () { ...@@ -1869,7 +1870,7 @@ describe(Support.getTestDialectTeaser("Hooks"), function () {
}) })
describe('#updateAttributes', function() { describe('#updateAttributes', function() {
describe('via deifne', function() { describe('via define', function() {
describe('on success', function() { describe('on success', function() {
describe('with a single hook', function() { describe('with a single hook', function() {
it('should return the user from the callback', function(done) { it('should return the user from the callback', function(done) {
...@@ -1987,7 +1988,7 @@ describe(Support.getTestDialectTeaser("Hooks"), function () { ...@@ -1987,7 +1988,7 @@ describe(Support.getTestDialectTeaser("Hooks"), function () {
User.sync({ force: true }).success(function() { User.sync({ force: true }).success(function() {
User.create({username: 'Cheech', mood: 'sad'}).success(function(user) { User.create({username: 'Cheech', mood: 'sad'}).success(function(user) {
user.updateAttributes({username: 'Chong'}).success(function(user) { user.updateAttributes({username: 'Chong'}).done(function(err, user) {
expect(user.username).to.equal('Chong') expect(user.username).to.equal('Chong')
expect(user.mood).to.equal('happy') expect(user.mood).to.equal('happy')
expect(beforeHook).to.be.true expect(beforeHook).to.be.true
......
...@@ -365,9 +365,11 @@ describe(Support.getTestDialectTeaser("Include"), function () { ...@@ -365,9 +365,11 @@ describe(Support.getTestDialectTeaser("Include"), function () {
ranks: function(callback) { ranks: function(callback) {
Rank.bulkCreate([ Rank.bulkCreate([
{name: 'Admin', canInvite: 1, canRemove: 1}, {name: 'Admin', canInvite: 1, canRemove: 1},
{name: 'Member', canInvite: 1} {name: 'Member', canInvite: 1, canRemove: 0}
]).done(function () { ]).done(function () {
Rank.findAll().done(callback) Rank.findAll().done(callback)
}).on('sql', function (sql) {
console.log(sql)
}) })
}, },
memberships: ['user', 'groups', 'ranks', function (callback, results) { memberships: ['user', 'groups', 'ranks', function (callback, results) {
...@@ -579,7 +581,7 @@ describe(Support.getTestDialectTeaser("Include"), function () { ...@@ -579,7 +581,7 @@ describe(Support.getTestDialectTeaser("Include"), function () {
ranks: function(callback) { ranks: function(callback) {
Rank.bulkCreate([ Rank.bulkCreate([
{name: 'Admin', canInvite: 1, canRemove: 1}, {name: 'Admin', canInvite: 1, canRemove: 1},
{name: 'Member', canInvite: 1} {name: 'Member', canInvite: 1, canRemove: 0}
]).done(function () { ]).done(function () {
Rank.findAll().done(callback) Rank.findAll().done(callback)
}) })
......
...@@ -339,7 +339,7 @@ if (Support.dialectIsMySQL()) { ...@@ -339,7 +339,7 @@ if (Support.dialectIsMySQL()) {
expectation: "INSERT INTO `myTable` (`name`,`foo`) VALUES ('foo',1),('bar',2);" expectation: "INSERT INTO `myTable` (`name`,`foo`) VALUES ('foo',1),('bar',2);"
}, { }, {
arguments: ['myTable', [{name: 'foo', foo: 1, nullValue: null}, {name: 'bar', nullValue: null}]], arguments: ['myTable', [{name: 'foo', foo: 1, nullValue: null}, {name: 'bar', nullValue: null}]],
expectation: "INSERT INTO `myTable` (`name`,`foo`,`nullValue`) VALUES ('foo',1,NULL),('bar',NULL);" expectation: "INSERT INTO `myTable` (`name`,`foo`,`nullValue`) VALUES ('foo',1,NULL),('bar',NULL,NULL);"
}, { }, {
arguments: ['myTable', [{name: 'foo', foo: 1, nullValue: null}, {name: 'bar', foo: 2, nullValue: null}]], arguments: ['myTable', [{name: 'foo', foo: 1, nullValue: null}, {name: 'bar', foo: 2, nullValue: null}]],
expectation: "INSERT INTO `myTable` (`name`,`foo`,`nullValue`) VALUES ('foo',1,NULL),('bar',2,NULL);", expectation: "INSERT INTO `myTable` (`name`,`foo`,`nullValue`) VALUES ('foo',1,NULL),('bar',2,NULL);",
...@@ -350,7 +350,7 @@ if (Support.dialectIsMySQL()) { ...@@ -350,7 +350,7 @@ if (Support.dialectIsMySQL()) {
context: {options: {omitNull: true}} // Note: We don't honour this because it makes little sense when some rows may have nulls and others not context: {options: {omitNull: true}} // Note: We don't honour this because it makes little sense when some rows may have nulls and others not
}, { }, {
arguments: ['myTable', [{name: 'foo', foo: 1, nullValue: undefined}, {name: 'bar', foo: 2, undefinedValue: undefined}]], arguments: ['myTable', [{name: 'foo', foo: 1, nullValue: undefined}, {name: 'bar', foo: 2, undefinedValue: undefined}]],
expectation: "INSERT INTO `myTable` (`name`,`foo`,`nullValue`) VALUES ('foo',1,NULL),('bar',2,NULL);", expectation: "INSERT INTO `myTable` (`name`,`foo`,`nullValue`,`undefinedValue`) VALUES ('foo',1,NULL,NULL),('bar',2,NULL,NULL);",
context: {options: {omitNull: true}} // Note: As above context: {options: {omitNull: true}} // Note: As above
}, { }, {
arguments: ['myTable', [{name: "foo", value: true}, {name: 'bar', value: false}]], arguments: ['myTable', [{name: "foo", value: true}, {name: 'bar', value: false}]],
...@@ -516,6 +516,7 @@ if (Support.dialectIsMySQL()) { ...@@ -516,6 +516,7 @@ if (Support.dialectIsMySQL()) {
test.arguments[1] = test.arguments[1](this.sequelize) test.arguments[1] = test.arguments[1](this.sequelize)
} }
QueryGenerator.options = context.options QueryGenerator.options = context.options
QueryGenerator._dialect = this.sequelize.dialect
var conditions = QueryGenerator[suiteTitle].apply(QueryGenerator, test.arguments) var conditions = QueryGenerator[suiteTitle].apply(QueryGenerator, test.arguments)
expect(conditions).to.deep.equal(test.expectation) expect(conditions).to.deep.equal(test.expectation)
done() done()
......
...@@ -904,6 +904,7 @@ if (dialect.match(/^postgres/)) { ...@@ -904,6 +904,7 @@ if (dialect.match(/^postgres/)) {
test.arguments[1] = test.arguments[1](this.sequelize) test.arguments[1] = test.arguments[1](this.sequelize)
} }
QueryGenerator.options = context.options QueryGenerator.options = context.options
QueryGenerator._dialect = this.sequelize.dialect
var conditions = QueryGenerator[suiteTitle].apply(QueryGenerator, test.arguments) var conditions = QueryGenerator[suiteTitle].apply(QueryGenerator, test.arguments)
expect(conditions).to.deep.equal(test.expectation) expect(conditions).to.deep.equal(test.expectation)
done() done()
......
...@@ -124,7 +124,7 @@ if (dialect === 'sqlite') { ...@@ -124,7 +124,7 @@ if (dialect === 'sqlite') {
}) })
}) })
it("should make aliased attributes available", function(done) { xit("should make aliased attributes available", function(done) {
this.User.find({ where: { name:'user' }, attributes: ['id', ['name', 'username']] }).success(function(user) { this.User.find({ where: { name:'user' }, attributes: ['id', ['name', 'username']] }).success(function(user) {
expect(user.username).to.equal('user') expect(user.username).to.equal('user')
done() done()
......
...@@ -20,19 +20,20 @@ if (dialect === 'sqlite') { ...@@ -20,19 +20,20 @@ if (dialect === 'sqlite') {
describe('findAll', function() { describe('findAll', function() {
it("handles dates correctly", function(done) { it("handles dates correctly", function(done) {
var self = this var self = this
, user = this.User.build({ username: 'user' })
this.User user.dataValues['createdAt'] = new Date(2011, 04, 04)
.create({ username: 'user', createdAt: new Date(2011, 04, 04) })
.success(function() { user.save().success(function() {
self.User.create({ username: 'new user' }).success(function() { self.User.create({ username: 'new user' }).success(function() {
self.User.findAll({ self.User.findAll({
where: ['createdAt > ?', new Date(2012, 01, 01)] where: ['createdAt > ?', new Date(2012, 01, 01)]
}).success(function(users) { }).success(function(users) {
expect(users).to.have.length(1) expect(users).to.have.length(1)
done() done()
})
}) })
}) })
})
}) })
}) })
}) })
......
...@@ -444,6 +444,7 @@ if (dialect === 'sqlite') { ...@@ -444,6 +444,7 @@ if (dialect === 'sqlite') {
test.arguments[1] = test.arguments[1](this.sequelize) test.arguments[1] = test.arguments[1](this.sequelize)
} }
QueryGenerator.options = context.options QueryGenerator.options = context.options
QueryGenerator._dialect = this.sequelize.dialect
var conditions = QueryGenerator[suiteTitle].apply(QueryGenerator, test.arguments) var conditions = QueryGenerator[suiteTitle].apply(QueryGenerator, test.arguments)
expect(conditions).to.deep.equal(test.expectation) expect(conditions).to.deep.equal(test.expectation)
done() done()
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!