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

Commit 4914341e by Daniel Durante

Merge branch 'master' into scopes

Conflicts:
	lib/dao-factory.js
2 parents 7484aded 346fcdb9
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
- [FEATURE] Added a `findAndCountAll`, useful for pagination. [#533](https://github.com/sequelize/sequelize/pull/533). Thanks to iamjochen - [FEATURE] Added a `findAndCountAll`, useful for pagination. [#533](https://github.com/sequelize/sequelize/pull/533). Thanks to iamjochen
- [FEATURE] Made explicit migrations possible. [#728](https://github.com/sequelize/sequelize/pull/728). Thanks to freezy - [FEATURE] Made explicit migrations possible. [#728](https://github.com/sequelize/sequelize/pull/728). Thanks to freezy
- [FEATURE] Added support for where clauses containing !=, < etc. and support for date ranges [#727](https://github.com/sequelize/sequelize/pull/727). Thanks to durango - [FEATURE] Added support for where clauses containing !=, < etc. and support for date ranges [#727](https://github.com/sequelize/sequelize/pull/727). Thanks to durango
- [FEATURE] Added support for model instances being referenced [#761](https://github.com/sequelize/sequelize/pull/761) thanks to sdepold
- [REFACTORING] hasMany now uses a single SQL statement when creating and destroying associations, instead of removing each association seperately [690](https://github.com/sequelize/sequelize/pull/690). Inspired by [#104](https://github.com/sequelize/sequelize/issues/104). janmeier - [REFACTORING] hasMany now uses a single SQL statement when creating and destroying associations, instead of removing each association seperately [690](https://github.com/sequelize/sequelize/pull/690). Inspired by [#104](https://github.com/sequelize/sequelize/issues/104). janmeier
- [REFACTORING] Consistent handling of offset across dialects. Offset is now always applied, and limit is set to max table size of not limit is given [#725](https://github.com/sequelize/sequelize/pull/725). janmeier - [REFACTORING] Consistent handling of offset across dialects. Offset is now always applied, and limit is set to max table size of not limit is given [#725](https://github.com/sequelize/sequelize/pull/725). janmeier
......
var Toposort = require('toposort-class') var Toposort = require('toposort-class')
, DaoFactory = require('./dao-factory')
module.exports = (function() { module.exports = (function() {
var DAOFactoryManager = function(sequelize) { var DAOFactoryManager = function(sequelize) {
...@@ -39,16 +40,17 @@ module.exports = (function() { ...@@ -39,16 +40,17 @@ module.exports = (function() {
* before dependents. * before dependents.
*/ */
DAOFactoryManager.prototype.forEachDAO = function(iterator) { DAOFactoryManager.prototype.forEachDAO = function(iterator) {
var daos = {} var daos = {}
, sorter = new Toposort() , sorter = new Toposort()
this.daos.forEach(function(dao) { this.daos.forEach(function(dao) {
daos[dao.tableName] = dao
var deps = [] var deps = []
for(var attrName in dao.rawAttributes) { daos[dao.tableName] = dao
if(dao.rawAttributes.hasOwnProperty(attrName)) {
if(dao.rawAttributes[attrName].references) { for (var attrName in dao.rawAttributes) {
if (dao.rawAttributes.hasOwnProperty(attrName)) {
if (dao.rawAttributes[attrName].references) {
deps.push(dao.rawAttributes[attrName].references) deps.push(dao.rawAttributes[attrName].references)
} }
} }
......
...@@ -24,22 +24,29 @@ module.exports = (function() { ...@@ -24,22 +24,29 @@ module.exports = (function() {
// error check options // error check options
Utils._.each(options.validate, function(validator, validatorType) { Utils._.each(options.validate, function(validator, validatorType) {
if (Utils._.contains(Utils._.keys(attributes), validatorType)) if (Utils._.contains(Utils._.keys(attributes), validatorType)) {
throw new Error("A model validator function must not have the same name as a field. Model: " + name + ", field/validation name: " + validatorType) throw new Error("A model validator function must not have the same name as a field. Model: " + name + ", field/validation name: " + validatorType)
if (!Utils._.isFunction(validator)) }
if (!Utils._.isFunction(validator)) {
throw new Error("Members of the validate option must be functions. Model: " + name + ", error with validate member " + validatorType) throw new Error("Members of the validate option must be functions. Model: " + name + ", error with validate member " + validatorType)
}
}) })
this.name = name this.name = name
if (!this.options.tableName) { if (!this.options.tableName) {
this.tableName = this.options.freezeTableName ? name : Utils.pluralize(name, this.options.language) this.tableName = this.options.freezeTableName ? name : Utils.pluralize(name, this.options.language)
} else { } else {
this.tableName = this.options.tableName this.tableName = this.options.tableName
} }
this.rawAttributes = attributes
attributes = replaceReferencesWithTableNames(attributes)
this.rawAttributes = attributes
this.daoFactoryManager = null // defined in init function this.daoFactoryManager = null // defined in init function
this.associations = {} this.associations = {}
this.scopeObj = {} this.scopeObj = {}
} }
Object.defineProperty(DAOFactory.prototype, 'attributes', { Object.defineProperty(DAOFactory.prototype, 'attributes', {
...@@ -60,8 +67,8 @@ module.exports = (function() { ...@@ -60,8 +67,8 @@ module.exports = (function() {
var self = this; var self = this;
this.daoFactoryManager = daoFactoryManager this.daoFactoryManager = daoFactoryManager
this.primaryKeys = {};
this.primaryKeys = {};
Utils._.each(this.attributes, function(dataTypeString, attributeName) { Utils._.each(this.attributes, function(dataTypeString, attributeName) {
if ((attributeName !== 'id') && (dataTypeString.indexOf('PRIMARY KEY') !== -1)) { if ((attributeName !== 'id') && (dataTypeString.indexOf('PRIMARY KEY') !== -1)) {
self.primaryKeys[attributeName] = dataTypeString self.primaryKeys[attributeName] = dataTypeString
...@@ -80,9 +87,11 @@ module.exports = (function() { ...@@ -80,9 +87,11 @@ module.exports = (function() {
findAutoIncrementField.call(this) findAutoIncrementField.call(this)
// DAO prototype // DAO prototype
// WTF ... ?
this.DAO = function() { this.DAO = function() {
DAO.apply(this, arguments); DAO.apply(this, arguments);
}; }
Util.inherits(this.DAO, DAO); Util.inherits(this.DAO, DAO);
this.DAO.prototype.rawAttributes = this.rawAttributes; this.DAO.prototype.rawAttributes = this.rawAttributes;
...@@ -94,47 +103,48 @@ module.exports = (function() { ...@@ -94,47 +103,48 @@ module.exports = (function() {
} }
Utils._.each(['Get', 'Set'], function(type) { Utils._.each(['Get', 'Set'], function(type) {
var prop = type.toLowerCase(), var prop = type.toLowerCase()
opt = prop + 'terMethods', , opt = prop + 'terMethods'
meth = '__define' + type + 'ter__', , meth = '__define' + type + 'ter__'
funcs = Utils._.isObject(self.options[opt]) ? self.options[opt] : {} , funcs = Utils._.isObject(self.options[opt]) ? self.options[opt] : {}
;
Utils._.each(self.rawAttributes, function(attr, name) { Utils._.each(self.rawAttributes, function(attr, name) {
if (attr.hasOwnProperty(prop)) if (attr.hasOwnProperty(prop)) {
funcs[name] = attr[prop] funcs[name] = attr[prop]
}); }
})
Utils._.each(funcs, function(fct, name) { Utils._.each(funcs, function(fct, name) {
if (!Utils._.isFunction(fct)) if (!Utils._.isFunction(fct)) {
throw new Error(type + 'ter for "' + name + '" is not a function.') throw new Error(type + 'ter for "' + name + '" is not a function.')
}
self.DAO.prototype[meth](name, fct); self.DAO.prototype[meth](name, fct)
}) })
}) })
this.DAO.prototype.attributes = Object.keys(this.DAO.prototype.rawAttributes); this.DAO.prototype.attributes = Object.keys(this.DAO.prototype.rawAttributes);
this.DAO.prototype.booleanValues = []
this.DAO.prototype.defaultValues = {}
this.DAO.prototype.validators = {}
this.DAO.prototype.booleanValues = [];
this.DAO.prototype.defaultValues = {};
this.DAO.prototype.validators = {};
Utils._.each(this.rawAttributes, function (definition, name) { Utils._.each(this.rawAttributes, function (definition, name) {
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.hasOwnProperty('defaultValue')) { if (definition.hasOwnProperty('defaultValue')) {
self.DAO.prototype.defaultValues[name] = function() { self.DAO.prototype.defaultValues[name] = function() {
return Utils.toDefaultValue(definition.defaultValue); return Utils.toDefaultValue(definition.defaultValue)
} }
} }
if (definition.hasOwnProperty('validate')) { if (definition.hasOwnProperty('validate')) {
self.DAO.prototype.validators[name] = definition.validate; self.DAO.prototype.validators[name] = definition.validate;
} }
}); })
this.DAO.prototype.__factory = this; this.DAO.prototype.__factory = this
this.DAO.prototype.hasDefaultValues = !Utils._.isEmpty(this.DAO.prototype.defaultValues); this.DAO.prototype.hasDefaultValues = !Utils._.isEmpty(this.DAO.prototype.defaultValues)
return this return this
} }
...@@ -606,6 +616,10 @@ module.exports = (function() { ...@@ -606,6 +616,10 @@ module.exports = (function() {
return this.QueryInterface.bulkUpdate(this.tableName, attrValueHash, where) return this.QueryInterface.bulkUpdate(this.tableName, attrValueHash, where)
} }
DAOFactory.prototype.describe = function() {
return this.QueryInterface.describeTable(this.tableName)
}
// private // private
var query = function() { var query = function() {
...@@ -713,6 +727,16 @@ module.exports = (function() { ...@@ -713,6 +727,16 @@ module.exports = (function() {
} }
} }
var replaceReferencesWithTableNames = function(attributes) {
Object.keys(attributes).forEach(function(attrName) {
if (attributes[attrName].references instanceof DAOFactory) {
attributes[attrName].references = attributes[attrName].references.tableName
}
})
return attributes
}
Utils._.extend(DAOFactory.prototype, require("./associations/mixin")) Utils._.extend(DAOFactory.prototype, require("./associations/mixin"))
return DAOFactory return DAOFactory
......
...@@ -514,7 +514,6 @@ module.exports = (function() { ...@@ -514,7 +514,6 @@ module.exports = (function() {
if(dataType.references) { if(dataType.references) {
template += " REFERENCES " + this.quoteIdentifier(dataType.references) template += " REFERENCES " + this.quoteIdentifier(dataType.references)
if(dataType.referencesKey) { if(dataType.referencesKey) {
template += " (" + this.quoteIdentifier(dataType.referencesKey) + ")" template += " (" + this.quoteIdentifier(dataType.referencesKey) + ")"
} else { } else {
......
...@@ -183,11 +183,12 @@ module.exports = (function() { ...@@ -183,11 +183,12 @@ module.exports = (function() {
// if you call "define" multiple times for the same daoName, do not clutter the factory // if you call "define" multiple times for the same daoName, do not clutter the factory
if(this.isDefined(daoName)) { if(this.isDefined(daoName)) {
this.daoFactoryManager.removeDAO(this.daoFactoryManager.getDAO(daoName)) this.daoFactoryManager.removeDAO(this.daoFactoryManager.getDAO(daoName))
} }
var factory = new DAOFactory(daoName, attributes, options) var factory = new DAOFactory(daoName, attributes, options)
this.daoFactoryManager.addDAO(factory.init(this.daoFactoryManager)) this.daoFactoryManager.addDAO(factory.init(this.daoFactoryManager))
return factory return factory
} }
......
...@@ -32,24 +32,24 @@ ...@@ -32,24 +32,24 @@
"url": "https://github.com/sequelize/sequelize/issues" "url": "https://github.com/sequelize/sequelize/issues"
}, },
"dependencies": { "dependencies": {
"lodash": "~1.2.1", "lodash": "~1.3.1",
"underscore.string": "~2.3.0", "underscore.string": "~2.3.0",
"lingo": "~0.0.5", "lingo": "~0.0.5",
"validator": "1.1.1", "validator": "~1.3.0",
"moment": "~1.7.0", "moment": "~2.1.0",
"commander": "~0.6.0", "commander": "~1.3.0",
"dottie": "0.0.6-1", "dottie": "0.0.8-0",
"toposort-class": "0.1.4", "toposort-class": "~0.2.0",
"generic-pool": "2.0.3", "generic-pool": "2.0.3",
"promise": "~3.0.0" "promise": "~3.2.0"
}, },
"devDependencies": { "devDependencies": {
"jasmine-node": "1.5.0", "jasmine-node": "~1.10.2",
"sqlite3": "~2.1.5", "sqlite3": "~2.1.12",
"mysql": "~2.0.0-alpha7", "mysql": "~2.0.0-alpha8",
"pg": "~2.0.0", "pg": "~2.1.0",
"buster": "~0.6.3", "buster": "~0.6.3",
"watchr": "~2.2.0", "watchr": "~2.4.3",
"yuidocjs": "~0.3.36", "yuidocjs": "~0.3.36",
"semver": "~2.0.8" "semver": "~2.0.8"
}, },
......
...@@ -2368,4 +2368,124 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() { ...@@ -2368,4 +2368,124 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() {
}.bind(this)) }.bind(this))
}) })
}) })
describe('references', function() {
before(function() {
this.Author = this.sequelize.define('author', { firstName: Sequelize.STRING })
})
describe("use of existing dao factory", function() {
before(function() {
this.Post = this.sequelize.define('post', {
title: Sequelize.STRING,
authorId: {
type: Sequelize.INTEGER,
references: this.Author,
referencesKey: "id"
}
})
this.Author.hasMany(this.Post)
this.Post.belongsTo(this.Author)
})
it('references the author table', function(done) {
this.Author.sync({ force: true }).success(function() {
this.Post.sync({ force: true }).on('sql', function(sql) {
if (dialect === 'postgres') {
expect(sql).toMatch(/"authorId" INTEGER REFERENCES "authors" \("id"\)/)
} else if (dialect === 'mysql') {
expect(sql).toMatch(/FOREIGN KEY \(`authorId`\) REFERENCES `authors` \(`id`\)/)
} else if (dialect === 'sqlite') {
expect(sql).toMatch(/`authorId` INTEGER REFERENCES `authors` \(`id`\)/)
} else {
throw new Error('Undefined dialect!')
}
done()
})
}.bind(this))
})
})
describe('use of table name as string', function() {
before(function() {
this.Post = this.sequelize.define('post', {
title: Sequelize.STRING,
authorId: {
type: Sequelize.INTEGER,
references: 'authors',
referencesKey: "id"
}
})
this.Author.hasMany(this.Post)
this.Post.belongsTo(this.Author)
})
it('references the author table', function(done) {
this.Author.sync({ force: true }).success(function() {
this.Post.sync({ force: true }).on('sql', function(sql) {
if (dialect === 'postgres') {
expect(sql).toMatch(/"authorId" INTEGER REFERENCES "authors" \("id"\)/)
} else if (dialect === 'mysql') {
expect(sql).toMatch(/FOREIGN KEY \(`authorId`\) REFERENCES `authors` \(`id`\)/)
} else if (dialect === 'sqlite') {
expect(sql).toMatch(/`authorId` INTEGER REFERENCES `authors` \(`id`\)/)
} else {
throw new Error('Undefined dialect!')
}
done()
})
}.bind(this))
})
})
describe('use of invalid table name', function() {
before(function() {
this.Post = this.sequelize.define('post', {
title: Sequelize.STRING,
authorId: {
type: Sequelize.INTEGER,
references: '4uth0r5',
referencesKey: "id"
}
})
this.Author.hasMany(this.Post)
this.Post.belongsTo(this.Author)
})
it("emits the error event as the referenced table name is invalid", function(done) {
this.Author.sync({ force: true }).success(function() {
this.Post
.sync({ force: true })
.success(function() {
if (dialect === 'sqlite') {
// sorry ... but sqlite is too stupid to understand whats going on ...
expect(1).toEqual(1)
done()
} else {
// the parser should not end up here ...
expect(2).toEqual(1)
}
}).error(function(err) {
if (dialect === 'mysql') {
expect(err.message).toMatch(/ER_CANT_CREATE_TABLE/)
} else if (dialect === 'sqlite') {
// the parser should not end up here ... see above
expect(1).toEqual(2)
} else if (dialect === 'postgres') {
expect(err.message).toMatch(/relation "4uth0r5" does not exist/)
} else {
throw new Error('Undefined dialect!')
}
done()
})
}.bind(this))
})
})
}) //- describe: references
}) })
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!