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

Commit fc65f200 by Sascha Depold

merge

2 parents 0b090296 cf4ab025
......@@ -42,6 +42,8 @@
- [BUG] Added tests & bugfixes for DAO-Factory.update and array of values in where clause [#880](https://github.com/sequelize/sequelize/pull/880). thanks to domasx2
- [BUG] sqlite no longer leaks a global `db` variable [#900](https://github.com/sequelize/sequelize/pull/900). thanks to xming
- [BUG] Fix for counts queries with no result [#906](https://github.com/sequelize/sequelize/pull/906). thanks to iamjochem
- [BUG] Allow include when the same table is referenced multiple times using hasMany [#913](https://github.com/sequelize/sequelize/pull/913). thanks to janmeier
- [BUG] Allow definition of defaultValue for the timestamp columns (createdAt, updatedAt, deletedAt) [#930](https://github.com/sequelize/sequelize/pull/930). Thank to durango
- [FEATURE] Validate a model before it gets saved. [#601](https://github.com/sequelize/sequelize/pull/601). thanks to durango
- [FEATURE] Schematics. [#564](https://github.com/sequelize/sequelize/pull/564). thanks to durango
- [FEATURE] Foreign key constraints. [#595](https://github.com/sequelize/sequelize/pull/595). thanks to optilude
......@@ -75,8 +77,8 @@
- [FEATURE] Added Sequelize.fn() and Sequelize.col() to properly call columns and functions within Sequelize. [#882](https://github.com/sequelize/sequelize/pull/882). thanks to janmeier
- [FEATURE] Sequelize.import supports relative paths. [#901](https://github.com/sequelize/sequelize/pull/901). thanks to accerqueira.
- [FEATURE] Sequelize.import can now handle functions. [#911](https://github.com/sequelize/sequelize/pull/911). Thanks to davidrivera.
- [BUG] Allow include when the same table is referenced multiple times using hasMany [#913](https://github.com/sequelize/sequelize/pull/913). thanks to janmeier
- [FEATURE] Uses sequelize.fn and sequelize.col functionality to allow you to use the value of another column or a function when updating. It also allows you to use a function as a default value when supported (in sqlite and postgres). [#928](https://github.com/sequelize/sequelize/pull/928). thanks to janmeier
- [FEATURE] Added possibility to pass options to node-mysql. [#929](https://github.com/sequelize/sequelize/pull/929). thanks to poying
- [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] Moved Jasmine to Buster and then Buster to Mocha + Chai. sdepold and durango
......
......@@ -339,6 +339,8 @@ module.exports = (function() {
this.options.whereCollection = options.where || null
}
options = paranoidClause.call(this, options)
return this.QueryInterface.select(this, this.tableName, options, Utils._.defaults({
type: 'SELECT',
hasJoin: hasJoin
......@@ -430,6 +432,7 @@ module.exports = (function() {
}
}
options = paranoidClause.call(this, options)
options.limit = 1
return this.QueryInterface.select(this, this.getTableName(), options, Utils._.defaults({
......@@ -444,6 +447,8 @@ module.exports = (function() {
options.attributes.push(['count(*)', 'count'])
options.parseInt = true
options = paranoidClause.call(this, options)
return this.QueryInterface.rawSelect(this.getTableName(), options, 'count')
}
......@@ -609,8 +614,13 @@ module.exports = (function() {
})
if (self.options.timestamps) {
values[createdAtAttr] = Utils.now()
values[updatedAtAttr] = Utils.now()
if (!values[createdAtAttr]) {
values[createdAtAttr] = Utils.now(self.daoFactoryManager.sequelize.options.dialect)
}
if (!values[updatedAtAttr]) {
values[updatedAtAttr] = Utils.now(self.daoFactoryManager.sequelize.options.dialect)
}
}
records.push(values)
......@@ -722,6 +732,29 @@ module.exports = (function() {
// private
var paranoidClause = function(options) {
if (this.options.paranoid === true) {
options = options || {}
options.where = options.where || {}
// Don't overwrite our explicit deletedAt search value if we provide one
if (!!options.where[this.options.deletedAt]) {
return options
}
if (typeof options.where === "string") {
options.where += ' AND ' + this.QueryInterface.quoteIdentifier(this.options.deletedAt) + ' IS NULL '
}
else if (Array.isArray(options.where)) {
options.where[0] += ' AND ' + this.QueryInterface.quoteIdentifier(this.options.deletedAt) + ' IS NULL '
} else {
options.where[this.options.deletedAt] = null
}
}
return options
}
var addOptionalClassMethods = function() {
var self = this
Utils._.each(this.options.classMethods || {}, function(fct, name) { self[name] = fct })
......
......@@ -157,7 +157,7 @@ module.exports = (function() {
}
if (this.__options.timestamps && this.dataValues.hasOwnProperty(updatedAtAttr)) {
this.dataValues[updatedAtAttr] = values[updatedAtAttr] = Utils.now(this.sequelize.options.dialect)
this.dataValues[updatedAtAttr] = values[updatedAtAttr] = (this.isNewRecord && !!this.daoFactory.rawAttributes[updatedAtAttr] && !!this.daoFactory.rawAttributes[updatedAtAttr].defaultValue ? this.daoFactory.rawAttributes[updatedAtAttr].defaultValue : Utils.now(this.sequelize.options.dialect))
}
return new Utils.CustomEventEmitter(function(emitter) {
......@@ -416,10 +416,15 @@ module.exports = (function() {
}
if (this.__options.timestamps) {
if (!this.defaultValues[Utils._.underscoredIf(this.__options.createdAt, this.__options.underscored)]) {
defaults[Utils._.underscoredIf(this.__options.createdAt, this.__options.underscored)] = Utils.now(this.sequelize.options.dialect)
}
if (!this.defaultValues[Utils._.underscoredIf(this.__options.updatedAt, this.__options.underscored)]) {
defaults[Utils._.underscoredIf(this.__options.updatedAt, this.__options.underscored)] = Utils.now(this.sequelize.options.dialect)
}
if (this.__options.paranoid) {
if (this.__options.paranoid && !this.defaultValues[Utils._.underscoredIf(this.__options.deletedAt, this.__options.underscored)]) {
defaults[Utils._.underscoredIf(this.__options.deletedAt, this.__options.underscored)] = null
}
}
......
......@@ -253,17 +253,25 @@ module.exports = (function() {
}
var connect = function(done, config) {
var emitter = new (require('events').EventEmitter)()
config = config || this.config
var connection = mysql.createConnection({
var emitter = new (require('events').EventEmitter)()
var connectionConfig = {
host: config.host,
port: config.port,
user: config.username,
password: config.password,
database: config.database,
timezone: 'Z'
})
};
if (config.dialectOptions) {
Object.keys(config.dialectOptions).forEach(function (key) {
connectionConfig[key] = config.dialectOptions[key];
});
}
var connection = mysql.createConnection(connectionConfig);
connection.connect(function(err) {
if (err) {
......
......@@ -103,7 +103,8 @@ module.exports = (function() {
native : this.options.native,
replication: this.options.replication,
dialectModulePath: this.options.dialectModulePath,
maxConcurrentQueries: this.options.maxConcurrentQueries
maxConcurrentQueries: this.options.maxConcurrentQueries,
dialectOptions: this.options.dialectOptions,
}
try {
......
......@@ -36,7 +36,7 @@
"url": "https://github.com/sequelize/sequelize/issues"
},
"dependencies": {
"lodash": "~2.0.0",
"lodash": "~2.1.0",
"underscore.string": "~2.3.0",
"lingo": "~0.0.5",
"validator": "~1.5.0",
......@@ -54,7 +54,7 @@
"pg": "~2.6.0",
"watchr": "~2.4.3",
"yuidocjs": "~0.3.36",
"chai": "~1.7.2",
"chai": "~1.8.0",
"mocha": "~1.13.0",
"chai-datetime": "~1.1.1",
"sinon": "~1.7.3"
......
......@@ -101,14 +101,23 @@ describe(Support.getTestDialectTeaser("Configuration"), function() {
})
it('should accept four parameters (database, username, password, options)', function(done) {
var sequelize = new Sequelize('dbname', 'root', 'pass', { port: 999 })
var sequelize = new Sequelize('dbname', 'root', 'pass', {
port: 999,
dialectOptions: {
supportBigNumbers: true,
bigNumberStrings: true
}
})
var config = sequelize.config
expect(config.database).to.equal('dbname')
expect(config.username).to.equal('root')
expect(config.password).to.equal('pass')
expect(config.port).to.equal(999)
expect(config.dialectOptions.supportBigNumbers).to.be.true
expect(config.dialectOptions.bigNumberStrings).to.be.true
done()
})
})
})
......@@ -69,6 +69,23 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
done()
})
it('allows us us to predefine the ID column with our own specs', function(done) {
var User = this.sequelize.define('UserCol', {
id: {
type: Sequelize.STRING,
defaultValue: 'User',
primaryKey: true
}
})
User.sync({ force: true }).success(function() {
User.create({id: 'My own ID!'}).success(function(user) {
expect(user.id).to.equal('My own ID!')
done()
})
})
})
it("throws an error if 2 autoIncrements are passed", function(done) {
var self = this
expect(function() {
......@@ -108,6 +125,39 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
done()
})
it('should allow me to set a default value for createdAt and updatedAt', function(done) {
var UserTable = this.sequelize.define('UserCol', {
aNumber: Sequelize.INTEGER,
createdAt: {
type: Sequelize.DATE,
defaultValue: moment('2012-01-01').toDate()
},
updatedAt: {
type: Sequelize.DATE,
defaultValue: moment('2012-01-02').toDate()
}
}, { timestamps: true })
UserTable.sync({ force: true }).success(function() {
UserTable.create({aNumber: 5}).success(function(user) {
UserTable.bulkCreate([
{aNumber: 10},
{aNumber: 12}
]).success(function() {
UserTable.all({where: {aNumber: { gte: 10 }}}).success(function(users) {
expect(moment(user.createdAt).format('YYYY-MM-DD')).to.equal('2012-01-01')
expect(moment(user.updatedAt).format('YYYY-MM-DD')).to.equal('2012-01-02')
users.forEach(function(u) {
expect(moment(u.createdAt).format('YYYY-MM-DD')).to.equal('2012-01-01')
expect(moment(u.updatedAt).format('YYYY-MM-DD')).to.equal('2012-01-02')
})
done()
})
})
})
})
})
it('should allow me to override updatedAt, createdAt, and deletedAt fields', function(done) {
var UserTable = this.sequelize.define('UserCol', {
aNumber: Sequelize.INTEGER
......@@ -385,7 +435,8 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
}
return '0' + number
}
expect(user.year).to.equal(now.getFullYear() + '-' + pad(now.getMonth() + 1) + '-' + pad(now.getDate()))
expect(user.year).to.equal(now.getUTCFullYear() + '-' + pad(now.getUTCMonth() + 1) + '-' + pad(now.getUTCDate()))
done()
})
})
......@@ -1119,6 +1170,8 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
it('sets deletedAt to the current timestamp if paranoid is true', function(done) {
var self = this
, ident = self.sequelize.queryInterface.QueryGenerator.quoteIdentifier
, escape = self.sequelize.queryInterface.QueryGenerator.quote
, ParanoidUser = self.sequelize.define('ParanoidUser', {
username: Sequelize.STRING,
secretValue: Sequelize.STRING,
......@@ -1133,18 +1186,52 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
ParanoidUser.sync({ force: true }).success(function() {
ParanoidUser.bulkCreate(data).success(function() {
var date = parseInt(+new Date()/5000, 10)
// since we save in UTC, let's format to UTC time
var date = moment().utc().format('YYYY-MM-DD h:mm')
ParanoidUser.destroy({secretValue: '42'}).success(function() {
ParanoidUser.findAll({order: 'id'}).success(function(users) {
expect(users.length).to.equal(3)
expect(users.length).to.equal(1)
expect(users[0].username).to.equal("Bob")
self.sequelize.query('SELECT * FROM ' + ident('ParanoidUsers') + ' WHERE ' + ident('deletedAt') + ' IS NOT NULL ORDER BY ' + ident('id'), null, {raw: true}).success(function(users) {
expect(users[0].username).to.equal("Peter")
expect(users[1].username).to.equal("Paul")
expect(users[2].username).to.equal("Bob")
expect(parseInt(+users[0].deletedAt/5000, 10)).to.equal(date)
expect(parseInt(+users[1].deletedAt/5000, 10)).to.equal(date)
if (dialect === "sqlite") {
expect(moment(users[0].deletedAt).format('YYYY-MM-DD h:mm')).to.equal(date)
expect(moment(users[1].deletedAt).format('YYYY-MM-DD h:mm')).to.equal(date)
} else {
expect(moment(users[0].deletedAt).utc().format('YYYY-MM-DD h:mm')).to.equal(date)
expect(moment(users[1].deletedAt).utc().format('YYYY-MM-DD h:mm')).to.equal(date)
}
done()
})
})
})
})
})
})
describe("can't find records marked as deleted with paranoid being true", function() {
it('with the DAOFactory', function(done) {
var User = this.sequelize.define('UserCol', {
username: Sequelize.STRING
}, { paranoid: true })
User.sync({ force: true }).success(function() {
User.bulkCreate([
{username: 'Toni'},
{username: 'Tobi'},
{username: 'Max'}
]).success(function() {
User.find(1).success(function(user) {
user.destroy().success(function() {
User.find(1).success(function(user) {
expect(user).to.be.null
User.count().success(function(cnt) {
expect(cnt).to.equal(2)
User.all().success(function(users) {
expect(users).to.have.length(2)
done()
})
})
......@@ -1152,6 +1239,10 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
})
})
})
})
})
})
})
describe('special where conditions/smartWhere object', function() {
beforeEach(function(done) {
......@@ -2693,6 +2784,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
it("handles offset and limit", function(done) {
var self = this
this.User.bulkCreate([{username: 'bobby'}, {username: 'tables'}]).success(function() {
self.User.findAll({ limit: 2, offset: 2 }).success(function(users) {
expect(users.length).to.equal(2)
......@@ -2780,6 +2872,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
done()
})
})
it("handles attributes", function(done) {
this.User.findAndCountAll({where: "id != " + this.users[0].id, attributes: ['data']}).success(function(info) {
expect(info.count).to.equal(2)
......
......@@ -35,7 +35,8 @@ var Support = {
logging: options.logging,
dialect: options.dialect,
port: options.port || process.env.SEQ_PORT || config[options.dialect].port,
pool: options.pool
pool: options.pool,
dialectOptions: options.dialectOptions || {}
}
if (!!options.host) {
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!