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

Commit ef5bfd20 by Sascha Depold

Merge branch 'master' into milestones/2.0.0

2 parents a8f6867c aadec576
......@@ -17,6 +17,9 @@
- [BUG] Columns with type BOOLEAN were always added to toJSON output, even if they were not selected [see](https://gist.github.com/gchaincl/45aca14e93934bf4a05e). janmeier
- [BUG] Hstore is now fully supported [#695](https://github.com/sequelize/sequelize/pull/695). thanks to tadman
- [BUG] Correct join table name for tables with custom names [#698](https://github.com/sequelize/sequelize/pull/698). thanks to jjclark1982
- [BUG] PostgreSQL should now be able to insert empty arrays with typecasting. [#718](https://github.com/sequelize/sequelize/pull/718). thanks to durango
- [BUG] Fields should be escaped by quoteIdentifier for max/min functions which allows SQL reserved keywords to be used. [#719](https://github.com/sequelize/sequelize/pull/719). thanks to durango
- [BUG] Fixed bug when trying to save objects with eagerly loaded attributes [#716](https://github.com/sequelize/sequelize/pull/716). thanks to iamjochen
- [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
......@@ -34,6 +37,7 @@
- [FEATURE] Use moment for better postgres timestamp strings. [#710](https://github.com/sequelize/sequelize/pull/710). Thanks to seth-admittedly
- [FEATURE] Keep milliseconds in timestamps for postgres. [#712](https://github.com/sequelize/sequelize/pull/712). Thanks to seth-admittedly
- [FEATURE] You can now set lingo's language through Sequelize. [#713](https://github.com/sequelize/sequelize/pull/713). Thanks to durango
- [FEATURE] Added a `findAndCountAll`, useful for pagination. [#533](https://github.com/sequelize/sequelize/pull/533). Thanks to iamjochen
# v1.6.0 #
- [DEPENDENCIES] upgrade mysql to alpha7. You *MUST* use this version or newer for DATETIMEs to work
......
......@@ -297,16 +297,61 @@ module.exports = (function() {
return this.QueryInterface.rawSelect(this.getTableName(), options, 'count')
}
DAOFactory.prototype.findAndCountAll = function(options) {
var self = this
, opts = Utils._.cloneDeep(options)
, copts = Utils._.extend({}, Utils._.cloneDeep(options) || {}, {
// no limit, offset, order or include for the options given to count()
offset : 0,
limit : 0,
order : null,
include : null
})
return new Utils.CustomEventEmitter(function (emitter) {
var emit = {
err : function(e) { // emit error
emitter.emit('error', e);
}
, okay : function(c, r) { // emit success
emitter.emit('success', {
count: c || 0,
rows : (r && Array.isArray(r) ? r : [])
});
}
, sql : function(s) { // emit SQL
emitter.emit('sql', s);
}
}
self.count(copts)
.on('sql', emit.sql)
.error(emit.err)
.success(function(cnt) {
if (cnt === 0)
return emit.okay(cnt) // no records, no need for another query
self.findAll(options)
.on('sql', emit.sql)
.error(emit.err)
.success(function(rows) {
emit.okay(cnt, rows)
})
})
}).run()
}
DAOFactory.prototype.max = function(field, options) {
options = Utils._.extend({ attributes: [] }, options || {})
options.attributes.push(['max(' + field + ')', 'max'])
options.attributes.push(['max(' + this.QueryInterface.QueryGenerator.quoteIdentifier(field) + ')', 'max'])
options.parseFloat = true
return this.QueryInterface.rawSelect(this.getTableName(), options, 'max')
}
DAOFactory.prototype.min = function(field, options) {
options = Utils._.extend({ attributes: [] }, options || {})
options.attributes.push(['min(' + field + ')', 'min'])
options.attributes.push(['min(' + this.QueryInterface.QueryGenerator.quoteIdentifier(field) + ')', 'min'])
options.parseFloat = true
return this.QueryInterface.rawSelect(this.getTableName(), options, 'min')
......@@ -396,7 +441,7 @@ module.exports = (function() {
daos.forEach(function(dao) {
var values = {};
fields.forEach(function(field) {
values[field] = dao.values[field]
values[field] = dao.dataValues[field]
})
if (self.options.timestamps) {
values[updatedAtAttr] = Utils.now()
......@@ -406,7 +451,7 @@ module.exports = (function() {
} else {
daos.forEach(function(dao) {
records.push(dao.values)
records.push(dao.dataValues)
})
}
......
......@@ -55,9 +55,9 @@ var validateAttributes = function() {
var errors = {}
// for each field and value
Utils._.each(this.model.values, function(value, field) {
Utils._.each(this.model.dataValues, function(value, field) {
var rawAttribute = this.model.rawAttributes[field]
, hasAllowedNull = ((rawAttribute.allowNull === true) && ((value === null) || (value === undefined)))
, hasAllowedNull = ((rawAttribute === undefined || rawAttribute.allowNull === true) && ((value === null) || (value === undefined)))
if (this.model.validators.hasOwnProperty(field) && !hasAllowedNull) {
errors = Utils._.merge(errors, validateAttribute.call(this, value, field))
......
......@@ -93,7 +93,7 @@ module.exports = (function() {
// only those fields will be updated
DAO.prototype.save = function(fields) {
var self = this
, values = fields ? {} : this.values
, values = fields ? {} : this.dataValues
, updatedAtAttr = this.__options.underscored ? 'updated_at' : 'updatedAt'
, createdAtAttr = this.__options.underscored ? 'created_at' : 'createdAt'
......@@ -108,7 +108,7 @@ module.exports = (function() {
}
}
var tmpVals = self.values
var tmpVals = self.dataValues
fields.forEach(function(field) {
if (tmpVals[field] !== undefined) {
......@@ -277,7 +277,7 @@ module.exports = (function() {
DAO.prototype.equals = function(other) {
var result = true
Utils._.each(this.values, function(value, key) {
Utils._.each(this.dataValues, function(value, key) {
if(Utils._.isDate(value) && Utils._.isDate(other[key])) {
result = result && (value.getTime() == other[key].getTime())
} else {
......
......@@ -241,7 +241,9 @@ module.exports = (function() {
}
var connect = function(done, config) {
var emitter = new (require('events').EventEmitter)()
config = config || this.config
var connection = mysql.createConnection({
host: config.host,
port: config.port,
......@@ -250,6 +252,23 @@ module.exports = (function() {
database: config.database,
timezone: 'Z'
})
connection.connect(function(err) {
if (err) {
switch(err.code) {
case 'ECONNREFUSED':
case 'ER_ACCESS_DENIED_ERROR':
emitter.emit('error', new Error("Failed to authenticate for MySQL. Please double check your settings."))
break
case 'ENOTFOUND':
case 'EHOSTUNREACH':
case 'EINVAL':
emitter.emit('error', new Error("Failed to find MySQL server. Please double check your settings."))
break
}
}
})
connection.query("SET time_zone = '+0:00'");
// client.setMaxListeners(self.maxConcurrentQueries)
this.isConnecting = false
......
......@@ -27,7 +27,8 @@ module.exports = (function() {
ConnectorManager.prototype.query = function(sql, callee, options) {
var self = this
if (this.client == null) {
if (this.client === null) {
this.connect()
}
......@@ -68,7 +69,18 @@ module.exports = (function() {
self.isConnecting = false
if (!!err) {
emitter.emit('error', err)
switch(err.code) {
case 'ECONNREFUSED':
emitter.emit('error', new Error("Failed to authenticate for PostgresSQL. Please double check your settings."))
break
case 'ENOTFOUND':
case 'EHOSTUNREACH':
case 'EINVAL':
emitter.emit('error', new Error("Failed to find PostgresSQL server. Please double check your settings."))
break
default:
emitter.emit('error', err)
}
} else if (client) {
client.query("SET TIME ZONE 'UTC'")
.on('end', function() {
......
......@@ -289,7 +289,7 @@ module.exports = (function() {
return Utils._.template(query)(options)
},
insertQuery: function(tableName, attrValueHash) {
insertQuery: function(tableName, attrValueHash, attributes) {
attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull)
var query = "INSERT INTO <%= table %> (<%= attributes %>) VALUES (<%= values %>) RETURNING *;"
......@@ -306,14 +306,17 @@ module.exports = (function() {
}
});
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: Utils._.values(attrValueHash).map(function(value){
return this.escape(value)
}.bind(this)).join(",")
, values: rowValues.join(",")
}
return Utils._.template(query)(replacements)
......@@ -734,7 +737,7 @@ module.exports = (function() {
return identifiers.split('.').map(function(t) { return this.quoteIdentifier(t, force) }.bind(this)).join('.')
},
escape: function (val) {
escape: function (val, field) {
if (val === undefined || val === null) {
return 'NULL';
}
......@@ -746,7 +749,11 @@ module.exports = (function() {
return val + '';
case 'object':
if (Array.isArray(val)) {
return 'ARRAY['+ val.map(function(it) { return this.escape(it) }.bind(this)).join(',') + ']';
var ret = 'ARRAY['+ val.map(function(it) { return this.escape(it) }.bind(this)).join(',') + ']'
if (!!field && !!field.type) {
ret += '::' + field.type.replace(/\(\d+\)/g, '')
}
return ret
}
}
......
......@@ -5,17 +5,34 @@ var Utils = require("../../utils")
module.exports = (function() {
var ConnectorManager = function(sequelize) {
this.sequelize = sequelize
this.database = db = new sqlite3.Database(sequelize.options.storage || ':memory:', function(err) {
if(!err && sequelize.options.foreignKeys !== false) {
}
Utils._.extend(ConnectorManager.prototype, require("../connector-manager").prototype)
ConnectorManager.prototype.connect = function() {
var emitter = new (require('events').EventEmitter)()
, self = this
this.database = db = new sqlite3.Database(self.sequelize.options.storage || ':memory:', function(err) {
if (err) {
if (err.code === "SQLITE_CANTOPEN") {
emitter.emit('error', new Error("Failed to find SQL server. Please double check your settings."))
}
}
if(!err && self.sequelize.options.foreignKeys !== false) {
// Make it possible to define and use foreign key constraints unless
// explicitly disallowed. It's still opt-in per relation
db.run('PRAGMA FOREIGN_KEYS=ON')
}
})
}
Utils._.extend(ConnectorManager.prototype, require("../connector-manager").prototype)
ConnectorManager.prototype.query = function(sql, callee, options) {
if (!this.database) {
this.connect()
}
return new Query(this.database, this.sequelize, callee, options).run(sql)
}
......
......@@ -254,7 +254,7 @@ module.exports = (function() {
}
QueryInterface.prototype.insert = function(dao, tableName, values) {
var sql = this.QueryGenerator.insertQuery(tableName, values)
var sql = this.QueryGenerator.insertQuery(tableName, values, dao.daoFactory.rawAttributes)
return queryAndEmit.call(this, [sql, dao], 'insert', {
success: function(obj) { obj.isNewRecord = false }
})
......
......@@ -208,6 +208,74 @@ describe('DAOFactory', function() {
})
})
describe('findAndCountAll', function() {
var users = [], fullcount = 3
beforeEach(function() {
Helpers.Factories.User({name: 'user', bio: 'foobar'}, function(_users) {
users = _users
}, fullcount)
})
it("handles where clause [only]", function() {
Helpers.async(function(done) {
User.findAndCountAll({where: "id != " + users[0].id}).success(function(info) {
expect(info.count).toEqual(fullcount - 1)
expect(Array.isArray(info.rows)).toBeTruthy()
expect(info.rows.length).toEqual(fullcount - 1)
done()
})
})
})
it("handles where clause with ordering [only]", function() {
Helpers.async(function(done) {
User.findAndCountAll({where: "id != " + users[0].id, order: 'id ASC'}).success(function(info) {
expect(info.count).toEqual(fullcount - 1)
expect(Array.isArray(info.rows)).toBeTruthy()
expect(info.rows.length).toEqual(fullcount - 1)
done()
})
})
})
/*
// at time of writing (v1.6.0) Sequelize does not seem to support 'offset' on it's own consistently (goes wrong for PostGRES and SQLite)
it("handles offset", function() {
Helpers.async(function(done) {
User.findAndCountAll({offset: 1}).success(function(info) {
expect(info.count).toEqual(fullcount)
expect(Array.isArray(info.rows)).toBeTruthy()
expect(info.rows.length).toEqual(fullcount - 1)
done()
})
})
})
*/
it("handles limit", function() {
Helpers.async(function(done) {
User.findAndCountAll({limit: 1}).success(function(info) {
expect(info.count).toEqual(fullcount)
expect(Array.isArray(info.rows)).toBeTruthy()
expect(info.rows.length).toEqual(1)
done()
})
})
})
it("handles offset and limit", function() {
Helpers.async(function(done) {
User.findAndCountAll({offset: 1, limit: 1}).success(function(info) {
expect(info.count).toEqual(fullcount)
expect(Array.isArray(info.rows)).toBeTruthy()
expect(info.rows.length).toEqual(1)
done()
})
})
})
})
describe('all', function() {
beforeEach(function() {
Helpers.Factories.User({name: 'user', bio: 'foobar'}, null, 2)
......
......@@ -372,7 +372,7 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() {
this.Task = this.sequelize.define('Task', { title: Sequelize.STRING }, {timestamps: false})
this.User.hasMany(this.Task)
this.Task.hasMany(this.User)
this.Task.hasMany(this.User)
this.sequelize.sync({force: true}).success(done)
})
......@@ -385,7 +385,7 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() {
this.Task.create({ title: 'task2' }).success(function(task2) {
user.setTasks([task1, task2]).on('sql', spy).on('sql', _.after(2, function (sql) {
expect(sql).toMatch("INSERT INTO")
expect(sql).toMatch("VALUES (1,1),(2,1)")
expect(sql).toMatch("VALUES (1,1),(1,2)")
})).success(function () {
expect(spy).toHaveBeenCalledTwice() // Once for SELECT, once for INSERT into
done()
......@@ -415,7 +415,7 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() {
}.bind(this))
})
}) // end optimization using bulk create, destroy and update
describe('join table creation', function () {
before(function (done) {
this.User = this.sequelize.define('User',
......@@ -430,7 +430,7 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() {
this.User.hasMany(this.Task,
{ joinTableName: 'user_has_tasks' }
)
this.Task.hasMany(this.User)
this.Task.hasMany(this.User)
this.sequelize.sync({force: true}).success(done)
})
......
if(typeof require === 'function') {
const buster = require("buster")
, CustomEventEmitter = require("../lib/emitters/custom-event-emitter")
, Helpers = require('./buster-helpers')
, dialect = Helpers.getTestDialect()
const buster = require("buster")
, CustomEventEmitter = require("../lib/emitters/custom-event-emitter")
, Helpers = require('./buster-helpers')
, config = require(__dirname + "/config/config")
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
......@@ -11,6 +12,86 @@ buster.testRunner.timeout = 1000
var Sequelize = require(__dirname + '/../index')
describe(Helpers.getTestDialectTeaser("Configuration"), function() {
describe('Connections problems should fail with a nice message', function() {
it('should give us an error for not having the correct server details', function(done) {
var domain
try {
domain = require('domain')
} catch (err) {
console.log('WARNING: Configuration specs requires Node version >= 0.8')
expect('').toEqual('') // Silence Buster!
done()
}
var d = domain.create()
var sequelize = new Sequelize(config[dialect].database, config[dialect].username, config[dialect].password, {storage: '/path/to/no/where/land', logging: false, host: '0.0.0.1', port: config[dialect].port, dialect: dialect})
d.add(sequelize.query)
d.on('error', function(err){
d.remove(sequelize.query)
var msg = 'Failed to find SQL server. Please double check your settings.'
if (dialect === "postgres" || dialect === "postgres-native") {
msg = 'Failed to find PostgresSQL server. Please double check your settings.'
}
else if (dialect === "mysql") {
msg = 'Failed to find MySQL server. Please double check your settings.'
}
expect(err.message).toEqual(msg)
done()
})
d.run(function(){
sequelize.query('select 1 as hello')
.success(function(){})
})
})
it('should give us an error for not having the correct login information', function(done) {
if (dialect !== "postgres" && dialect !== "postgres-native" && dialect !== "mysql") {
// This dialect doesn't support incorrect login information
expect('').toEqual('') // Silence Buster
return done()
}
var domain
try {
domain = require('domain')
} catch (err) {
console.log('WARNING: Configuration specs requires Node version >= 0.8')
expect('').toEqual('') // Silence Buster!
done()
}
var d = domain.create()
var sequelize = new Sequelize(config[dialect].database, config[dialect].username, 'fakepass123', {logging: false, host: config[dialect].host, port: 1, dialect: dialect})
d.add(sequelize.query)
d.on('error', function(err){
d.remove(sequelize.query)
var msg = 'Failed to authenticate for SQL. Please double check your settings.'
if (dialect === "postgres" || dialect === "postgres-native") {
msg = 'Failed to authenticate for PostgresSQL. Please double check your settings.'
}
else if (dialect === "mysql") {
msg = 'Failed to authenticate for MySQL. Please double check your settings.'
}
expect(err.message).toEqual(msg)
done()
})
d.run(function(){
sequelize.query('select 1 as hello')
.success(function(){})
})
})
})
describe('Instantiation with a URL string', function() {
it('should accept username, password, host, port, and database', function() {
var sequelize = new Sequelize('mysql://user:pass@example.com:9821/dbname')
......
......@@ -281,6 +281,26 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() {
})
describe('create', function() {
it("casts empty arrays correctly for postgresql", function(done) {
if (dialect !== "postgres" && dialect !== "postgresql-native") {
expect('').toEqual('')
return done()
}
var User = this.sequelize.define('UserWithArray', {
myvals: { type: Sequelize.ARRAY(Sequelize.INTEGER) },
mystr: { type: Sequelize.ARRAY(Sequelize.STRING) }
})
User.sync({force: true}).success(function() {
User.create({myvals: [], mystr: []}).on('sql', function(sql){
expect(sql.indexOf('ARRAY[]::INTEGER[]')).toBeGreaterThan(-1)
expect(sql.indexOf('ARRAY[]::VARCHAR[]')).toBeGreaterThan(-1)
done()
})
})
})
it("doesn't allow duplicated records with unique:true", function(done) {
var User = this.sequelize.define('UserWithUniqueUsername', {
username: { type: Sequelize.STRING, unique: true }
......@@ -357,7 +377,7 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() {
User.sync({ force: true }).success(function() {
User.create({ big: '9223372036854775807' }).on('success', function(user) {
expect(user.big).toEqual( '9223372036854775807' )
expect(user.big).toBe( '9223372036854775807' )
done()
})
})
......@@ -984,8 +1004,8 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() {
]
, done = _.after(2 * permutations.length, _done);
this.User.create({name: 'jack'}).success(function (jack) {
this.User.create({name: 'jill'}).success(function (jill) {
this.User.create({username: 'jack'}).success(function (jack) {
this.User.create({username: 'jill'}).success(function (jill) {
permutations.forEach(function(perm) {
this.User.find(perm).done(function(err, user) {
expect(err).toBeNull();
......@@ -1635,7 +1655,8 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() {
describe('max', function() {
before(function(done) {
this.UserWithAge = this.sequelize.define('UserWithAge', {
age: Sequelize.INTEGER
age: Sequelize.INTEGER,
order: Sequelize.INTEGER
})
this.UserWithDec = this.sequelize.define('UserWithDec', {
......@@ -1647,6 +1668,15 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() {
}.bind(this))
})
it("should return the max value for a field named the same as an SQL reserved keyword", function(done) {
this.UserWithAge.create({age: 3, order: 5}).success(function(){
this.UserWithAge.max('order').success(function(max) {
expect(max).toEqual(5)
done()
})
}.bind(this))
})
it("should return the max value", function(done) {
this.UserWithAge.create({ age: 2 }).success(function() {
this.UserWithAge.create({ age: 3 }).success(function() {
......
......@@ -444,6 +444,140 @@ describe(Helpers.getTestDialectTeaser("DAO"), function() {
})
})
})
before(function(done) {
this.UserEager = this.sequelize.define('UserEagerLoadingSaves', {
username : Helpers.Sequelize.STRING,
age : Helpers.Sequelize.INTEGER
}, { timestamps: false })
this.ProjectEager = this.sequelize.define('ProjectEagerLoadingSaves', {
title : Helpers.Sequelize.STRING,
overdue_days: Helpers.Sequelize.INTEGER
}, { timestamps: false })
this.UserEager.hasMany(this.ProjectEager, { as: 'Projects' })
this.ProjectEager.belongsTo(this.UserEager, { as: 'Poobah' })
this.sequelize.sync({ force: true }).success(done)
})
it('saves one object that has a collection of eagerly loaded objects', function(done) {
this.UserEager.create({ username: 'joe', age: 1 }).success(function(user) {
this.ProjectEager.create({ title: 'project-joe1', overdue_days: 0 }).success(function(project1) {
this.ProjectEager.create({ title: 'project-joe2', overdue_days: 0 }).success(function(project2) {
user.setProjects([project1, project2]).success(function() {
this.UserEager.find({where: {age: 1}, include: [{model: this.ProjectEager, as: 'Projects'}]}).success(function(user) {
expect(user.username).toEqual('joe')
expect(user.age).toEqual(1)
expect(user.projects).toBeDefined()
expect(user.projects.length).toEqual(2)
user.age = user.age + 1; // happy birthday joe
user.save().success(function(saveduser) {
expect(user.username).toEqual('joe')
expect(user.age).toEqual(2)
expect(user.projects).toBeDefined()
expect(user.projects.length).toEqual(2)
done()
})
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
})
it('saves many objects that each a have collection of eagerly loaded objects', function(done) {
this.UserEager.create({ username: 'bart', age: 20 }).success(function(bart) {
this.UserEager.create({ username: 'lisa', age: 20 }).success(function(lisa) {
this.ProjectEager.create({ title: 'detention1', overdue_days: 0 }).success(function(detention1) {
this.ProjectEager.create({ title: 'detention2', overdue_days: 0 }).success(function(detention2) {
this.ProjectEager.create({ title: 'exam1', overdue_days: 0 }).success(function(exam1) {
this.ProjectEager.create({ title: 'exam2', overdue_days: 0 }).success(function(exam2) {
bart.setProjects([detention1, detention2]).success(function() {
lisa.setProjects([exam1, exam2]).success(function() {
this.UserEager.findAll({where: {age: 20}, order: 'username ASC', include: [{model: this.ProjectEager, as: 'Projects'}]}).success(function(simpsons) {
var _bart, _lisa;
expect(simpsons.length).toEqual(2)
_bart = simpsons[0];
_lisa = simpsons[1];
expect(_bart.projects).toBeDefined()
expect(_lisa.projects).toBeDefined()
expect(_bart.projects.length).toEqual(2)
expect(_lisa.projects.length).toEqual(2)
_bart.age = _bart.age + 1; // happy birthday bart - off to Moe's
_bart.save().success(function(savedbart) {
expect(savedbart.username).toEqual('bart')
expect(savedbart.age).toEqual(21)
_lisa.username = 'lsimpson';
_lisa.save().success(function(savedlisa) {
expect(savedlisa.username).toEqual('lsimpson')
expect(savedlisa.age).toEqual(20)
done()
})
})
})
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
})
it('saves many objects that each has one eagerly loaded object (to which they belong)', function(done) {
this.UserEager.create({ username: 'poobah', age: 18 }).success(function(user) {
this.ProjectEager.create({ title: 'homework', overdue_days: 10 }).success(function(homework) {
this.ProjectEager.create({ title: 'party', overdue_days: 2 }).success(function(party) {
user.setProjects([homework, party]).success(function() {
this.ProjectEager.findAll({include: [{model: this.UserEager, as: 'Poobah'}]}).success(function(projects) {
expect(projects.length).toEqual(2)
expect(projects[0].poobah).toBeDefined()
expect(projects[1].poobah).toBeDefined()
expect(projects[0].poobah.username).toEqual('poobah')
expect(projects[1].poobah.username).toEqual('poobah')
projects[0].title = 'partymore';
projects[1].title = 'partymore';
projects[0].overdue_days = 0;
projects[1].overdue_days = 0;
projects[0].save().success(function() {
projects[1].save().success(function() {
this.ProjectEager.findAll({where: {title: 'partymore', overdue_days: 0}, include: [{model: this.UserEager, as: 'Poobah'}]}).success(function(savedprojects) {
expect(savedprojects.length).toEqual(2)
expect(savedprojects[0].poobah).toBeDefined()
expect(savedprojects[1].poobah).toBeDefined()
expect(savedprojects[0].poobah.username).toEqual('poobah')
expect(savedprojects[1].poobah.username).toEqual('poobah')
done()
})
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
})
})
describe('toJSON', function toJSON() {
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!