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

Commit a728a7aa by Jan Aagaard Meier

Fix default scope being overwritten. Closes #2087

1 parent aaf80ff9
...@@ -9,6 +9,7 @@ Notice: All 1.7.x changes are present in 2.0.x aswell ...@@ -9,6 +9,7 @@ Notice: All 1.7.x changes are present in 2.0.x aswell
- [BUG] Hid `dottie.transform` on raw queries behind a flag (`nest`) [#2064](https://github.com/sequelize/sequelize/pull/2064) - [BUG] Hid `dottie.transform` on raw queries behind a flag (`nest`) [#2064](https://github.com/sequelize/sequelize/pull/2064)
- [BUG] Fixed problems with transcation parameter being removed / not passed on in associations [#1789](https://github.com/sequelize/sequelize/issues/1789) and [#1968](https://github.com/sequelize/sequelize/issues/1968) - [BUG] Fixed problems with transcation parameter being removed / not passed on in associations [#1789](https://github.com/sequelize/sequelize/issues/1789) and [#1968](https://github.com/sequelize/sequelize/issues/1968)
- [BUG] Fix problem with minConnections. [#2048](https://github.com/sequelize/sequelize/issues/2048) - [BUG] Fix problem with minConnections. [#2048](https://github.com/sequelize/sequelize/issues/2048)
- [BUG] Fix default scope being overwritten [#2087](https://github.com/sequelize/sequelize/issues/2087)
- [INTERNALS] Replaced lingo with inflection - [INTERNALS] Replaced lingo with inflection
- [INTERNALS] Removed underscore.string dependency and moved a couple of helper functions from `Utils._` to `Utils` - [INTERNALS] Removed underscore.string dependency and moved a couple of helper functions from `Utils._` to `Utils`
- [INTERNALS] Update dependencies - [INTERNALS] Update dependencies
......
...@@ -497,6 +497,8 @@ module.exports = (function() { ...@@ -497,6 +497,8 @@ module.exports = (function() {
, argLength = arguments.length , argLength = arguments.length
, lastArg = arguments[argLength - 1]; , lastArg = arguments[argLength - 1];
self.scoped = true;
// Set defaults // Set defaults
scopeOptions = (typeof lastArg === 'object' && !Array.isArray(lastArg) ? lastArg : {}) || {}; // <-- for no arguments scopeOptions = (typeof lastArg === 'object' && !Array.isArray(lastArg) ? lastArg : {}) || {}; // <-- for no arguments
scopeOptions.silent = (scopeOptions !== null && scopeOptions.hasOwnProperty('silent') ? scopeOptions.silent : true); scopeOptions.silent = (scopeOptions !== null && scopeOptions.hasOwnProperty('silent') ? scopeOptions.silent : true);
......
...@@ -554,18 +554,18 @@ module.exports = (function() { ...@@ -554,18 +554,18 @@ module.exports = (function() {
return this.sequelize.query(sql, null, options); return this.sequelize.query(sql, null, options);
}; };
QueryInterface.prototype.select = function(factory, tableName, options, queryOptions) { QueryInterface.prototype.select = function(model, tableName, options, queryOptions) {
options = options || {}; options = options || {};
// See if we need to merge options and factory.scopeObj // See if we need to merge options and model.scopeObj
// we're doing this on the QueryInterface level because it's a bridge between // we're doing this on the QueryInterface level because it's a bridge between
// sequelize and the databases // sequelize and the databases
if (Object.keys(factory.scopeObj).length > 0) { if (model.options.defaultScope && Object.keys(model.options.defaultScope).length > 0) {
if (!!options) { if (!!options) {
Utils.injectScope.call(factory, options, true); Utils.injectScope.call(model, options, true);
} }
var scopeObj = buildScope.call(factory); var scopeObj = buildScope.call(model);
Object.keys(scopeObj).forEach(function(method) { Object.keys(scopeObj).forEach(function(method) {
if (typeof scopeObj[method] === 'number' || !Utils._.isEmpty(scopeObj[method])) { if (typeof scopeObj[method] === 'number' || !Utils._.isEmpty(scopeObj[method])) {
options[method] = scopeObj[method]; options[method] = scopeObj[method];
...@@ -575,7 +575,7 @@ module.exports = (function() { ...@@ -575,7 +575,7 @@ module.exports = (function() {
options.lock = queryOptions.lock; options.lock = queryOptions.lock;
var sql = this.QueryGenerator.selectQuery(tableName, options, factory); var sql = this.QueryGenerator.selectQuery(tableName, options, model);
queryOptions = Utils._.extend({}, queryOptions, { queryOptions = Utils._.extend({}, queryOptions, {
include: options.include, include: options.include,
includeNames: options.includeNames, includeNames: options.includeNames,
...@@ -586,7 +586,7 @@ module.exports = (function() { ...@@ -586,7 +586,7 @@ module.exports = (function() {
originalAttributes: options.originalAttributes originalAttributes: options.originalAttributes
}); });
return this.sequelize.query(sql, factory, queryOptions); return this.sequelize.query(sql, model, queryOptions);
}; };
QueryInterface.prototype.increment = function(dao, tableName, values, identifier, options) { QueryInterface.prototype.increment = function(dao, tableName, values, identifier, options) {
......
...@@ -82,7 +82,15 @@ var Utils = module.exports = { ...@@ -82,7 +82,15 @@ var Utils = module.exports = {
var self = this; var self = this;
scope = scope || {}; scope = scope || {};
if (!this.scoped && self.options.defaultScope) {
self.scopeObj = Utils._.clone(self.options.defaultScope);
if (!Array.isArray(self.scopeObj.where)) {
self.scopeObj.where = [self.scopeObj.where];
}
} else {
self.scopeObj = self.scopeObj || {}; self.scopeObj = self.scopeObj || {};
}
if (Array.isArray(scope.where)) { if (Array.isArray(scope.where)) {
self.scopeObj.where = self.scopeObj.where || []; self.scopeObj.where = self.scopeObj.where || [];
......
"use strict";
/* jshint camelcase: false */ /* jshint camelcase: false */
/* jshint expr: true */ /* jshint expr: true */
var chai = require('chai') var chai = require('chai')
...@@ -5,13 +7,13 @@ var chai = require('chai') ...@@ -5,13 +7,13 @@ var chai = require('chai')
, expect = chai.expect , expect = chai.expect
, Support = require(__dirname + '/../support') , Support = require(__dirname + '/../support')
, DataTypes = require(__dirname + "/../../lib/data-types") , DataTypes = require(__dirname + "/../../lib/data-types")
, datetime = require('chai-datetime') , datetime = require('chai-datetime');
chai.use(datetime) chai.use(datetime);
chai.config.includeStack = true chai.config.includeStack = true;
describe(Support.getTestDialectTeaser("DAOFactory"), function () { describe(Support.getTestDialectTeaser("DAOFactory"), function () {
beforeEach(function(done) { beforeEach(function() {
this.User = this.sequelize.define('User', { this.User = this.sequelize.define('User', {
username: DataTypes.STRING, username: DataTypes.STRING,
secretValue: DataTypes.STRING, secretValue: DataTypes.STRING,
...@@ -19,15 +21,13 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -19,15 +21,13 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
intVal: DataTypes.INTEGER, intVal: DataTypes.INTEGER,
theDate: DataTypes.DATE, theDate: DataTypes.DATE,
aBool: DataTypes.BOOLEAN aBool: DataTypes.BOOLEAN
}) });
this.User.sync({ force: true }).success(function() { return this.User.sync({ force: true });
done() });
})
})
describe('scopes', function() { describe('scopes', function() {
beforeEach(function(done) { beforeEach(function() {
this.ScopeMe = this.sequelize.define('ScopeMe', { this.ScopeMe = this.sequelize.define('ScopeMe', {
username: Sequelize.STRING, username: Sequelize.STRING,
email: Sequelize.STRING, email: Sequelize.STRING,
...@@ -103,210 +103,222 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -103,210 +103,222 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
} }
} }
} }
}) });
this.sequelize.sync({force: true}).success(function() { return this.sequelize.sync({force: true}).then(function() {
var records = [ var records = [
{username: 'dan', email: 'dan@sequelizejs.com', access_level: 5, other_value: 10}, {username: 'dan', email: 'dan@sequelizejs.com', access_level: 5, other_value: 10},
{username: 'tobi', email: 'tobi@fakeemail.com', access_level: 10, other_value: 11}, {username: 'tobi', email: 'tobi@fakeemail.com', access_level: 10, other_value: 11},
{username: 'tony', email: 'tony@sequelizejs.com', access_level: 3, other_value: 7} {username: 'tony', email: 'tony@sequelizejs.com', access_level: 3, other_value: 7}
]; ];
this.ScopeMe.bulkCreate(records).success(function() { return this.ScopeMe.bulkCreate(records);
done() }.bind(this));
}) });
}.bind(this))
}) it("should have no problems with escaping SQL", function() {
var self = this;
it("should have no problems with escaping SQL", function(done) { return this.ScopeMe.create({username: 'escape\'d', email: 'fake@fakemail.com'}).then(function(){
var self = this return self.ScopeMe.scope('escape').all().then(function(users){
this.ScopeMe.create({username: 'escape\'d', email: 'fake@fakemail.com'}).success(function(){ expect(users).to.be.an.instanceof(Array);
self.ScopeMe.scope('escape').all().success(function(users){ expect(users.length).to.equal(1);
expect(users).to.be.an.instanceof(Array)
expect(users.length).to.equal(1)
expect(users[0].username).to.equal('escape\'d'); expect(users[0].username).to.equal('escape\'d');
done() });
}) });
}) });
})
it("should be able to use a defaultScope if declared", function() {
it("should be able to use a defaultScope if declared", function(done) { return this.ScopeMe.all().then(function(users) {
this.ScopeMe.all().success(function(users) { expect(users).to.be.an.instanceof(Array);
expect(users).to.be.an.instanceof(Array) expect(users.length).to.equal(2);
expect(users.length).to.equal(2) expect([10,5].indexOf(users[0].access_level) !== -1).to.be.true;
expect([10,5].indexOf(users[0].access_level) !== -1).to.be.true expect([10,5].indexOf(users[1].access_level) !== -1).to.be.true;
expect([10,5].indexOf(users[1].access_level) !== -1).to.be.true expect(['dan', 'tobi'].indexOf(users[0].username) !== -1).to.be.true;
expect(['dan', 'tobi'].indexOf(users[0].username) !== -1).to.be.true expect(['dan', 'tobi'].indexOf(users[1].username) !== -1).to.be.true;
expect(['dan', 'tobi'].indexOf(users[1].username) !== -1).to.be.true });
done() });
})
}) it("should be able to amend the default scope with a find object", function() {
return this.ScopeMe.findAll({where: {username: 'dan'}}).then(function(users) {
it("should be able to amend the default scope with a find object", function(done) { expect(users).to.be.an.instanceof(Array);
this.ScopeMe.findAll({where: {username: 'dan'}}).success(function(users) { expect(users.length).to.equal(1);
expect(users).to.be.an.instanceof(Array) expect(users[0].username).to.equal('dan');
expect(users.length).to.equal(1) });
expect(users[0].username).to.equal('dan') });
done()
}) it("should be able to override the default scope", function() {
}) return this.ScopeMe.scope('fakeEmail').findAll().then(function(users) {
expect(users).to.be.an.instanceof(Array);
it("should be able to override the default scope", function(done) { expect(users.length).to.equal(1);
this.ScopeMe.scope('fakeEmail').findAll().success(function(users) { expect(users[0].username).to.equal('tobi');
expect(users).to.be.an.instanceof(Array) });
expect(users.length).to.equal(1) });
expect(users[0].username).to.equal('tobi')
done() it("should be able to combine two scopes", function() {
}) return this.ScopeMe.scope(['sequelizeTeam', 'highValue']).findAll().then(function(users) {
}) expect(users).to.be.an.instanceof(Array);
expect(users.length).to.equal(1);
it("should be able to combine two scopes", function(done) { expect(users[0].username).to.equal('dan');
this.ScopeMe.scope(['sequelizeTeam', 'highValue']).findAll().success(function(users) { });
expect(users).to.be.an.instanceof(Array) });
expect(users.length).to.equal(1)
expect(users[0].username).to.equal('dan') it("should be able to call a scope that's a function", function() {
done() return this.ScopeMe.scope({method: ['actualValue', 11]}).findAll().then(function(users) {
}) expect(users).to.be.an.instanceof(Array);
}) expect(users.length).to.equal(1);
expect(users[0].username).to.equal('tobi');
it("should be able to call a scope that's a function", function(done) { });
this.ScopeMe.scope({method: ['actualValue', 11]}).findAll().success(function(users) { });
expect(users).to.be.an.instanceof(Array)
expect(users.length).to.equal(1) it("should be able to handle multiple function scopes", function() {
expect(users[0].username).to.equal('tobi') return this.ScopeMe.scope([{method: ['actualValue', 10]}, {method: ['complexFunction', 'dan', '5']}]).findAll().then(function(users) {
done() expect(users).to.be.an.instanceof(Array);
}) expect(users.length).to.equal(1);
}) expect(users[0].username).to.equal('dan');
});
it("should be able to handle multiple function scopes", function(done) { });
this.ScopeMe.scope([{method: ['actualValue', 10]}, {method: ['complexFunction', 'dan', '5']}]).findAll().success(function(users) {
expect(users).to.be.an.instanceof(Array) it("should be able to stack the same field in the where clause", function() {
expect(users.length).to.equal(1) return this.ScopeMe.scope(['canBeDan', 'canBeTony']).findAll().then(function(users) {
expect(users[0].username).to.equal('dan') expect(users).to.be.an.instanceof(Array);
done() expect(users.length).to.equal(2);
}) expect(['dan', 'tony'].indexOf(users[0].username) !== -1).to.be.true;
}) expect(['dan', 'tony'].indexOf(users[1].username) !== -1).to.be.true;
});
it("should be able to stack the same field in the where clause", function(done) { });
this.ScopeMe.scope(['canBeDan', 'canBeTony']).findAll().success(function(users) {
expect(users).to.be.an.instanceof(Array) it("should be able to merge scopes", function() {
expect(users.length).to.equal(2) return this.ScopeMe.scope(['highValue', 'isTony', {merge: true, method: ['actualValue', 7]}]).findAll().then(function(users) {
expect(['dan', 'tony'].indexOf(users[0].username) !== -1).to.be.true expect(users).to.be.an.instanceof(Array);
expect(['dan', 'tony'].indexOf(users[1].username) !== -1).to.be.true expect(users.length).to.equal(1);
done() expect(users[0].username).to.equal('tony');
}) });
}) });
it("should be able to merge scopes", function(done) { describe("should not overwrite", function () {
this.ScopeMe.scope(['highValue', 'isTony', {merge: true, method: ['actualValue', 7]}]).findAll().success(function(users) { it("default scope with values from previous finds", function () {
expect(users).to.be.an.instanceof(Array) return this.ScopeMe.findAll({ where: { other_value: 10 }}).bind(this).then(function (users) {
expect(users.length).to.equal(1) expect(users).to.have.length(1);
expect(users[0].username).to.equal('tony')
done() return this.ScopeMe.findAll();
}) }).then(function (users) {
}) // This should not have other_value: 10
expect(users).to.have.length(2);
it("should give us the correct order if we declare an order in our scope", function(done) { });
this.ScopeMe.scope('sequelizeTeam', 'orderScope').findAll().success(function(users) {
expect(users).to.be.an.instanceof(Array) });
expect(users.length).to.equal(2)
expect(users[0].username).to.equal('dan') it("other scopes with values from previous finds", function () {
expect(users[1].username).to.equal('tony') return this.ScopeMe.scope('highValue').findAll({ where: { access_level: 10 }}).bind(this).then(function (users) {
done() expect(users).to.have.length(1);
})
}) return this.ScopeMe.scope('highValue').findAll();
}).then(function (users) {
it("should give us the correct order as well as a limit if we declare such in our scope", function(done) { // This should not have other_value: 10
this.ScopeMe.scope(['orderScope', 'limitScope']).findAll().success(function(users) { expect(users).to.have.length(2);
expect(users).to.be.an.instanceof(Array) });
expect(users.length).to.equal(2) });
expect(users[0].username).to.equal('tobi')
expect(users[1].username).to.equal('dan') it("function scopes", function () {
done() return this.ScopeMe.scope({method: ['actualValue', 11]}).findAll().bind(this).then(function(users) {
}) expect(users).to.have.length(1);
}) expect(users[0].other_value).to.equal(11);
it("should have no problems combining scopes and traditional where object", function(done) { return this.ScopeMe.scope({method: ['actualValue', 10]}).findAll();
this.ScopeMe.scope('sequelizeTeam').findAll({where: {other_value: 10}}).success(function(users) { }).then(function(users) {
expect(users).to.be.an.instanceof(Array) expect(users).to.have.length(1);
expect(users.length).to.equal(1) expect(users[0].other_value).to.equal(10);
expect(users[0].username).to.equal('dan') });
expect(users[0].access_level).to.equal(5) });
expect(users[0].other_value).to.equal(10) });
done()
}) it("should give us the correct order if we declare an order in our scope", function() {
}) return this.ScopeMe.scope('sequelizeTeam', 'orderScope').findAll().then(function(users) {
expect(users).to.be.an.instanceof(Array);
it("should be able to remove all scopes", function(done) { expect(users.length).to.equal(2);
this.ScopeMe.scope(null).findAll().success(function(users) { expect(users[0].username).to.equal('dan');
expect(users).to.be.an.instanceof(Array) expect(users[1].username).to.equal('tony');
expect(users.length).to.equal(3) });
done() });
})
}) it("should give us the correct order as well as a limit if we declare such in our scope", function() {
return this.ScopeMe.scope(['orderScope', 'limitScope']).findAll().then(function(users) {
it("should have no problem performing findOrCreate", function(done) { expect(users).to.be.an.instanceof(Array);
this.ScopeMe.findOrCreate({username: 'fake'}).spread(function(user) { expect(users.length).to.equal(2);
expect(user.username).to.equal('fake') expect(users[0].username).to.equal('tobi');
done() expect(users[1].username).to.equal('dan');
}) });
}) });
it("should be able to hold multiple scope objects", function(done) { it("should have no problems combining scopes and traditional where object", function() {
return this.ScopeMe.scope('sequelizeTeam').findAll({where: {other_value: 10}}).then(function(users) {
expect(users).to.be.an.instanceof(Array);
expect(users.length).to.equal(1);
expect(users[0].username).to.equal('dan');
expect(users[0].access_level).to.equal(5);
expect(users[0].other_value).to.equal(10);
});
});
it("should be able to remove all scopes", function() {
return this.ScopeMe.scope(null).findAll().then(function(users) {
expect(users).to.be.an.instanceof(Array);
expect(users.length).to.equal(3);
});
});
it("should have no problem performing findOrCreate", function() {
return this.ScopeMe.findOrCreate({username: 'fake'}).spread(function(user) {
expect(user.username).to.equal('fake');
});
});
it("should be able to hold multiple scope objects", function() {
var sequelizeTeam = this.ScopeMe.scope('sequelizeTeam', 'orderScope') var sequelizeTeam = this.ScopeMe.scope('sequelizeTeam', 'orderScope')
, tobi = this.ScopeMe.scope({method: ['actualValue', 11]}) , tobi = this.ScopeMe.scope({method: ['actualValue', 11]});
sequelizeTeam.all().success(function(team) { return sequelizeTeam.all().then(function(team) {
tobi.all().success(function(t) { return tobi.all().then(function(t) {
expect(team).to.be.an.instanceof(Array) expect(team).to.be.an.instanceof(Array);
expect(team.length).to.equal(2) expect(team.length).to.equal(2);
expect(team[0].username).to.equal('dan') expect(team[0].username).to.equal('dan');
expect(team[1].username).to.equal('tony') expect(team[1].username).to.equal('tony');
expect(t).to.be.an.instanceof(Array) expect(t).to.be.an.instanceof(Array);
expect(t.length).to.equal(1) expect(t.length).to.equal(1);
expect(t[0].username).to.equal('tobi') expect(t[0].username).to.equal('tobi');
done() });
}) });
}) });
})
it("should gracefully omit any scopes that don't exist", function() {
it("should gracefully omit any scopes that don't exist", function(done) { return this.ScopeMe.scope('sequelizeTeam', 'orderScope', 'doesntexist').all().then(function(team) {
this.ScopeMe.scope('sequelizeTeam', 'orderScope', 'doesntexist').all().success(function(team) { expect(team).to.be.an.instanceof(Array);
expect(team).to.be.an.instanceof(Array) expect(team.length).to.equal(2);
expect(team.length).to.equal(2) expect(team[0].username).to.equal('dan');
expect(team[0].username).to.equal('dan') expect(team[1].username).to.equal('tony');
expect(team[1].username).to.equal('tony') });
done() });
})
}) it("should gracefully omit any scopes that don't exist through an array", function() {
return this.ScopeMe.scope(['sequelizeTeam', 'orderScope', 'doesntexist']).all().then(function(team) {
it("should gracefully omit any scopes that don't exist through an array", function(done) { expect(team).to.be.an.instanceof(Array);
this.ScopeMe.scope(['sequelizeTeam', 'orderScope', 'doesntexist']).all().success(function(team) { expect(team.length).to.equal(2);
expect(team).to.be.an.instanceof(Array) expect(team[0].username).to.equal('dan');
expect(team.length).to.equal(2) expect(team[1].username).to.equal('tony');
expect(team[0].username).to.equal('dan') });
expect(team[1].username).to.equal('tony') });
done()
}) it("should gracefully omit any scopes that don't exist through an object", function() {
}) return this.ScopeMe.scope('sequelizeTeam', 'orderScope', {method: 'doesntexist'}).all().then(function(team) {
expect(team).to.be.an.instanceof(Array);
it("should gracefully omit any scopes that don't exist through an object", function(done) { expect(team.length).to.equal(2);
this.ScopeMe.scope('sequelizeTeam', 'orderScope', {method: 'doesntexist'}).all().success(function(team) { expect(team[0].username).to.equal('dan');
expect(team).to.be.an.instanceof(Array) expect(team[1].username).to.equal('tony');
expect(team.length).to.equal(2) });
expect(team[0].username).to.equal('dan') });
expect(team[1].username).to.equal('tony')
done() it("should emit an error for scopes that don't exist with silent: false", function() {
}) expect(this.ScopeMe.scope.bind(this.ScopeMe, 'doesntexist', {silent: false})).to.throw('Invalid scope doesntexist called.');
}) });
});
it("should emit an error for scopes that don't exist with silent: false", function(done) { });
try {
this.ScopeMe.scope('doesntexist', {silent: false})
} catch (err) {
expect(err.message).to.equal('Invalid scope doesntexist called.')
done()
}
})
})
})
\ No newline at end of file
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!