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

Commit aaff7157 by Edgar Veiga

Merge from upstream.

2 parents 87a378d0 03d0fc39
Showing with 1565 additions and 689 deletions
[submodule "doc"]
path = doc
url = git://github.com/sdepold/sequelize-doc.git
......@@ -3,18 +3,22 @@ before_script:
- "psql -c 'create database sequelize_test;' -U postgres"
script:
- "npm test"
- "npm run test-buster-travis"
- "npm run test-jasmine"
notifications:
email:
- sascha@depold.com
env:
- DB=mysql
- DB=mysql DIALECT=mysql
- DB=mysql DIALECT=postgres
- DB=mysql DIALECT=postgres-native
- DB=mysql DIALECT=sqlite
language: node_js
node_js:
- 0.6
- 0.8
- 0.9
......@@ -30,31 +30,33 @@ Also make sure to take a look at the examples in the repository. The website wil
- [IRC](irc://irc.freenode.net/sequelizejs)
- [XING](https://www.xing.com/net/priec1b5cx/sequelize)
## Collaboration ##
## Collaboration 2.0 ##
I'm glad to get pull request if any functionality is missing or something is buggy. But _please_ ... run the tests before you send me the pull request.
Now if you want to contribute but don't really know where to begin
don't worry, the steps below will guide you to have a sequelize
contributor's environment running in a couple minutes.
Still interested? Coolio! Here is how to get started:
### 1. Prepare the environment ###
### 1. Prepare your environment ###
All the following steps consider you already have [npm](http://npmjs.org/) installed in your [node.js version 0.4.6 or higher](https://github.com/sdepold/sequelize/blob/master/package.json#L30)
Here comes a little surprise: You need [Node.JS](http://nodejs.org). In order to be
a productive developer, I would recommend the latest v0.8 (or a stable 0.9 if
already out). Also I usually recommend [NVM](https://github.com/creationix/nvm).
#### 1.1 MySQL and other external dependencies ####
Once Node.JS is installed on your computer, you will also have access to the lovely
Node Package Manager (NPM).
Contributing to sequelize requires you to have
[MySQL](http://www.mysql.com/) up and running in your local
environment. The reason for that is that we have test cases that runs
against an actual MySQL server and make sure everything is always
working.
### 2. Database... Come to me! ###
That is also one of the reasons your features must come with tests:
let's make sure sequelize will stay awesome as more features are added
as well as that fixed bugs will never come back.
First class citizen of Sequelize was MySQL. Over time, Sequelize began to
become compatible to SQLite and PostgreSQL. In order to provide a fully
featured pull request, you would most likely want to install of them. Give
it a try, it's not that hard.
Well, after installing **MySQL** you also need to create the sequelize test database:
If you are too lazy or just don't know how to get this work,
feel free to join the IRC channel (freenode@#sequelizejs).
For MySQL and PostgreSQL you'll need to create a DB called `sequelize_test`.
For MySQL this would look like this:
```console
$ echo "CREATE DATABASE sequelize_test;" | mysql -uroot
......@@ -65,10 +67,11 @@ $ echo "CREATE DATABASE sequelize_test;" | mysql -uroot
tests, but make sure to don't commit your credentials, we don't want
to expose your personal data in sequelize codebase ;)
**AND ONE LAST THING:** Sequelize also supports SQLite. So this should be working
on your machine as well :)
**AND ONE LAST THING:** Once `npm install` worked for you (see below), you'll
get SQLite tests for free :)
### 2. Install the dependencies ###
### 3. Install the dependencies ###
Just "cd" into sequelize directory and run `npm install`, see an example below:
......@@ -77,25 +80,44 @@ $ cd path/to/sequelize
$ npm install
```
### 3. Run the tests ###
### 4. Run the tests ###
In order to run the tests you got to run `jasmine-node` against the `spec` directory.
By the way, [there](https://github.com/sdepold/sequelize/tree/master/spec) is where
you will write new tests if that's the case.
Right now, the test base is split into the `spec` folder (which contains the
lovely [BusterJS](http://busterjs.org) tests) and the `spec-jasmine` folder
(which contains the ugly and awkward node-jasmine based tests). A main goal
is to get rid of the jasmine tests!
All you need is to run `./node_modules/.bin/jasmine-node spec/`,
although this is kinda long and boring, so we configures a NPM task
and made that less laborious to you :)
As you might haven't installed all of the supported SQL dialects, here is how
to run the test suites for your development environment:
```console
$ # run all tests at once:
$ npm test
$ # run only the jasmine tests (for all dialects):
$ npm run test-jasmine
$ # run all of the buster specs (for all dialects):
$ npm run test-buster
$ # run the buster specs for mysql:
$ npm run test-buster-mysql
$ # run the buster specs for sqlite:
$ npm run test-buster-sqlite
$ # run the buster specs for postgresql:
$ npm run test-buster-postgres
```
### 4. That's all ###
### 5. That's all ###
Just commit and send pull requests.
Happy hacking and thank you for contributing.
Ah and one last thing: If you think you deserve it, feel free to add yourself to the
`package.json`. Also I always look for projects which are using sequelize. If you have
one of them, drop me a line!
Happy hacking and thank you for contributing
# Build status
......
......@@ -9,9 +9,11 @@ const path = require("path")
var configPath = process.cwd() + '/config'
, migrationsPath = process.cwd() + '/migrations'
, packageJsonPath = __dirname + '/../package.json'
, packageJson = JSON.parse(fs.readFileSync(packageJsonPath).toString())
, configFile = configPath + '/config.json'
, configPathExists = path.existsSync(configPath)
, configFileExists = path.existsSync(configFile)
, configPathExists = fs.existsSync(configPath)
, configFileExists = fs.existsSync(configFile)
var writeConfig = function(config) {
!configPathExists && fs.mkdirSync(configPath)
......@@ -49,14 +51,21 @@ var createMigrationsFolder = function(force) {
var readConfig = function() {
try {
return JSON.parse(fs.readFileSync(configFile))
var config = JSON.parse(fs.readFileSync(configFile))
, env = process.env.NODE_ENV || 'development'
if (config[env]) {
config = config[env]
}
return config
} catch(e) {
throw new Error('The config.json is not available or contains invalid JSON.')
}
}
program
.version('1.3.0')
.version(packageJson.version)
.option('-i, --init', 'Initializes the project. Creates a config/config.json')
.option('-m, --migrate', 'Runs undone migrations')
.option('-u, --undo', 'Redo the last migration.')
......@@ -100,10 +109,24 @@ if(program.migrate) {
} else if(program.init) {
if(!configFileExists || !!program.force) {
writeConfig({
username: "root",
password: null,
database: 'database',
host: '127.0.0.1'
development: {
username: "root",
password: null,
database: 'database_development',
host: '127.0.0.1'
},
test: {
username: "root",
password: null,
database: 'database_test',
host: '127.0.0.1'
},
production: {
username: "root",
password: null,
database: 'database_production',
host: '127.0.0.1'
}
})
console.log('Successfully created config.json')
......
# v1.6.0 #
- [DEPENDENCIES] upgraded most dependencies. most important: mysql was upgraded to 2.0.0-alpha-3
- [REFACTORING] separated tests for dialects
- [BUG] fixed wrong version in sequelize binary
- [BUG] local options have higher priority than global options (thanks to guersam)
- [BUG] fixed where clause when passing an empty array (thanks to kbackowski)
- [FEATURE] added association prefetching for find and findAll
- [FEATURE] it's now possible to use callbacks of async functions inside migrations (thanks to mphilpot)
- [FEATURE] improved comfort of sequelize.query. just pass an sql string to it and wait for the result
- [FEATURE] Migrations now understand NODE_ENV (thanks to gavri)
- [FEATURE] Performance improvements (thanks to Mick-Hansen and janmeier from innofluence)
- [FEATURE] Model.find and Model.findAll can now take a String with an ID. (thanks to ghernandez345)
- [FEATURE] Compatibility for JSON-like strings in Postgres (thanks to aslakhellesoy)
- [FEATURE] honor maxConcurrentQueries option (thanks to dchester)
- [FEATURE] added support for stored procedures (inspired by wuyuntao)
- [FEATURE] added possibility to use pg lib's native api (thanks to denysonique)
- [FEATURE] added possibility to define the attributes of received associations (thanks to joshm)
# v1.5.0 #
- [REFACTORING] use underscore functions for Utils.isHash (thanks to Mick-Hansen/innofluence)
- [REFACTORING] removed the 'failure' event and replaced it with 'error'
......@@ -5,6 +23,7 @@
- [BUG] obsolete reference attribute for many-to-many associations are removed correctly
- [BUG] associations can be cleared via passing null to the set method
- [BUG] "fixed" quota handling (thanks to dgf)
- [BUG] fixed destroy in postgresql (thanks to robraux)
- [FEATURE] added possibility to set protocol and to remove port from postgresql connection uri (thanks to danielschwartz)
- [FEATURE] added possibility to not use a junction table for many-to-many associations on the same table (thanks to janmeier/innofluence)
- [FEATURE] results of the `import` method is now cached (thanks to janmeier/innofluence)
......
Subproject commit 3c0dc39aeada08622898b0c231a86114319a09dd
......@@ -3,13 +3,15 @@ var Utils = require("./../utils")
module.exports = (function() {
var BelongsTo = function(srcDAO, targetDAO, options) {
this.source = srcDAO
this.target = targetDAO
this.options = options
this.associationType = 'BelongsTo'
this.source = srcDAO
this.target = targetDAO
this.options = options
this.isSelfAssociation = (this.source.tableName == this.target.tableName)
if(this.isSelfAssociation && !this.options.foreignKey && !!this.options.as)
if(this.isSelfAssociation && !this.options.foreignKey && !!this.options.as) {
this.options.foreignKey = Utils._.underscoredIf(Utils.singularize(this.options.as) + "Id", this.source.options.underscored)
}
this.associationAccessor = this.isSelfAssociation
? Utils.combineTableNames(this.target.tableName, this.options.as || this.target.tableName)
......@@ -22,7 +24,11 @@ module.exports = (function() {
this.identifier = this.options.foreignKey || Utils._.underscoredIf(Utils.singularize(this.target.tableName) + "Id", this.source.options.underscored)
newAttributes[this.identifier] = { type: DataTypes.INTEGER }
Utils._.extend(this.source.rawAttributes, newAttributes)
Utils._.defaults(this.source.rawAttributes, newAttributes)
// Sync attributes to DAO proto each time a new assoc is added
this.target.DAO.prototype.attributes = Object.keys(this.target.DAO.prototype.rawAttributes);
return this
}
......@@ -30,9 +36,18 @@ module.exports = (function() {
var self = this
, accessor = Utils._.camelize('get_' + (this.options.as || Utils.singularize(this.target.tableName)))
obj[accessor] = function() {
var id = obj[self.identifier]
return self.target.find(id)
obj[accessor] = function(params) {
var id = this[self.identifier]
if (!Utils._.isUndefined(params)) {
if (!Utils._.isUndefined(params.attributes)) {
params = Utils._.extend({where: {id:id}}, params)
}
} else {
params = id
}
return self.target.find(params)
}
return this
......@@ -43,10 +58,10 @@ module.exports = (function() {
, accessor = Utils._.camelize('set_' + (this.options.as || Utils.singularize(this.target.tableName)))
obj[accessor] = function(associatedObject) {
obj[self.identifier] = associatedObject ? associatedObject.id : null
this[self.identifier] = associatedObject ? associatedObject.id : null
// passes the changed field to save, so only that field get updated.
return obj.save([ self.identifier ])
return this.save([ self.identifier ])
}
return this
......
......@@ -8,7 +8,7 @@ module.exports = (function() {
HasManyDoubleLinked.prototype.injectGetter = function(options) {
var self = this, _options = options
var customEventEmitter = new Utils.CustomEventEmitter(function() {
var where = {}, options = _options || {};
......@@ -26,15 +26,15 @@ module.exports = (function() {
options.where[self.__factory.target.tableName+"."+index] = value;
});
options.where = options.where ? Utils.merge(options.where, where) : where
Utils._.extend(options.where, where)
} else {
options.where = where;
}
self.__factory.target.findAllJoin(self.__factory.connectorDAO.tableName, options)
.on('success', function(objects) { customEventEmitter.emit('success', objects) })
.on('error', function(err){ customEventEmitter.emit('error', err) })
.on('sql', function(sql) { customEventEmitter.emit('sql', sql)})
.on('success', function(objects) { customEventEmitter.emit('success', objects) })
.on('error', function(err){ customEventEmitter.emit('error', err) })
.on('sql', function(sql) { customEventEmitter.emit('sql', sql)})
})
return customEventEmitter.run()
......
......@@ -11,23 +11,22 @@ module.exports = (function() {
where[this.__factory.identifier] = this.instance.id
options.where = options.where ? Utils.merge(options.where, where) : where
options.where = options.where ? Utils._.extend(options.where, where) : where
return this.__factory.target.findAll(options)
}
HasManySingleLinked.prototype.injectSetter = function(emitter, oldAssociations, newAssociations) {
var self = this
, options = this.__factory.options
, chainer = new Utils.QueryChainer()
// clear the old associations
oldAssociations.forEach(function(associatedObject) {
associatedObject[self.__factory.identifier] = options.omitNull ? '' : null
associatedObject.save()
associatedObject[self.__factory.identifier] = null
chainer.add(associatedObject.save())
})
// set the new one
var chainer = new Utils.QueryChainer()
// set the new associations
newAssociations.forEach(function(associatedObject) {
associatedObject[self.__factory.identifier] = self.instance.id
chainer.add(associatedObject.save())
......
......@@ -6,6 +6,7 @@ var HasManySingleLinked = require("./has-many-single-linked")
module.exports = (function() {
var HasMany = function(srcDAO, targetDAO, options) {
this.associationType = 'HasMany'
this.source = srcDAO
this.target = targetDAO
this.options = options
......@@ -53,6 +54,7 @@ module.exports = (function() {
combinedTableAttributes[this.foreignIdentifier] = {type:DataTypes.INTEGER, primaryKey: true}
this.connectorDAO = this.source.daoFactoryManager.sequelize.define(this.combinedName, combinedTableAttributes, this.options)
if(!this.isSelfAssociation) {
this.target.associations[this.associationAccessor].connectorDAO = this.connectorDAO
}
......@@ -63,9 +65,13 @@ module.exports = (function() {
} else {
var newAttributes = {}
newAttributes[this.identifier] = { type: DataTypes.INTEGER }
Utils._.extend(this.target.rawAttributes, newAttributes)
Utils._.defaults(this.target.rawAttributes, newAttributes)
}
// Sync attributes to DAO proto each time a new assoc is added
this.target.DAO.prototype.attributes = Object.keys(this.target.DAO.prototype.rawAttributes);
this.source.DAO.prototype.attributes = Object.keys(this.source.DAO.prototype.rawAttributes);
return this
}
......
......@@ -3,13 +3,15 @@ var Utils = require("./../utils")
module.exports = (function() {
var HasOne = function(srcDAO, targetDAO, options) {
this.source = srcDAO
this.target = targetDAO
this.options = options
this.associationType = 'HasOne'
this.source = srcDAO
this.target = targetDAO
this.options = options
this.isSelfAssociation = (this.source.tableName == this.target.tableName)
if(this.isSelfAssociation && !this.options.foreignKey && !!this.options.as)
if(this.isSelfAssociation && !this.options.foreignKey && !!this.options.as) {
this.options.foreignKey = Utils._.underscoredIf(Utils.singularize(this.options.as) + "Id", this.options.underscored)
}
this.associationAccessor = this.isSelfAssociation
? Utils.combineTableNames(this.target.tableName, this.options.as || this.target.tableName)
......@@ -27,7 +29,10 @@ module.exports = (function() {
this.identifier = this.options.foreignKey || Utils._.underscoredIf(Utils.singularize(this.source.tableName) + "Id", this.options.underscored)
newAttributes[this.identifier] = { type: DataTypes.INTEGER }
Utils._.extend(this.target.rawAttributes, newAttributes)
Utils._.defaults(this.target.rawAttributes, newAttributes)
// Sync attributes to DAO proto each time a new assoc is added
this.target.DAO.prototype.attributes = Object.keys(this.target.DAO.prototype.rawAttributes);
return this
}
......@@ -35,12 +40,21 @@ module.exports = (function() {
HasOne.prototype.injectGetter = function(obj) {
var self = this
obj[this.accessors.get] = function() {
var id = obj.id
obj[this.accessors.get] = function(params) {
var id = this.id
, where = {}
where[self.identifier] = id
return self.target.find({where: where})
if (!Utils._.isUndefined(params)) {
if (!Utils._.isUndefined(params.attributes)) {
params = Utils._.extend({where: where}, params)
}
} else {
params = {where: where}
}
return self.target.find(params)
}
return this
......@@ -51,15 +65,16 @@ module.exports = (function() {
, options = self.options || {}
obj[this.accessors.set] = function(associatedObject) {
var instance = this;
return new Utils.CustomEventEmitter(function(emitter) {
obj[self.accessors.get]().success(function(oldObj) {
instance[self.accessors.get]().success(function(oldObj) {
if(oldObj) {
oldObj[self.identifier] = options.omitNull ? '' : null;
oldObj[self.identifier] = null
oldObj.save()
}
if(associatedObject) {
associatedObject[self.identifier] = obj.id
associatedObject[self.identifier] = instance.id
associatedObject
.save()
.success(function() { emitter.emit('success', associatedObject) })
......
......@@ -10,21 +10,49 @@ Mixin.hasOne = function(associatedDAO, options) {
// the id is in the foreign table
var association = new HasOne(this, associatedDAO, Utils._.extend((options||{}), this.options))
this.associations[association.associationAccessor] = association.injectAttributes()
association.injectGetter(this.DAO.prototype);
association.injectSetter(this.DAO.prototype);
return this
}
Mixin.belongsTo = function(associatedDAO, options) {
// the id is in this table
var association = new BelongsTo(this, associatedDAO, Utils._.extend((options||{}), this.options))
this.associations[association.associationAccessor] = association.injectAttributes()
association.injectGetter(this.DAO.prototype);
association.injectSetter(this.DAO.prototype);
return this
}
Mixin.hasMany = function(associatedDAO, options) {
// the id is in the foreign table or in a connecting table
var association = new HasMany(this, associatedDAO, Utils._.extend((options||{}), this.options))
this.associations[association.associationAccessor] = association.injectAttributes()
association.injectGetter(this.DAO.prototype);
association.injectSetter(this.DAO.prototype);
return this
}
Mixin.getAssociation = function(target, options) {
var result = null
for (var associationName in this.associations) {
var association = this.associations[associationName]
if (!result && (association.target === target)) {
result = association
}
}
return result
}
/* example for instance methods:
Mixin.prototype.test = function() {
console.log('asd')
......
......@@ -16,9 +16,12 @@ module.exports = (function() {
})
}
DAOFactoryManager.prototype.getDAO = function(daoName) {
DAOFactoryManager.prototype.getDAO = function(daoName, options) {
options = options || {}
options.attribute = options.attribute || 'name'
var dao = this.daos.filter(function(dao) {
return dao.name == daoName
return dao[options.attribute] === daoName
})
return !!dao ? dao[0] : null
......
var Utils = require("./utils")
, DAO = require("./dao")
, DataTypes = require("./data-types")
, Util = require('util')
module.exports = (function() {
var DAOFactory = function(name, attributes, options) {
......@@ -41,26 +42,65 @@ module.exports = (function() {
get: function() { return this.QueryInterface.QueryGenerator }
})
Object.defineProperty(DAOFactory.prototype, 'primaryKeyCount', {
get: function() { return Utils._.keys(this.primaryKeys).length }
})
Object.defineProperty(DAOFactory.prototype, 'hasPrimaryKeys', {
get: function() { return this.primaryKeyCount > 0 }
})
DAOFactory.prototype.init = function(daoFactoryManager) {
var self = this;
this.daoFactoryManager = daoFactoryManager
this.primaryKeys = {};
Utils._.each(this.attributes, function(dataTypeString, attributeName) {
if((attributeName != 'id') && (dataTypeString.indexOf('PRIMARY KEY') !== -1)) {
self.primaryKeys[attributeName] = dataTypeString
}
})
this.primaryKeyCount = Utils._.keys(this.primaryKeys).length;
this.options.hasPrimaryKeys = this.hasPrimaryKeys = this.primaryKeyCount > 0;
addDefaultAttributes.call(this)
addOptionalClassMethods.call(this)
findAutoIncrementField.call(this)
// DAO prototype
this.DAO = function() {
DAO.apply(this, arguments);
};
Util.inherits(this.DAO, DAO);
this.DAO.prototype.rawAttributes = this.rawAttributes;
if (this.options.instanceMethods) {
Utils._.each(this.options.instanceMethods, function(fct, name) {
self.DAO.prototype[name] = fct
})
}
this.DAO.prototype.attributes = Object.keys(this.DAO.prototype.rawAttributes);
this.DAO.prototype.booleanValues = [];
this.DAO.prototype.defaultValues = {};
this.DAO.prototype.validators = {};
Utils._.each(this.rawAttributes, function (definition, name) {
if(((definition === DataTypes.BOOLEAN) || (definition.type === DataTypes.BOOLEAN))) {
self.DAO.prototype.booleanValues.push(name);
}
if(definition.hasOwnProperty('defaultValue')) {
self.DAO.prototype.defaultValues[name] = function() {
return Utils.toDefaultValue(definition.defaultValue);
}
}
if (definition.hasOwnProperty('validate')) {
self.DAO.prototype.validators[name] = definition.validate;
}
});
this.DAO.prototype.__factory = this;
this.DAO.prototype.hasDefaultValues = !Utils._.isEmpty(this.DAO.prototype.defaultValues);
return this
}
DAOFactory.prototype.sync = function(options) {
options = Utils.merge(options || {}, this.options)
options = Utils._.extend({}, this.options, options || {})
var self = this
return new Utils.CustomEventEmitter(function(emitter) {
......@@ -90,47 +130,84 @@ module.exports = (function() {
}
DAOFactory.prototype.findAll = function(options) {
return this.QueryInterface.select(this, this.tableName, options)
var hasJoin = false;
if ((typeof options === 'object') && (options.hasOwnProperty('include'))) {
var includes = options.include
hasJoin = true;
options.include = {}
includes.forEach(function(daoName) {
options.include[daoName] = this.daoFactoryManager.getDAO(daoName)
}.bind(this))
}
return this.QueryInterface.select(this, this.tableName, options, { type: 'SELECT', hasJoin: hasJoin })
}
//right now, the caller (has-many-double-linked) is in charge of the where clause
DAOFactory.prototype.findAllJoin = function(joinTableName, options) {
optcpy = Utils._.clone(options)
var optcpy = Utils._.clone(options)
optcpy.attributes = optcpy.attributes || [Utils.addTicks(this.tableName)+".*"]
return this.QueryInterface.select(this, [this.tableName, joinTableName], optcpy)
return this.QueryInterface.select(this, [this.tableName, joinTableName], optcpy, { type: 'SELECT' })
}
DAOFactory.prototype.find = function(options) {
if([null, undefined].indexOf(options) > -1) {
var hasJoin = false;
// no options defined?
// return an emitter which emits null
if([null, undefined].indexOf(options) !== -1) {
return new Utils.CustomEventEmitter(function(emitter) {
setTimeout(function() { emitter.emit('success', null) }, 10)
}).run()
}
var primaryKeys = this.primaryKeys;
// options is not a hash but an id
if(typeof options == 'number')
if(typeof options === 'number') {
options = { where: options }
else if (Utils.argsArePrimaryKeys(arguments, this.primaryKeys)) {
var where = {}
, self = this
} else if (Utils._.size(primaryKeys) && Utils.argsArePrimaryKeys(arguments, primaryKeys)) {
var where = {}
, self = this
, keys = Utils._.keys(primaryKeys)
Utils._.each(arguments, function(arg, i) {
var key = keys[i]
where[key] = arg
})
options = { where: where }
} else if ((typeof options === 'string') && (parseInt(options, 10).toString() === options)) {
var parsedId = parseInt(options, 10);
if(!Utils._.isFinite(parsedId)) {
throw new Error('Invalid argument to find(). Must be an id or an options object.')
}
Utils._.each(arguments, function(arg, i) {
var key = Utils._.keys(self.primaryKeys)[i]
where[key] = arg
})
options = { where: parsedId }
} else if ((typeof options === 'object') && (options.hasOwnProperty('include'))) {
var includes = options.include
hasJoin = true;
options = { where: where }
options.include = {}
includes.forEach(function(daoName) {
options.include[daoName] = this.daoFactoryManager.getDAO(daoName)
}.bind(this))
}
options.limit = 1
return this.QueryInterface.select(this, this.tableName, options, {plain: true})
return this.QueryInterface.select(this, this.tableName, options, { plain: true, type: 'SELECT', hasJoin: hasJoin })
}
DAOFactory.prototype.count = function(options) {
options = Utils._.extend({ attributes: [] }, options || {})
options.attributes.push(['count(*)', 'count'])
options.parseInt = true
return this.QueryInterface.rawSelect(this.tableName, options, 'count')
}
......@@ -138,52 +215,23 @@ module.exports = (function() {
DAOFactory.prototype.max = function(field, options) {
options = Utils._.extend({ attributes: [] }, options || {})
options.attributes.push(['max(' + field + ')', 'max'])
options.parseInt = true
return this.QueryInterface.rawSelect(this.tableName, options, 'max')
}
DAOFactory.prototype.min = function(field, options) {
options = Utils._.extend({ attributes: [] }, options || {})
options.attributes.push(['min(' + field + ')', 'min'])
options.parseInt = true
return this.QueryInterface.rawSelect(this.tableName, options, 'min')
}
DAOFactory.prototype.build = function(values, options) {
var instance = new DAO(values, Utils._.extend(this.options, this.attributes, { hasPrimaryKeys: this.hasPrimaryKeys }))
, self = this
options = options || {}
instance.__factory = this
Utils._.each(this.attributes, function(definition, name) {
//transform integer 0,1 into boolean
if((definition.indexOf(DataTypes.BOOLEAN) !== -1) && (typeof instance[name] === "number")) {
instance[name] = (instance[name] !== 0)
}
//add default attributes
if(typeof instance[name] === 'undefined') {
var value = null
if(self.rawAttributes.hasOwnProperty(name) && self.rawAttributes[name].hasOwnProperty('defaultValue')) {
value = Utils.toDefaultValue(self.rawAttributes[name].defaultValue)
}
instance[name] = value
instance.addAttribute(name, value)
}
// add validation
if (self.rawAttributes.hasOwnProperty(name) && self.rawAttributes[name].hasOwnProperty('validate')) {
instance.setValidators(name, self.rawAttributes[name].validate)
}
})
Utils._.each(this.options.instanceMethods || {}, function(fct, name) { instance[name] = fct })
Utils._.each(this.associations, function(association, associationName) {
association.injectGetter(instance)
association.injectSetter(instance)
})
var self = this
, instance = new this.DAO(values, this.options)
instance.isNewRecord = options.hasOwnProperty('isNewRecord') ? options.isNewRecord : true
......@@ -222,25 +270,23 @@ module.exports = (function() {
}).run()
}
DAOFactory.prototype.__defineGetter__('primaryKeys', function() {
var result = {}
Utils._.each(this.attributes, function(dataTypeString, attributeName) {
if((attributeName != 'id') && (dataTypeString.indexOf('PRIMARY KEY') > -1))
result[attributeName] = dataTypeString
})
return result
})
// private
var query = function() {
var args = Utils._.map(arguments, function(arg, _) { return arg })
, s = this.daoFactoryManager.sequelize
var args = Utils._.map(arguments, function(arg, _) { return arg })
, sequelize = this.daoFactoryManager.sequelize
// add this as the second argument
if(arguments.length == 1) args.push(this)
return s.query.apply(s, args)
if (arguments.length === 1) {
args.push(this)
}
// add {} as options
if (args.length === 2) {
args.push({})
}
return sequelize.query.apply(sequelize, args)
}
var addOptionalClassMethods = function() {
......@@ -275,17 +321,17 @@ module.exports = (function() {
}
var findAutoIncrementField = function() {
var self = this
, fields = this.QueryGenerator.findAutoIncrementField(this)
var fields = this.QueryGenerator.findAutoIncrementField(this)
this.autoIncrementField = null
fields.forEach(function(field) {
if(self.autoIncrementField)
if (this.autoIncrementField) {
throw new Error('Invalid DAO definition. Only one autoincrement field allowed.')
else
self.autoIncrementField = field
})
} else {
this.autoIncrementField = field
}
}.bind(this))
}
Utils._.extend(DAOFactory.prototype, require("./associations/mixin"))
......
var Utils = require("./utils")
, Mixin = require("./associations/mixin")
, DataTypes = require("./data-types")
, Validator = require("validator")
, DataTypes = require("./data-types")
module.exports = (function() {
var DAO = function(values, options) {
var self = this
this.attributes = []
this.validators = {} // holds validation settings for each attribute
this.__factory = null // will be set in DAO.build
this.__options = Utils._.extend({
underscored: false,
hasPrimaryKeys: false,
timestamps: true,
paranoid: false
}, options || {})
var self = this;
this.__options = options;
this.hasPrimaryKeys = options.hasPrimaryKeys;
this.selectedValues = values;
initAttributes.call(this, values)
if (this.hasDefaultValues) {
Utils._.each(this.defaultValues, function (value, name) {
if(typeof self[name] === 'undefined') {
self.addAttribute(name, value());
}
})
}
if (this.booleanValues.length) {
this.booleanValues.forEach(function (name) {
//transform integer 0,1 into boolean
self[name] = !!self[name];
});
}
}
Utils._.extend(DAO.prototype, Mixin.prototype)
......@@ -32,7 +40,7 @@ module.exports = (function() {
Object.defineProperty(DAO.prototype, 'isDeleted', {
get: function() {
var result = this.__options.timestamps && this.__options.paranoid
result = result && this[this.__options.underscored ? 'deleted_at' : 'deletedAt'] != null
result = result && this[this.__options.underscored ? 'deleted_at' : 'deletedAt'] !== null
return result
}
......@@ -89,29 +97,25 @@ module.exports = (function() {
, updatedAtAttr = this.__options.underscored ? 'updated_at' : 'updatedAt'
, createdAtAttr = this.__options.underscored ? 'created_at' : 'createdAt'
if(fields) {
if(self.__options.timestamps) {
if(fields.indexOf(updatedAtAttr) === -1) {
if (fields) {
if (self.__options.timestamps) {
if (fields.indexOf(updatedAtAttr) === -1) {
fields.push(updatedAtAttr)
}
if(fields.indexOf(createdAtAttr) === -1) {
if (fields.indexOf(createdAtAttr) === -1) {
fields.push(createdAtAttr)
}
}
fields.forEach(function(field) {
if(self.values[field] !== undefined) {
if (self.values[field] !== undefined) {
values[field] = self.values[field]
}
})
}
if(this.__options.timestamps && this.hasOwnProperty(updatedAtAttr)) {
var now = new Date()
this[updatedAtAttr] = now
values[updatedAtAttr] = now
this[updatedAtAttr] = values[updatedAtAttr] = new Date()
}
if(this.isNewRecord) {
......@@ -155,7 +159,7 @@ module.exports = (function() {
// is it a validator module function?
else {
// extra args
fn_args = details.hasOwnProperty("args") ? details.args : []
fn_args = details.hasOwnProperty("args") ? details.args : details
if (!Utils._.isArray(fn_args))
fn_args = [fn_args]
// error msg
......@@ -249,7 +253,6 @@ module.exports = (function() {
DAO.prototype.addAttribute = function(attribute, value) {
this[attribute] = value
this.attributes.push(attribute)
}
DAO.prototype.setValidators = function(attribute, validators) {
......@@ -263,14 +266,16 @@ module.exports = (function() {
// private
var initAttributes = function(values) {
var self = this
// add all passed values to the dao and store the attribute names in this.attributes
Utils._.map(values, function(value, key) { self.addAttribute(key, value) })
for (var key in values) {
if (values.hasOwnProperty(key)) {
this.addAttribute(key, values[key])
}
}
// set id to null if not passed as value
// a newly created dao has no id
var defaults = this.__options.hasPrimaryKeys ? {} : { id: null }
var defaults = this.hasPrimaryKeys ? {} : { id: null }
if(this.__options.timestamps) {
defaults[this.__options.underscored ? 'created_at' : 'createdAt'] = new Date()
......@@ -281,15 +286,16 @@ module.exports = (function() {
}
}
Utils._.map(defaults, function(value, attr) {
if(!self.hasOwnProperty(attr)) {
self.addAttribute(attr, Utils.toDefaultValue(value))
if (Utils._.size(defaults)) {
for (var attr in defaults) {
var value = defaults[attr]
if(!this.hasOwnProperty(attr)) {
this.addAttribute(attr, Utils.toDefaultValue(value))
}
}
})
}
}
/* Add the instance methods to DAO */
Utils._.extend(DAO.prototype, Mixin.prototype)
return DAO
})()
var Pooling = require('generic-pool')
var mysql = require("mysql")
, Pooling = require('generic-pool')
, Query = require("./query")
, Utils = require("../../utils")
, without = function(arr, elem) { return arr.filter(function(e) { return e != elem }) }
......@@ -13,7 +14,11 @@ module.exports = (function() {
this.activeQueue = []
this.maxConcurrentQueries = (this.config.maxConcurrentQueries || 50)
this.poolCfg = this.config.pool
this.pendingQueries = 0;
this.useQueue = config.queue !== undefined ? config.queue : true;
var self = this
if (this.poolCfg) {
//the user has requested pooling, so create our connection pool
if (!this.poolCfg.maxConnections) {
......@@ -31,6 +36,7 @@ module.exports = (function() {
idleTimeoutMillis: self.poolCfg.maxIdleTime
})
}
process.on('exit', function () {
//be nice & close our connections on exit
if (self.pool) {
......@@ -42,49 +48,80 @@ module.exports = (function() {
return
})
}
Utils._.extend(ConnectorManager.prototype, require("../connector-manager").prototype)
var isConnecting = false
Utils._.extend(ConnectorManager.prototype, require("../connector-manager").prototype);
var isConnecting = false;
ConnectorManager.prototype.query = function(sql, callee, options) {
if (!this.isConnected && !this.pool) this.connect();
if(!this.isConnected && !this.pool) this.connect()
if (this.useQueue) {
var queueItem = {
query: new Query(this.client, this.sequelize, callee, options || {}),
sql: sql
};
var queueItem = {
query: new Query(this.client, callee, options || {}),
sql: sql
enqueue.call(this, queueItem);
return queueItem.query;
}
enqueue.call(this, queueItem)
var self = this, query = new Query(this.client, this.sequelize, callee, options || {});
this.pendingQueries++;
query.done(function() {
self.pendingQueries--;
if (self.pool) self.pool.release(query.client);
else {
if (self.pendingQueries === 0) {
setTimeout(function() {
self.pendingQueries === 0 && self.disconnect.call(self);
}, 100);
}
}
});
return queueItem.query
}
if (!this.pool) query.run(sql);
else {
this.pool.acquire(function(err, client) {
if (err) return query.emit('error', err);
query.client = client;
query.run(sql);
return;
});
}
return query;
};
ConnectorManager.prototype.connect = function() {
var self = this
var self = this;
// in case database is slow to connect, prevent orphaning the client
if (this.isConnecting || this.pool) {
return
return;
}
connect.call(self, function(err, client) {
self.client = client
return
})
return
}
self.client = client;
return;
});
return;
};
ConnectorManager.prototype.disconnect = function() {
if (this.client)
disconnect.call(this, this.client)
return
}
if (this.client) disconnect.call(this, this.client);
return;
};
// private
var disconnect = function(client) {
var self = this
var self = this;
if (!this.useQueue) this.client = null;
client.end(function() {
if (!self.useQueue) return client.destroy();
var intervalObj = null
var cleanup = function () {
var retryCt = 0
......@@ -106,18 +143,17 @@ module.exports = (function() {
}
var connect = function(done) {
var self = this
var client = require("mysql").createClient({
user: self.config.username,
password: self.config.password,
host: self.config.host,
port: self.config.port,
database: self.config.database
var connection = mysql.createConnection({
host: this.config.host,
port: this.config.port,
user: this.config.username,
password: this.config.password,
database: this.config.database
})
client.setMaxListeners(self.maxConcurrentQueries)
self.isConnecting = false
done(null, client)
return
// client.setMaxListeners(self.maxConcurrentQueries)
this.isConnecting = false
done(null, connection)
}
var enqueue = function(queueItem) {
......@@ -157,10 +193,9 @@ module.exports = (function() {
var transferQueuedItems = function(count) {
for(var i = 0; i < count; i++) {
var queueItem = this.queue[0]
var queueItem = this.queue.shift();
if(queueItem) {
enqueue.call(this, queueItem)
this.queue = without(this.queue, queueItem)
}
}
}
......@@ -207,6 +242,4 @@ module.exports = (function() {
}
return ConnectorManager
})()
})()
\ No newline at end of file
var Utils = require("../../utils")
var Utils = require("../../utils")
, AbstractQuery = require('../abstract/query')
module.exports = (function() {
var Query = function(client, callee, options) {
var self = this
this.client = client
this.callee = callee
this.options = Utils._.extend({
var Query = function(client, sequelize, callee, options) {
this.client = client
this.callee = callee
this.sequelize = sequelize
this.options = Utils._.extend({
logging: console.log,
plain: false,
raw: false
}, options || {})
if(this.options.logging === true) {
console.log('DEPRECATION WARNING: The logging-option should be either a function or false. Default: console.log')
this.options.logging = console.log
}
if(this.options.logging == console.log) {
// using just console.log will break in node < 0.6
this.options.logging = function(s) { console.log(s) }
}
this.bindClientFunction = function(err) { onFailure.call(self, err) }
this.checkLoggingOption()
}
Utils._.extend(Query.prototype, require("../query").prototype)
Utils.inherit(Query, AbstractQuery)
Query.prototype.run = function(sql) {
var self = this
this.sql = sql
bindClient.call(this)
if(this.options.logging !== false)
if(this.options.logging !== false) {
this.options.logging('Executing: ' + this.sql)
}
this.client.query(this.sql, function(err, results, fields) {
self.emit('sql', self.sql)
err ? onFailure.call(self, err) : onSuccess.call(self, results, fields)
}).setMaxListeners(100)
return this
}
//private
this.emit('sql', this.sql)
var bindClient = function() {
this.client.on('error', this.bindClientFunction)
}
var unbindClient = function() {
this.client.removeListener('error', this.bindClientFunction)
}
var onSuccess = function(results, fields) {
var result = this.callee
, self = this
// add the inserted row id to the instance
if (this.callee && (this.sql.indexOf('INSERT INTO') == 0) && (results.hasOwnProperty('insertId')))
this.callee[this.callee.__factory.autoIncrementField] = results.insertId
if (this.sql.indexOf('SELECT') == 0) {
// transform results into real model instances
// return the first real model instance if options.plain is set (e.g. Model.find)
if(this.options.raw) {
result = results
if (err) {
this.emit('error', err, this.callee)
} else {
result = results.map(function(result) {
return self.callee.build(result, { isNewRecord: false })
})
this.emit('success', this.formatResults(results))
}
if(this.options.plain)
result = (result.length == 0) ? null : result[0]
} else if(this.sql.indexOf('SHOW TABLES') == 0) {
result = Utils._.flatten(results.map(function(resultSet) {
return Utils._.values(resultSet)
}))
} else if((this.sql.indexOf('SHOW') == 0) || (this.sql.indexOf('DESCRIBE') == 0)) {
result = results
}
unbindClient.call(this)
this.emit('success', result)
}
var onFailure = function(err) {
unbindClient.call(this)
this.emit('error', err, this.callee)
}.bind(this)).setMaxListeners(100)
return this
}
return Query
......
var Query = require("./query")
, Utils = require("../../utils")
, pg = require("pg")
, without = function(arr, elem) { return arr.filter(function(e) { return e != elem }) }
module.exports = (function() {
var ConnectorManager = function(sequelize, config) {
this.sequelize = sequelize
this.client = null
this.config = config || {}
this.pooling = (this.config.poolCfg != null && this.config.poolCfg.maxConnections > 0)
this.client = null
this.config = config || {}
this.pooling = (!!this.config.poolCfg && (this.config.poolCfg.maxConnections > 0))
this.pg = this.config.native ? require('pg').native : require('pg')
// set pooling parameters if specified
if (this.pooling) {
pg.defaults.poolSize = this.config.poolCfg.maxConnections
pg.defaults.poolIdleTimeout = this.config.poolCfg.maxIdleTime
this.pg.defaults.poolSize = this.config.poolCfg.maxConnections
this.pg.defaults.poolIdleTimeout = this.config.poolCfg.maxIdleTime
}
this.disconnectTimeoutId = null
this.pendingQueries = 0
this.maxConcurrentQueries = (this.config.maxConcurrentQueries || 50)
......@@ -26,7 +27,7 @@ module.exports = (function() {
ConnectorManager.prototype.query = function(sql, callee, options) {
var self = this
if (this.client == null) this.connect()
var query = new Query(this.client, callee, options || {})
var query = new Query(this.client, this.sequelize, callee, options || {})
self.pendingQueries += 1
return query.run(sql)
.success(function() { self.endQuery.call(self) })
......@@ -55,7 +56,10 @@ module.exports = (function() {
var connectCallback = function(err, client) {
self.isConnecting = false
if (!err && client) {
if (!!err) {
throw err
} else if (client) {
client.query("SET TIME ZONE 'UTC'")
.on('end', function() {
self.isConnected = true
......@@ -68,11 +72,10 @@ module.exports = (function() {
if (this.pooling) {
// acquire client from pool
pg.connect(uri, connectCallback)
this.pg.connect(uri, connectCallback)
} else {
//create one-off client
this.client = new pg.Client(uri)
this.client = new this.pg.Client(uri)
this.client.connect(connectCallback)
}
}
......
var Utils = require("../../utils");
var Utils = require("../../utils")
, AbstractQuery = require('../abstract/query')
module.exports = (function() {
var Query = function(client, callee, options) {
var self = this
var Query = function(client, sequelize, callee, options) {
this.client = client
this.sequelize = sequelize
this.callee = callee
this.options = Utils._.extend({
logging: console.log,
......@@ -12,83 +12,79 @@ module.exports = (function() {
raw: false
}, options || {})
if(this.options.logging === true) {
console.log('DEPRECATION WARNING: The logging-option should be either a function or false. Default: console.log')
this.options.logging = console.log
}
if(this.options.logging == console.log) {
// using just console.log will break in node < 0.6
this.options.logging = function(s) { console.log(s) }
}
this.checkLoggingOption()
}
Utils._.extend(Query.prototype, require("../query").prototype)
Utils.inherit(Query, AbstractQuery)
Query.prototype.run = function(sql) {
var self = this
this.sql = sql
if(this.options.logging !== false) {
this.options.logging('Executing: ' + this.sql)
}
var results = [];
var receivedError = false;
var receivedError = false
, query = this.client.query(sql)
, rows = []
var query = this.client.query(sql)
query.on('row', function(row) {
if (self.callee && (self.sql.indexOf('INSERT INTO') == 0 || self.sql.indexOf('UPDATE') == 0)) {
Utils._.forEach(row, function(value, key) {
self.callee[key] = value
})
results.push(self.callee)
rows.push(row)
})
query.on('error', function(err) {
receivedError = true
this.emit('error', err, this.callee)
}.bind(this))
query.on('end', function() {
this.emit('sql', this.sql)
if (receivedError) {
return
}
if (self.sql.indexOf('SELECT table_name FROM information_schema.tables') == 0) {
results.push(Utils._.values(row))
} else if (self.sql.indexOf('SELECT relname FROM pg_class WHERE oid IN') == 0) {
results.push(Utils._.values(row))
} else if (self.sql.indexOf('SELECT') == 0) {
// transform results into real model instances
// return the first real model instance if options.plain is set (e.g. Model.find)
if (self.options.raw) {
results.push(row);
} else {
results.push(self.callee.build(row, { isNewRecord: false }))
onSuccess.call(this, rows)
}.bind(this))
return this
}
Query.prototype.getInsertIdField = function() {
return 'id'
}
var onSuccess = function(rows) {
var results = []
, isTableNameQuery = (this.sql.indexOf('SELECT table_name FROM information_schema.tables') === 0)
, isRelNameQuery = (this.sql.indexOf('SELECT relname FROM pg_class WHERE oid IN') === 0)
if (isTableNameQuery || isRelNameQuery) {
return this.emit('success', rows.map(function(row) { return Utils._.values(row) }))
}
if (this.send('isSelectQuery')) {
this.emit('success', this.send('handleSelectQuery', rows))
} else if (this.send('isShowOrDescribeQuery')) {
this.emit('success', results)
} else if (this.send('isInsertQuery')) {
for (var key in rows[0]) {
if (rows[0].hasOwnProperty(key)) {
this.callee[key] = rows[0][key]
}
} else if((self.sql.indexOf('SHOW') == 0) || (self.sql.indexOf('DESCRIBE') == 0)) {
results.push(row)
}
});
query.on('end', function() {
self.emit('sql', self.sql)
if (receivedError) return;
if (self.sql.indexOf('SELECT') == 0) {
if (self.options.plain) {
self.emit('success', (results.length == 0) ? null : results[0])
} else {
self.emit('success', results)
this.emit('success', this.callee)
} else if (this.send('isUpdateQuery')) {
for (var key in rows[0]) {
if (rows[0].hasOwnProperty(key)) {
this.callee[key] = rows[0][key]
}
} else if((self.sql.indexOf('SHOW') == 0) || (self.sql.indexOf('DESCRIBE') == 0)) {
self.emit('success', results)
} else if (self.sql.indexOf('INSERT INTO') == 0) {
self.emit('success', results[0])
} else if (self.sql.indexOf('UPDATE') == 0) {
self.emit('success', self.callee)
} else {
self.emit('success', results)
}
});
query.on('error', function(err) {
receivedError = true
self.emit('error', err, self.callee)
});
return this
this.emit('success', this.callee)
} else {
this.emit('success', results)
}
}
return Query
......
var Utils = require("../utils")
module.exports = (function() {
var Query = function(database, callee, options) {
throw new Error('Constructor was not overwritten!')
}
Utils._.extend(Query.prototype, require("../emitters/custom-event-emitter").prototype)
Query.prototype.run = function(sql) {
throw new Error("The run method wasn't overwritten!")
}
Query.prototype.success = Query.prototype.ok = function(fct) {
this.on('success', fct)
return this
}
Query.prototype.failure = Query.prototype.fail = Query.prototype.error = function(fct) {
this.on('error', fct)
return this
}
return Query
})()
......@@ -10,7 +10,7 @@ module.exports = (function() {
Utils._.extend(ConnectorManager.prototype, require("../connector-manager").prototype)
ConnectorManager.prototype.query = function(sql, callee, options) {
return new Query(this.database, callee, options).run(sql)
return new Query(this.database, this.sequelize, callee, options).run(sql)
}
return ConnectorManager
......
var Utils = require("../../utils")
, util = require("util")
var MySqlQueryGenerator = Utils._.extend(
Utils._.clone(require("../query-generator")),
Utils._.clone(require("../mysql/query-generator"))
)
var hashToWhereConditions = MySqlQueryGenerator.hashToWhereConditions
var escape = function(str) {
if (typeof str === 'string') {
......@@ -22,25 +27,41 @@ module.exports = (function() {
var query = "CREATE TABLE IF NOT EXISTS <%= table %> (<%= attributes%>)"
, primaryKeys = []
, attrStr = Utils._.map(attributes, function(dataType, attr) {
if (Utils._.includes(dataType, 'PRIMARY KEY')) {
primaryKeys.push(attr)
return Utils.addTicks(attr) + " " + dataType
} else {
return Utils.addTicks(attr) + " " + dataType
}
}).join(", ")
, values = {
table: Utils.addTicks(tableName),
attributes: attrStr,
charset: (options.charset ? "DEFAULT CHARSET=" + options.charset : "")
, needsMultiplePrimaryKeys = (Utils._.values(attributes).filter(function(definition) {
return Utils._.includes(definition, 'PRIMARY KEY')
}).length > 1)
, attrStr = []
for (var attr in attributes) {
if (attributes.hasOwnProperty(attr)) {
var dataType = attributes[attr]
if (Utils._.includes(dataType, 'PRIMARY KEY') && needsMultiplePrimaryKeys) {
primaryKeys.push(attr)
attrStr.push(Utils.addTicks(attr) + " " + dataType.replace(/PRIMARY KEY/, 'NOT NULL'))
} else {
attrStr.push(Utils.addTicks(attr) + " " + dataType)
}
}
}
var values = {
table: Utils.addTicks(tableName),
attributes: attrStr.join(", "),
charset: (options.charset ? "DEFAULT CHARSET=" + options.charset : "")
}
, pkString = primaryKeys.map(function(pk) { return Utils.addTicks(pk) }).join(", ")
if (pkString.length > 0) {
values.attributes += ", PRIMARY KEY (" + pkString + ")"
}
return Utils._.template(query)(values).trim() + ";"
},
showTablesQuery: function() {
return "SELECT name FROM sqlite_master WHERE type='table';"
return "SELECT name FROM sqlite_master WHERE type='table' and name!='sqlite_sequence';"
},
insertQuery: function(tableName, attrValueHash) {
......@@ -62,13 +83,17 @@ module.exports = (function() {
updateQuery: function(tableName, attrValueHash, where) {
attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull)
var query = "UPDATE <%= table %> SET <%= values %> WHERE <%= where %>"
var query = "UPDATE <%= table %> SET <%= values %> WHERE <%= where %>"
, values = []
for (var key in attrValueHash) {
var value = attrValueHash[key]
values.push(Utils.addTicks(key) + "=" + escape((value instanceof Date) ? Utils.toSqlDate(value) : value))
}
var replacements = {
table: Utils.addTicks(tableName),
values: Utils._.map(attrValueHash, function(value, key){
return Utils.addTicks(key) + "=" + escape((value instanceof Date) ? Utils.toSqlDate(value) : value)
}).join(","),
values: values.join(","),
where: MySqlQueryGenerator.getWhereConditions(where)
}
......@@ -91,45 +116,75 @@ module.exports = (function() {
attributesToSQL: function(attributes) {
var result = {}
Utils._.map(attributes, function(dataType, name) {
for (var name in attributes) {
var dataType = attributes[name]
if(Utils.isHash(dataType)) {
var template = "<%= type %>"
, replacements = { type: dataType.type }
if(dataType.hasOwnProperty('allowNull') && !dataType.allowNull && !dataType.primaryKey)
if (dataType.hasOwnProperty('allowNull') && !dataType.allowNull && !dataType.primaryKey) {
template += " NOT NULL"
}
if(dataType.defaultValue != undefined) {
template += " DEFAULT <%= defaultValue %>"
replacements.defaultValue = Utils.escape(dataType.defaultValue)
}
if(dataType.unique) template += " UNIQUE"
if(dataType.primaryKey) template += " PRIMARY KEY"
if (dataType.unique) {
template += " UNIQUE"
}
if (dataType.primaryKey) {
template += " PRIMARY KEY"
if (dataType.autoIncrement) {
template += ' AUTOINCREMENT'
}
}
result[name] = Utils._.template(template)(replacements)
} else {
result[name] = dataType
}
})
}
return result
},
findAutoIncrementField: function(factory) {
var fields = Utils._.map(factory.attributes, function(definition, name) {
var isAutoIncrementField = (definition && (definition.indexOf('INTEGER PRIMARY KEY') == 0))
return isAutoIncrementField ? name : null
})
var fields = []
for (var name in factory.attributes) {
if (factory.attributes.hasOwnProperty(name)) {
var definition = factory.attributes[name]
if (definition && (definition.indexOf('INTEGER PRIMARY KEY AUTOINCREMENT') === 0)) {
fields.push(name)
}
}
}
return Utils._.compact(fields)
return fields
},
hashToWhereConditions: function(hash) {
for (var key in hash) {
if (hash.hasOwnProperty(key)) {
var value = hash[key]
if (typeof value === 'boolean') {
value = !!value ? 1 : 0
}
hash[key] = value
}
}
return hashToWhereConditions(hash)
}
}
var MySqlQueryGenerator = Utils._.extend(
Utils._.clone(require("../query-generator")),
Utils._.clone(require("../mysql/query-generator"))
)
return Utils._.extend(MySqlQueryGenerator, QueryGenerator)
})()
var Utils = require("../../utils")
var Utils = require("../../utils")
, AbstractQuery = require('../abstract/query')
module.exports = (function() {
var Query = function(database, callee, options) {
var Query = function(database, sequelize, callee, options) {
this.database = database
this.sequelize = sequelize
this.callee = callee
this.options = Utils._.extend({
logging: console.log,
......@@ -10,17 +12,13 @@ module.exports = (function() {
raw: false
}, options || {})
if(this.options.logging === true) {
console.log('DEPRECATION WARNING: The logging-option should be either a function or false. Default: console.log')
this.options.logging = console.log
}
this.checkLoggingOption()
}
Utils.inherit(Query, AbstractQuery)
if(this.options.logging == console.log) {
// using just console.log will break in node < 0.6
this.options.logging = function(s) { console.log(s) }
}
Query.prototype.getInsertIdField = function() {
return 'lastID'
}
Utils._.extend(Query.prototype, require("../query").prototype)
Query.prototype.run = function(sql) {
var self = this
......@@ -34,19 +32,17 @@ module.exports = (function() {
var columnTypes = {};
this.database.serialize(function() {
var executeSql = function() {
self.database[databaseMethod](self.sql, function(err, results) {
//allow clients to listen to sql to do their own logging or whatnot
self.database[getDatabaseMethod.call(self)](self.sql, function(err, results) {
// allow clients to listen to sql to do their own logging or whatnot
self.emit('sql', self.sql)
this.columnTypes = columnTypes;
err ? onFailure.call(self, err) : onSuccess.call(self, results, this)
})
};
var isInsertCommand = (self.sql.toLowerCase().indexOf('insert') == 0)
, isUpdateCommand = (self.sql.toLowerCase().indexOf('update') == 0)
, databaseMethod = (isInsertCommand || isUpdateCommand) ? 'run' : 'all'
if (databaseMethod === 'all' && /select\s.*?\sfrom\s+([^ ;]+)/i.test(self.sql)) {
if ((getDatabaseMethod.call(self) === 'all') && /select\s.*?\sfrom\s+([^ ;]+)/i.test(self.sql)) {
var tableName = RegExp.$1;
if (tableName !== 'sqlite_master') {
// get the column types
self.database.all("PRAGMA table_info(" + tableName + ")", function(err, results) {
......@@ -70,39 +66,43 @@ module.exports = (function() {
//private
var getDatabaseMethod = function() {
if (this.send('isInsertQuery') || this.send('isUpdateQuery')) {
return 'run'
} else {
return 'all'
}
}
var onSuccess = function(results, metaData) {
var result = this.callee
, self = this
// add the inserted row id to the instance
if (this.callee && (this.sql.indexOf('INSERT INTO') == 0) && metaData.hasOwnProperty('lastID')) {
var autoIncrementField = this.callee.__factory.autoIncrementField
this.callee[autoIncrementField] = metaData.lastID
if (this.send('isInsertQuery', results, metaData)) {
this.send('handleInsertQuery', results, metaData)
}
if (this.sql.indexOf('sqlite_master') != -1) {
result = results.map(function(resultSet){ return resultSet.name })
} else if (this.sql.indexOf('SELECT') == 0) {
// transform results into real model instances
// return the first real model instance if options.plain is set (e.g. Model.find)
result = results.map(function(resultSet) { return resultSet.name })
} else if (this.send('isSelectQuery')) {
// we need to convert the timestamps into actual date objects
if(this.options.raw) {
result = results
} else {
result = results.map(function(result) {
if(!this.options.raw) {
results = results.map(function(result) {
for (var name in result) {
if (metaData.columnTypes[name] === 'DATETIME') {
if (result.hasOwnProperty(name) && (metaData.columnTypes[name] === 'DATETIME')) {
result[name] = new Date(result[name]);
}
}
return self.callee.build(result, { isNewRecord: false })
return result
})
}
if(this.options.plain)
result = (result.length == 0) ? null : result[0]
} else if((this.sql.indexOf('SHOW') == 0) || (this.sql.indexOf('DESCRIBE') == 0))
result = this.send('handleSelectQuery', results)
} else if (this.send('isShowOrDescribeQuery')) {
result = results
}
this.emit('success', result)
}
......
......@@ -121,7 +121,7 @@ module.exports = (function() {
(self.undoneMethods == 0) && callback && callback()
})
self.queryInterface[_method].apply(self.queryInterface, args)
return self.queryInterface[_method].apply(self.queryInterface, args)
}
})(method)
}
......
......@@ -5,7 +5,7 @@ module.exports = (function() {
var QueryInterface = function(sequelize) {
this.sequelize = sequelize
this.QueryGenerator = require('./dialects/' + this.sequelize.options.dialect + '/query-generator')
this.QueryGenerator.options = this.sequelize.options;
this.QueryGenerator.options = this.sequelize.options;
}
Utils.addEventEmitter(QueryInterface)
......@@ -148,7 +148,7 @@ module.exports = (function() {
self.QueryGenerator.attributesToSQL(options)
)
self.sequelize.query(sql).success(function() {
self.sequelize.query(sql, null, {}).success(function() {
self.emit('renameColumn', null)
emitter.emit('success', null)
}).error(function(err) {
......@@ -202,26 +202,32 @@ module.exports = (function() {
QueryInterface.prototype.rawSelect = function(tableName, options, attributeSelector) {
var self = this
if(attributeSelector == undefined)
if(attributeSelector == undefined) {
throw new Error('Please pass an attribute selector!')
}
return new Utils.CustomEventEmitter(function(emitter) {
var sql = self.QueryGenerator.selectQuery(tableName, options)
, qry = self.sequelize.query(sql, null, { plain: true, raw: true, type: 'SELECT' })
var qry = self.sequelize
.query(sql, null, { plain: true, raw: true })
qry
.success(function(data) {
var result = data[attributeSelector]
if (options && options.parseInt) {
result = parseInt(result)
}
qry.success(function(data) {
self.emit('rawSelect', null)
emitter.emit('success', data[attributeSelector])
emitter.emit('success', result)
})
.error(function(err) {
self.emit('rawSelect', err)
emitter.emit('error', err)
})
qry.on('sql', function(sql) {
emitter.emit('sql', sql)
})
.on('sql', function(sql) {
emitter.emit('sql', sql)
})
}).run()
}
......@@ -231,17 +237,25 @@ module.exports = (function() {
var self = this
options = Utils._.extend({
success: function(obj){},
error: function(err){}
success: function(){},
error: function(){}
}, options || {})
return new Utils.CustomEventEmitter(function(emitter) {
var query = null
if(Array.isArray(sqlOrQueryParams)) {
if (sqlOrQueryParams.length === 1) {
sqlOrQueryParams.push(null)
}
if (sqlOrQueryParams.length === 2) {
sqlOrQueryParams.push({})
}
query = self.sequelize.query.apply(self.sequelize, sqlOrQueryParams)
} else {
query = self.sequelize.query(sqlOrQueryParams)
query = self.sequelize.query(sqlOrQueryParams, null, {})
}
// append the query for better testing
......@@ -258,7 +272,7 @@ module.exports = (function() {
})
query.on('sql', function(sql) {
emitter.emit('sql', sql)
})
});
}).run()
}
......
var Utils = require("./utils")
, DAOFactory = require("./dao-factory")
, DataTypes = require('./data-types')
, DAOFactoryManager = require("./dao-factory-manager")
, Migrator = require("./migrator")
, QueryInterface = require("./query-interface")
var Utils = require("./utils")
, DAOFactory = require("./dao-factory")
, DataTypes = require('./data-types')
, DAOFactoryManager = require("./dao-factory-manager")
, Migrator = require("./migrator")
, QueryInterface = require("./query-interface")
if(parseFloat(process.version.replace('v', '')) < 0.6) {
console.log("DEPRECATION WARNING: Support for Node.JS < v0.6 will be canceled in the next minor release.")
}
module.exports = (function() {
/**
Main constructor of the project.
Params:
- `database`
- `username`
- `password`, optional, default: null
- `options`, optional, default: {}
Examples:
mymodule.write('foo')
mymodule.write('foo', { stream: process.stderr })
*/
var Sequelize = function(database, username, password, options) {
this.options = Utils._.extend({
dialect: 'mysql',
......@@ -20,7 +36,9 @@ module.exports = (function() {
query: {},
sync: {},
logging: console.log,
omitNull: false
omitNull: false,
queue: true,
native: false
}, options || {})
if(this.options.logging === true) {
......@@ -35,7 +53,10 @@ module.exports = (function() {
host : this.options.host,
port : this.options.port,
pool : this.options.pool,
protocol: this.options.protocol
protocol: this.options.protocol,
queue : this.options.queue,
native : this.options.native,
maxConcurrentQueries: this.options.maxConcurrentQueries
}
var ConnectorManager = require("./dialects/" + this.options.dialect + "/connector-manager")
......@@ -46,8 +67,14 @@ module.exports = (function() {
this.importCache = {}
}
/**
Reference to Utils
*/
Sequelize.Utils = Utils
Sequelize.Utils._.map(DataTypes, function(sql, accessor) { Sequelize[accessor] = sql})
for (var dataType in DataTypes) {
Sequelize[dataType] = DataTypes[dataType]
}
Sequelize.prototype.getQueryInterface = function() {
this.queryInterface = this.queryInterface || new QueryInterface(this)
......@@ -55,28 +82,40 @@ module.exports = (function() {
}
Sequelize.prototype.getMigrator = function(options, force) {
if(force)
if(force) {
this.migrator = new Migrator(this, options)
else
} else {
this.migrator = this.migrator || new Migrator(this, options)
}
return this.migrator
}
Sequelize.prototype.define = function(daoName, attributes, options) {
options = options || {}
if(this.options.define)
options = Sequelize.Utils.merge(options, this.options.define)
options.omitNull = this.options.omitNull
var globalOptions = this.options
if(globalOptions.define) {
options = Utils._.extend({}, globalOptions.define, options)
Utils._(['classMethods', 'instanceMethods']).each(function(key) {
if(globalOptions.define[key]) {
options[key] = options[key] || {}
Utils._.extend(options[key], globalOptions.define[key])
}
})
}
options.omitNull = globalOptions.omitNull
var factory = new DAOFactory(daoName, attributes, options)
this.daoFactoryManager.addDAO(factory.init(this.daoFactoryManager))
return factory
}
Sequelize.prototype.isDefined = function(daoName) {
var daos = this.daoFactoryManager.daos
return (daos.filter(function(dao) { return dao.name === daoName }).length !== 0)
}
Sequelize.prototype.import = function(path) {
if (!this.importCache[path]) {
var defineCall = require(path)
......@@ -91,9 +130,18 @@ module.exports = (function() {
}
Sequelize.prototype.query = function(sql, callee, options) {
options = Utils._.extend(Utils._.clone(this.options.query), options || {})
if (arguments.length === 3) {
options = options
} else if (arguments.length === 2) {
options = {}
} else {
options = { raw: true }
}
options = Utils._.extend(Utils._.clone(this.options.query), options)
options = Utils._.extend(options, {
logging: this.options.hasOwnProperty('logging') ? this.options.logging : console.log
logging: this.options.hasOwnProperty('logging') ? this.options.logging : console.log,
type: (sql.toLowerCase().indexOf('select') === 0) ? 'SELECT' : false
})
return this.connectorManager.query(sql, callee, options)
......@@ -103,7 +151,7 @@ module.exports = (function() {
options = options || {}
if(this.options.sync) {
options = Sequelize.Utils.merge(options, this.options.sync)
options = Utils._.extend({}, this.options.sync, options)
}
var chainer = new Utils.QueryChainer()
......
var client = new (require("mysql").Client)()
, util = require("util")
, DataTypes = require("./data-types")
var mysql = require("mysql")
, connection = mysql.createConnection({})
, util = require("util")
, DataTypes = require("./data-types")
var Utils = module.exports = {
_: (function() {
......@@ -12,12 +13,20 @@ var Utils = module.exports = {
includes: _s.include,
camelizeIf: function(string, condition) {
var result = string
if(condition) result = _.camelize(string)
if(condition) {
result = _.camelize(string)
}
return result
},
underscoredIf: function(string, condition) {
var result = string
if(condition) result = _.underscored(string)
if(condition) {
result = _.underscored(string)
}
return result
}
})
......@@ -32,16 +41,16 @@ var Utils = module.exports = {
return Utils.TICK_CHAR + Utils.removeTicks(s) + Utils.TICK_CHAR
},
removeTicks: function(s) {
return s.replace("`", "")
return s.replace(new RegExp(Utils.TICK_CHAR, 'g'), "")
},
escape: function(s) {
return client.escape(s).replace(/\\"/g, '"')
return connection.escape(s).replace(/\\"/g, '"')
},
format: function(arr) {
var query = arr[0]
, replacements = Utils._.compact(arr.map(function(obj) { return obj != query ? obj : null}))
return client.format.apply(client, [query, replacements])
return connection.format.apply(connection, [query, replacements])
},
isHash: function(obj) {
return Utils._.isObject(obj) && !Utils._.isArray(obj);
......@@ -58,15 +67,17 @@ var Utils = module.exports = {
},
argsArePrimaryKeys: function(args, primaryKeys) {
var result = (args.length == Utils._.keys(primaryKeys).length)
Utils._.each(args, function(arg) {
if(result) {
if(['number', 'string'].indexOf(typeof arg) > -1)
result = true
else
result = (arg instanceof Date)
if (result) {
Utils._.each(args, function(arg) {
if(result) {
if(['number', 'string'].indexOf(typeof arg) !== -1)
result = true
else
result = (arg instanceof Date)
}
})
}
})
}
return result
},
combineTableNames: function(tableName1, tableName2) {
......@@ -81,13 +92,6 @@ var Utils = module.exports = {
return Utils.Lingo.en.isPlural(s) ? s : Utils.Lingo.en.pluralize(s)
},
merge: function(a, b){
for(var key in b) {
a[key] = b[key]
}
return a
},
removeCommentsFromFunctionString: function(s) {
s = s.replace(/\s*(\/\/.*)/g, '')
s = s.replace(/(\/\*[\n\r\s\S]*?\*\/)/mg, '')
......@@ -119,7 +123,7 @@ var Utils = module.exports = {
var _hash = {}
Utils._.each(hash, function(val, key) {
if(val !== null && val !== undefined) {
if (key.match(/Id$/) || ((val !== null) && (val !== undefined))) {
_hash[key] = val;
}
})
......@@ -128,6 +132,40 @@ var Utils = module.exports = {
}
return result
},
prependTableNameToHash: function(tableName, hash) {
if (tableName) {
var _hash = {}
for (var key in hash) {
if (key.indexOf('.') === -1) {
_hash[tableName + '.' + key] = hash[key]
} else {
_hash[key] = hash[key]
}
}
return _hash
} else {
return hash
}
},
inherit: function(subClass, superClass) {
if (superClass.constructor == Function) {
// Normal Inheritance
subClass.prototype = new superClass();
subClass.prototype.constructor = subClass;
subClass.prototype.parent = superClass.prototype;
} else {
// Pure Virtual Inheritance
subClass.prototype = superClass;
subClass.prototype.constructor = subClass;
subClass.prototype.parent = superClass;
}
return subClass;
}
}
......
{
"name": "sequelize",
"description": "Multi dialect ORM for Node.JS",
"version": "1.5.0-beta-2",
"version": "1.6.0-beta-1",
"author": "Sascha Depold <sascha@depold.com>",
"contributors": [
{
......@@ -22,20 +22,21 @@
}
],
"dependencies": {
"mysql": "0.9.x",
"underscore": "1.2.x",
"underscore.string": "2.0.x",
"lingo": "0.0.x",
"mysql": "~2.0.0-alpha3",
"underscore": "~1.4.0",
"underscore.string": "~2.3.0",
"lingo": "~0.0.5",
"validator": "0.3.x",
"moment": "1.1.x",
"moment": "~1.7.0",
"commander": "~0.6.0",
"generic-pool": "1.0.9"
},
"devDependencies": {
"jasmine-node": "1.0.17",
"sqlite3": "~2.1.5",
"pg": "0.6.x",
"buster": "~0.6.0"
"pg": "~0.8.6",
"buster": "~0.6.0",
"dox-foundation": "~0.3.0"
},
"keywords": [
"mysql",
......@@ -47,7 +48,13 @@
"scripts": {
"test": "npm run test-jasmine && npm run test-buster",
"test-jasmine": "./node_modules/.bin/jasmine-node spec-jasmine/",
"test-buster": "./node_modules/.bin/buster-test"
"test-buster": "npm run test-buster-mysql && npm run test-buster-postgres && npm run test-buster-postgres-native && npm run test-buster-sqlite",
"test-buster-travis": "./node_modules/.bin/buster-test",
"test-buster-mysql": "DIALECT=mysql ./node_modules/.bin/buster-test",
"test-buster-postgres": "DIALECT=postgres ./node_modules/.bin/buster-test",
"test-buster-postgres-native": "DIALECT=postgres-native ./node_modules/.bin/buster-test",
"test-buster-sqlite": "DIALECT=sqlite ./node_modules/.bin/buster-test",
"generate-docs": "node_modules/.bin/dox-foundation --source ./lib --title Sequelize"
},
"bin": {
"sequelize": "bin/sequelize"
......
......@@ -62,7 +62,7 @@ describe('BelongsTo', function() {
Task.belongsTo(User)
var task = Task.build({title: 'asd'})
expect(task['UserId']).toBeNull()
expect(task['UserId']).not.toBeDefined();
})
it("sets and gets the correct objects", function() {
......
......@@ -87,7 +87,11 @@ describe('HasMany', function() {
user.setTasks([task1, task2]).success(function() {
user.getTasks().success(function(tasks) {
expect(tasks.length).toEqual(2)
done()
user.getTasks({attributes: ['title']}).success(function(tasks) {
expect(tasks[0].selectedValues.title).toEqual('task1')
expect(tasks[0].selectedValues.id).toEqual(null)
done()
})
})
})
})
......
......@@ -88,7 +88,11 @@ describe('HasOne', function() {
user.setTask(task).on('success', function() {
user.getTask().on('success', function(task2) {
expect(task.title).toEqual(task2.title)
done()
user.getTask({attributes: ['title']}).on('success', function(task2) {
expect(task2.selectedValues.title).toEqual('snafu')
expect(task2.selectedValues.id).toEqual(null)
done()
})
})
})
})
......
......@@ -35,44 +35,61 @@ describe('QueryGenerator', function() {
selectQuery: [
{
arguments: ['myTable'],
expectation: "SELECT * FROM `myTable`;"
expectation: "SELECT * FROM `myTable`;",
context: QueryGenerator
}, {
arguments: ['myTable', {attributes: ['id', 'name']}],
expectation: "SELECT `id`, `name` FROM `myTable`;"
expectation: "SELECT `id`, `name` FROM `myTable`;",
context: QueryGenerator
}, {
arguments: ['myTable', {where: {id: 2}}],
expectation: "SELECT * FROM `myTable` WHERE `id`=2;"
expectation: "SELECT * FROM `myTable` WHERE `myTable`.`id`=2;",
context: QueryGenerator
}, {
arguments: ['myTable', {where: {name: 'foo'}}],
expectation: "SELECT * FROM `myTable` WHERE `name`='foo';"
expectation: "SELECT * FROM `myTable` WHERE `myTable`.`name`='foo';",
context: QueryGenerator
}, {
arguments: ['myTable', {where: {name: "foo';DROP TABLE myTable;"}}],
expectation: "SELECT * FROM `myTable` WHERE `name`='foo\\';DROP TABLE myTable;';"
expectation: "SELECT * FROM `myTable` WHERE `myTable`.`name`='foo\\';DROP TABLE myTable;';",
context: QueryGenerator
}, {
arguments: ['myTable', {where: 2}],
expectation: "SELECT * FROM `myTable` WHERE `id`=2;"
expectation: "SELECT * FROM `myTable` WHERE `myTable`.`id`=2;",
context: QueryGenerator
}, {
arguments: ['foo', { attributes: [['count(*)', 'count']] }],
expectation: 'SELECT count(*) as `count` FROM `foo`;'
expectation: 'SELECT count(*) as `count` FROM `foo`;',
context: QueryGenerator
}, {
arguments: ['myTable', {where: "foo='bar'"}],
expectation: "SELECT * FROM `myTable` WHERE foo='bar';"
expectation: "SELECT * FROM `myTable` WHERE foo='bar';",
context: QueryGenerator
}, {
arguments: ['myTable', {order: "id DESC"}],
expectation: "SELECT * FROM `myTable` ORDER BY id DESC;"
expectation: "SELECT * FROM `myTable` ORDER BY id DESC;",
context: QueryGenerator
}, {
arguments: ['myTable', {group: "name"}],
expectation: "SELECT * FROM `myTable` GROUP BY `name`;"
expectation: "SELECT * FROM `myTable` GROUP BY `name`;",
context: QueryGenerator
}, {
arguments: ['myTable', {group: "name", order: "id DESC"}],
expectation: "SELECT * FROM `myTable` GROUP BY `name` ORDER BY id DESC;",
context: QueryGenerator
}, {
arguments: ['myTable', {limit: 10}],
expectation: "SELECT * FROM `myTable` LIMIT 10;"
expectation: "SELECT * FROM `myTable` LIMIT 10;",
context: QueryGenerator
}, {
arguments: ['myTable', {limit: 10, offset: 2}],
expectation: "SELECT * FROM `myTable` LIMIT 2, 10;"
expectation: "SELECT * FROM `myTable` LIMIT 2, 10;",
context: QueryGenerator
}, {
title: 'ignores offset if no limit was passed',
arguments: ['myTable', {offset: 2}],
expectation: "SELECT * FROM `myTable`;"
expectation: "SELECT * FROM `myTable`;",
context: QueryGenerator
}
],
......@@ -194,6 +211,10 @@ describe('QueryGenerator', function() {
{
arguments: [{ id: [1,2,3] }],
expectation: "`id` IN (1,2,3)"
},
{
arguments: [{ id: [] }],
expectation: "`id` IN (NULL)"
}
]
}
......@@ -206,7 +227,7 @@ describe('QueryGenerator', function() {
// Options would normally be set by the query interface that instantiates the query-generator, but here we specify it explicitly
var context = test.context || {options: {}};
var conditions = QueryGenerator[suiteTitle].apply(context, test.arguments)
expect(conditions).toEqual(test.expectation)
})
})
......
......@@ -19,12 +19,21 @@ describe('QueryGenerator', function() {
arguments: ['myTable', {title: 'VARCHAR(255)', name: 'VARCHAR(255)'}],
expectation: "CREATE TABLE IF NOT EXISTS \"myTable\" (\"title\" VARCHAR(255), \"name\" VARCHAR(255));"
},
{
arguments: ['mySchema.myTable', {title: 'VARCHAR(255)', name: 'VARCHAR(255)'}],
expectation: "CREATE TABLE IF NOT EXISTS \"mySchema\".\"myTable\" (\"title\" VARCHAR(255), \"name\" VARCHAR(255));"
},
],
dropTableQuery: [
{
arguments: ['myTable'],
expectation: "DROP TABLE IF EXISTS \"myTable\";"
},
{
arguments: ['mySchema.myTable'],
expectation: "DROP TABLE IF EXISTS \"mySchema\".\"myTable\";"
}
],
......@@ -69,6 +78,12 @@ describe('QueryGenerator', function() {
title: 'uses offset even if no limit was passed',
arguments: ['myTable', {offset: 2}],
expectation: "SELECT * FROM \"myTable\" OFFSET 2;"
}, {
arguments: ['mySchema.myTable'],
expectation: "SELECT * FROM \"mySchema\".\"myTable\";"
}, {
arguments: ['mySchema.myTable', {where: {name: "foo';DROP TABLE mySchema.myTable;"}}],
expectation: "SELECT * FROM \"mySchema\".\"myTable\" WHERE \"name\"='foo'';DROP TABLE mySchema.myTable;';"
}
],
......@@ -100,38 +115,53 @@ describe('QueryGenerator', function() {
arguments: ['myTable', {name: 'foo', nullValue: undefined}],
expectation: "INSERT INTO \"myTable\" (\"name\") VALUES ('foo') RETURNING *;",
context: {options: {omitNull: true}}
}, {
arguments: ['mySchema.myTable', {name: 'foo'}],
expectation: "INSERT INTO \"mySchema\".\"myTable\" (\"name\") VALUES ('foo') RETURNING *;"
}, {
arguments: ['mySchema.myTable', {name: JSON.stringify({info: 'Look ma a " quote'})}],
expectation: "INSERT INTO \"mySchema\".\"myTable\" (\"name\") VALUES ('{\"info\":\"Look ma a \\\" quote\"}') RETURNING *;"
}, {
arguments: ['mySchema.myTable', {name: "foo';DROP TABLE mySchema.myTable;"}],
expectation: "INSERT INTO \"mySchema\".\"myTable\" (\"name\") VALUES ('foo'';DROP TABLE mySchema.myTable;') RETURNING *;"
}
],
updateQuery: [
{
arguments: ['myTable', {name: 'foo', birthday: new Date(Date.UTC(2011, 2, 27, 10, 1, 55))}, {id: 2}],
expectation: "UPDATE \"myTable\" SET \"name\"='foo',\"birthday\"='2011-03-27 10:01:55.0' WHERE \"id\"=2"
expectation: "UPDATE \"myTable\" SET \"name\"='foo',\"birthday\"='2011-03-27 10:01:55.0' WHERE \"id\"=2 RETURNING *"
}, {
arguments: ['myTable', {name: 'foo', birthday: new Date(Date.UTC(2011, 2, 27, 10, 1, 55))}, 2],
expectation: "UPDATE \"myTable\" SET \"name\"='foo',\"birthday\"='2011-03-27 10:01:55.0' WHERE \"id\"=2"
expectation: "UPDATE \"myTable\" SET \"name\"='foo',\"birthday\"='2011-03-27 10:01:55.0' WHERE \"id\"=2 RETURNING *"
}, {
arguments: ['myTable', {bar: 2}, {name: 'foo'}],
expectation: "UPDATE \"myTable\" SET \"bar\"=2 WHERE \"name\"='foo'"
expectation: "UPDATE \"myTable\" SET \"bar\"=2 WHERE \"name\"='foo' RETURNING *"
}, {
arguments: ['myTable', {name: "foo';DROP TABLE myTable;"}, {name: 'foo'}],
expectation: "UPDATE \"myTable\" SET \"name\"='foo'';DROP TABLE myTable;' WHERE \"name\"='foo'"
expectation: "UPDATE \"myTable\" SET \"name\"='foo'';DROP TABLE myTable;' WHERE \"name\"='foo' RETURNING *"
}, {
arguments: ['myTable', {bar: 2, nullValue: null}, {name: 'foo'}],
expectation: "UPDATE \"myTable\" SET \"bar\"=2,\"nullValue\"=NULL WHERE \"name\"='foo'"
expectation: "UPDATE \"myTable\" SET \"bar\"=2,\"nullValue\"=NULL WHERE \"name\"='foo' RETURNING *"
}, {
arguments: ['myTable', {bar: 2, nullValue: null}, {name: 'foo'}],
expectation: "UPDATE \"myTable\" SET \"bar\"=2,\"nullValue\"=NULL WHERE \"name\"='foo'",
expectation: "UPDATE \"myTable\" SET \"bar\"=2,\"nullValue\"=NULL WHERE \"name\"='foo' RETURNING *",
context: {options: {omitNull: false}}
}, {
arguments: ['myTable', {bar: 2, nullValue: null}, {name: 'foo'}],
expectation: "UPDATE \"myTable\" SET \"bar\"=2 WHERE \"name\"='foo'",
expectation: "UPDATE \"myTable\" SET \"bar\"=2 WHERE \"name\"='foo' RETURNING *",
context: {options: {omitNull: true}}
}, {
arguments: ['myTable', {bar: 2, nullValue: undefined}, {name: 'foo'}],
expectation: "UPDATE \"myTable\" SET \"bar\"=2 WHERE \"name\"='foo'",
expectation: "UPDATE \"myTable\" SET \"bar\"=2 WHERE \"name\"='foo' RETURNING *",
context: {options: {omitNull: true}}
},
}, {
arguments: ['mySchema.myTable', {name: 'foo', birthday: new Date(Date.UTC(2011, 2, 27, 10, 1, 55))}, {id: 2}],
expectation: "UPDATE \"mySchema\".\"myTable\" SET \"name\"='foo',\"birthday\"='2011-03-27 10:01:55.0' WHERE \"id\"=2 RETURNING *"
}, {
arguments: ['mySchema.myTable', {name: "foo';DROP TABLE mySchema.myTable;"}, {name: 'foo'}],
expectation: "UPDATE \"mySchema\".\"myTable\" SET \"name\"='foo'';DROP TABLE mySchema.myTable;' WHERE \"name\"='foo' RETURNING *"
}
],
deleteQuery: [
......@@ -147,6 +177,12 @@ describe('QueryGenerator', function() {
}, {
arguments: ['myTable', {name: "foo';DROP TABLE myTable;"}, {limit: 10}],
expectation: "DELETE FROM \"myTable\" WHERE \"id\" IN (SELECT \"id\" FROM \"myTable\" WHERE \"name\"='foo'';DROP TABLE myTable;' LIMIT 10)"
}, {
arguments: ['mySchema.myTable', {name: 'foo'}],
expectation: "DELETE FROM \"mySchema\".\"myTable\" WHERE \"id\" IN (SELECT \"id\" FROM \"mySchema\".\"myTable\" WHERE \"name\"='foo' LIMIT 1)"
}, {
arguments: ['mySchema.myTable', {name: "foo';DROP TABLE mySchema.myTable;"}, {limit: 10}],
expectation: "DELETE FROM \"mySchema\".\"myTable\" WHERE \"id\" IN (SELECT \"id\" FROM \"mySchema\".\"myTable\" WHERE \"name\"='foo'';DROP TABLE mySchema.myTable;' LIMIT 10)"
}
],
......@@ -167,6 +203,9 @@ describe('QueryGenerator', function() {
'User', ['username', 'isAdmin'], { indicesType: 'FULLTEXT', indexName: 'bar'}
],
expectation: "CREATE FULLTEXT INDEX \"bar\" ON \"User\" (\"username\", \"isAdmin\")"
}, {
arguments: ['mySchema.User', ['username', 'isAdmin']],
expectation: 'CREATE INDEX \"user_username_is_admin\" ON \"mySchema\".\"User\" (\"username\", \"isAdmin\")'
}
],
......@@ -188,6 +227,9 @@ describe('QueryGenerator', function() {
}, {
arguments: ['User', ['foo', 'bar']],
expectation: "DROP INDEX IF EXISTS \"user_foo_bar\""
}, {
arguments: ['User', 'mySchema.user_foo_bar'],
expectation: "DROP INDEX IF EXISTS \"mySchema\".\"user_foo_bar\""
}
],
......@@ -195,6 +237,10 @@ describe('QueryGenerator', function() {
{
arguments: [{ id: [1,2,3] }],
expectation: "\"id\" IN (1,2,3)"
},
{
arguments: [{ id: [] }],
expectation: "\"id\" IN (NULL)"
}
]
}
......
......@@ -40,6 +40,35 @@ describe('Sequelize', function() {
sequelize.define('foo', { title: Sequelize.STRING })
expect(sequelize.daoFactoryManager.all.length).toEqual(1)
})
it("overwrites global options", function() {
setup({ define: { collate: 'utf8_general_ci' } })
var DAO = sequelize.define('foo', {bar: Sequelize.STRING}, {collate: 'utf8_bin'})
expect(DAO.options.collate).toEqual('utf8_bin')
})
it("inherits global collate option", function() {
setup({ define: { collate: 'utf8_general_ci' } })
var DAO = sequelize.define('foo', {bar: Sequelize.STRING})
expect(DAO.options.collate).toEqual('utf8_general_ci')
})
it("inherits global classMethods and instanceMethods", function() {
setup({
define: {
classMethods : { globalClassMethod : function() {} },
instanceMethods : { globalInstanceMethod : function() {} }
}
})
var DAO = sequelize.define('foo', {bar: Sequelize.STRING}, {
classMethods : { localClassMethod : function() {} }
})
expect(typeof DAO.options.classMethods.globalClassMethod).toEqual('function')
expect(typeof DAO.options.classMethods.localClassMethod).toEqual('function')
expect(typeof DAO.options.instanceMethods.globalInstanceMethod).toEqual('function')
})
})
describe('sync', function() {
......
......@@ -2,37 +2,38 @@ if (typeof require === 'function') {
const buster = require("buster")
, Helpers = require('../buster-helpers')
, Sequelize = require('../../index')
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
buster.testRunner.timeout = 500
describe('BelongsTo', function() {
describe("[" + Helpers.getTestDialectTeaser() + "] BelongsTo", function() {
before(function(done) {
var self = this
Helpers.initTests({
beforeComplete: function(sequelize) { self.sequelize = sequelize },
beforeComplete: function(sequelize) {
this.sequelize = sequelize
}.bind(this),
onComplete: done
})
})
describe('setAssociation', function() {
it('clears the association if null is passed', function(done) {
var User = this.sequelize.define('User', { username: Sequelize.STRING })
, Task = this.sequelize.define('Task', { title: Sequelize.STRING })
var User = this.sequelize.define('UserXYZ', { username: Sequelize.STRING })
, Task = this.sequelize.define('TaskXYZ', { title: Sequelize.STRING })
Task.belongsTo(User)
this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
task.setUser(user).success(function() {
task.getUser().success(function(user) {
task.setUserXYZ(user).success(function() {
task.getUserXYZ().success(function(user) {
expect(user).not.toEqual(null)
task.setUser(null).success(function() {
task.getUser().success(function(user) {
task.setUserXYZ(null).success(function() {
task.getUserXYZ().success(function(user) {
expect(user).toEqual(null)
done()
})
......
......@@ -2,16 +2,18 @@ if (typeof require === 'function') {
const buster = require("buster")
, Helpers = require('../buster-helpers')
, Sequelize = require('../../index')
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
buster.testRunner.timeout = 500
describe('HasMany', function() {
describe("[" + Helpers.getTestDialectTeaser() + "] HasMany", function() {
before(function(done) {
var self = this
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize) { self.sequelize = sequelize },
onComplete: done
})
......@@ -21,7 +23,7 @@ describe('HasMany', function() {
describe('hasSingle', function() {
before(function(done) {
this.Article = this.sequelize.define('Article', { 'title': Sequelize.STRING })
this.Label = this.sequelize.define('Label', { 'text': Sequelize.STRING })
this.Label = this.sequelize.define('Label', { 'text': Sequelize.STRING })
this.Article.hasMany(this.Label)
......@@ -154,7 +156,7 @@ describe('HasMany', function() {
})
it("clears associations when passing null to the set-method with omitNull set to true", function(done) {
this.sequelize.options.omitNull = true;
this.sequelize.options.omitNull = true
var User = this.sequelize.define('User', { username: Sequelize.STRING })
, Task = this.sequelize.define('Task', { title: Sequelize.STRING })
......@@ -212,9 +214,9 @@ describe('HasMany', function() {
})
})
it("only get objects that fullfil the options", function(done) {
this.User.find({where: {username: 'John'}}).success(function (john) {
john.getTasks({where: {active: true}, limit: 10, order: 'ID DESC'}).success(function (tasks) {
it("only get objects that fulfill the options", function(done) {
this.User.find({ where: { username: 'John' } }).success(function (john) {
john.getTasks({ where: { active: true }, limit: 10, order: 'id DESC' }).success(function (tasks) {
expect(tasks.length).toEqual(1)
done();
})
......@@ -256,7 +258,7 @@ describe('HasMany', function() {
})
})
it("only get objects that fullfil the options", function(done) {
it("only get objects that fulfill the options", function(done) {
this.User.find({where: {username: 'John'}}).success(function (john) {
john.getTasks({where: {active: true}}).success(function (tasks) {
expect(tasks.length).toEqual(1)
......
......@@ -2,16 +2,18 @@ if (typeof require === 'function') {
const buster = require("buster")
, Sequelize = require("../../index")
, Helpers = require('../buster-helpers')
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
buster.testRunner.timeout = 500
buster.testRunner.timeout = 1500
describe('HasOne', function() {
describe("[" + Helpers.getTestDialectTeaser() + "] HasOne", function() {
before(function(done) {
var self = this
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize) { self.sequelize = sequelize },
onComplete: done
})
......@@ -19,20 +21,20 @@ describe('HasOne', function() {
describe('setAssociation', function() {
it('clears the association if null is passed', function(done) {
var User = this.sequelize.define('User', { username: Sequelize.STRING })
, Task = this.sequelize.define('Task', { title: Sequelize.STRING })
var User = this.sequelize.define('UserXYZ', { username: Sequelize.STRING })
, Task = this.sequelize.define('TaskXYZ', { title: Sequelize.STRING })
User.hasOne(Task)
this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
user.setTask(task).success(function() {
user.getTask().success(function(task) {
user.setTaskXYZ(task).success(function() {
user.getTaskXYZ().success(function(task) {
expect(task).not.toEqual(null)
user.setTask(null).success(function() {
user.getTask().success(function(task) {
user.setTaskXYZ(null).success(function() {
user.getTaskXYZ().success(function(task) {
expect(task).toEqual(null)
done()
})
......
if (typeof require === 'function') {
const buster = require("buster")
, Helpers = require('../buster-helpers')
, Sequelize = require('../../index')
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
describe("[" + Helpers.getTestDialectTeaser() + "] Mixin", function() {
before(function(done) {
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize) {
this.sequelize = sequelize
}.bind(this),
onComplete: done
})
})
describe('getAssociation', function() {
it('returns the respective part of the association for 1:1 associations', function() {
var User = this.sequelize.define('User', {})
var Task = this.sequelize.define('Task', {})
User.hasOne(Task)
Task.belongsTo(User)
expect(User.getAssociation(Task).target).toEqual(Task)
})
})
})
......@@ -4,6 +4,8 @@ const Sequelize = require(__dirname + "/../index")
, fs = require('fs')
var BusterHelpers = module.exports = {
Sequelize: Sequelize,
initTests: function(options) {
var sequelize = this.createSequelizeInstance(options)
......@@ -19,15 +21,21 @@ var BusterHelpers = module.exports = {
options.dialect = options.dialect || 'mysql'
options.logging = (options.hasOwnProperty('logging') ? options.logging : false)
var sequelizeOptions = {
logging: options.logging,
dialect: options.dialect,
port: config[options.dialect].port
}
if (process.env.DIALECT === 'postgres-native') {
sequelizeOptions.native = true
}
return new Sequelize(
config[options.dialect].database,
config[options.dialect].username,
config[options.dialect].password,
{
logging: options.logging,
dialect: options.dialect,
port: config[options.dialect].port
}
sequelizeOptions
)
},
......@@ -44,7 +52,39 @@ var BusterHelpers = module.exports = {
getSupportedDialects: function() {
return fs.readdirSync(__dirname + '/../lib/dialects').filter(function(file) {
return (file.indexOf('.js') === -1)
return ((file.indexOf('.js') === -1) && (file.indexOf('abstract') === -1))
})
},
getTestDialect: function() {
var envDialect = process.env.DIALECT || 'mysql'
if (envDialect === 'postgres-native') {
envDialect = 'postgres'
}
if (this.getSupportedDialects().indexOf(envDialect) === -1) {
throw new Error('The dialect you have passed is unknown. Did you really mean: ' + envDialect)
}
return envDialect
},
getTestDialectTeaser: function() {
var dialect = this.getTestDialect()
if (process.env.DIALECT === 'postgres-native') {
dialect = 'postgres-native'
}
return dialect.toUpperCase()
},
checkMatchForDialects: function(dialect, value, expectations) {
if (!!expectations[dialect]) {
expect(value).toMatch(expectations[dialect])
} else {
throw new Error('Undefined expectation for "' + dialect + '"!')
}
}
}
if(typeof require === 'function') {
const buster = require("buster")
, dialects = ['sqlite', 'mysql', 'postgres']
, Helpers = require('./buster-helpers')
const buster = require("buster")
, Helpers = require('./buster-helpers')
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
dialects.forEach(function(dialect) {
describe('DAO@' + dialect, function() {
before(function(done) {
var self = this
describe("[" + Helpers.getTestDialectTeaser() + "] DAO", function() {
before(function(done) {
var self = this
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize, DataTypes) {
self.sequelize = sequelize
self.User = sequelize.define('User', {
username: { type: DataTypes.STRING },
touchedAt: { type: DataTypes.DATE, defaultValue: DataTypes.NOW },
aNumber: { type: DataTypes.INTEGER }
})
},
onComplete: function(sequelize) {
self.User.sync({ force: true }).success(done)
}
})
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize, DataTypes) {
self.sequelize = sequelize
self.User = sequelize.define('User', {
username: { type: DataTypes.STRING },
touchedAt: { type: DataTypes.DATE, defaultValue: DataTypes.NOW },
aNumber: { type: DataTypes.INTEGER }
})
},
onComplete: function() {
self.User.sync({ force: true }).success(done)
}
})
})
describe('default values', function() {
describe('current date', function() {
it('should store a date in touchedAt', function() {
var user = this.User.build({ username: 'a user'})
expect(user.touchedAt instanceof Date).toBeTrue()
})
describe('default values', function() {
describe('current date', function() {
it('should store a date in touchedAt', function() {
var user = this.User.build({ username: 'a user'})
expect(user.touchedAt instanceof Date).toBeTrue()
})
it("should store the current date in touchedAt", function() {
this.useFakeTimers().tick(5000)
it("should store the current date in touchedAt", function() {
this.useFakeTimers().tick(5000)
var user = this.User.build({ username: 'a user'})
expect(+user.touchedAt).toBe(5000)
})
var user = this.User.build({ username: 'a user'})
expect(+user.touchedAt).toBe(5000)
})
})
})
describe('complete', function() {
it("gets triggered if an error occurs", function(done) {
this.User.find({ where: "asdasdasd" }).complete(function(err, result) {
expect(err).toBeDefined()
expect(err.message).toBeDefined()
done()
})
describe('complete', function() {
it("gets triggered if an error occurs", function(done) {
this.User.find({ where: "asdasdasd" }).complete(function(err, result) {
expect(err).toBeDefined()
expect(err.message).toBeDefined()
done()
})
})
it("gets triggered if everything was ok", function(done) {
this.User.count().complete(function(err, result) {
expect(err).toBeNull()
expect(result).toBeDefined()
done()
})
it("gets triggered if everything was ok", function(done) {
this.User.count().complete(function(err, result) {
expect(err).toBeNull()
expect(result).toBeDefined()
done()
})
})
})
describe('save', function() {
it('takes zero into account', function(done) {
this.User.build({ aNumber: 0 }).save([ 'aNumber' ]).success(function(user) {
expect(user.aNumber).toEqual(0)
done()
})
describe('save', function() {
it('takes zero into account', function(done) {
this.User.build({ aNumber: 0 }).save([ 'aNumber' ]).success(function(user) {
expect(user.aNumber).toEqual(0)
done()
})
})
})
describe('toJSON', function toJSON() {
before(function(done) {
this.User = this.sequelize.define('UserWithUsernameAndAgeAndIsAdmin', {
username: Helpers.Sequelize.STRING,
age: Helpers.Sequelize.INTEGER,
isAdmin: Helpers.Sequelize.BOOLEAN
}, {
timestamps: false,
logging: true
})
this.User.sync({ force: true }).success(done)
})
it('returns an object containing all values', function() {
var user = this.User.build({ username: 'test.user', age: 99, isAdmin: true })
expect(user.toJSON()).toEqual({ username: 'test.user', age: 99, isAdmin: true, id: null })
})
it('returns a response that can be stringified', function() {
var user = this.User.build({ username: 'test.user', age: 99, isAdmin: true })
expect(JSON.stringify(user)).toEqual('{"username":"test.user","age":99,"isAdmin":true,"id":null}')
})
it('returns a response that can be stringified and then parsed', function() {
var user = this.User.build({ username: 'test.user', age: 99, isAdmin: true })
expect(JSON.parse(JSON.stringify(user))).toEqual({ username: 'test.user', age: 99, isAdmin: true, id: null })
})
})
})
if(typeof require === 'function') {
const buster = require("buster")
, Sequelize = require("../index")
, Helpers = require('./buster-helpers')
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
describe("[" + Helpers.getTestDialectTeaser() + "] DAO", function() {
describe('validations', function() {
before(function(done) {
Helpers.initTests({
dialect: dialect,
onComplete: function(sequelize) {
this.sequelize = sequelize
done()
}.bind(this)
})
}) //- before
var checks = {
is: {
spec: { args: ["[a-z]",'i'] },
fail: "0",
pass: "a"
},
not: {
spec: { args: ["[a-z]",'i'] },
fail: "a",
pass: "0"
},
isEmail : {
fail: "a",
pass: "abc@abc.com"
}
, isUrl : {
fail: "abc",
pass: "http://abc.com"
}
, isIP : {
fail: "abc",
pass: "129.89.23.1"
}
, isAlpha : {
fail: "012",
pass: "abc"
}
, isAlphanumeric : {
fail: "_abc019",
pass: "abc019"
}
, isNumeric : {
fail: "abc",
pass: "019"
}
, isInt : {
fail: "9.2",
pass: "-9"
}
, isLowercase : {
fail: "AB",
pass: "ab"
}
, isUppercase : {
fail: "ab",
pass: "AB"
}
, isDecimal : {
fail: "a",
pass: "0.2"
}
, isFloat : {
fail: "a",
pass: "9.2"
}
, notNull : {
fail: null,
pass: 0
}
, isNull : {
fail: 0,
pass: null
}
, notEmpty : {
fail: " ",
pass: "a"
}
, equals : {
spec : { args : "bla bla bla" },
fail: "bla",
pass: "bla bla bla"
}
, contains : {
spec : { args : "bla" },
fail: "la",
pass: "0bla23"
}
, notContains : {
spec : { args : "bla" },
fail: "0bla23",
pass: "la"
}
, regex : {
spec : { args: ["[a-z]",'i'] },
fail: "0",
pass: "a"
}
, notRegex : {
spec: { args: ["[a-z]",'i'] },
fail: "a",
pass: "0"
}
, len : {
spec: { args: [2,4] },
fail: ["1", "12345"],
pass: ["12", "123", "1234"],
raw: true
}
, len: {
spec: [2,4],
fail: ["1", "12345"],
pass: ["12", "123", "1234"],
raw: true
}
, isUUID : {
spec: { args: 4 },
fail: "f47ac10b-58cc-3372-a567-0e02b2c3d479",
pass: "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}
, isDate : {
fail: "not a date",
pass: "2011-02-04"
}
, isAfter : {
spec: { args: "2011-11-05" },
fail: "2011-11-04",
pass: "2011-11-05"
}
, isBefore : {
spec: { args: "2011-11-05" },
fail: "2011-11-06",
pass: "2011-11-05"
}
, isIn : {
spec: { args: "abcdefghijk" },
fail: "ghik",
pass: "ghij"
}
, notIn : {
spec: { args: "abcdefghijk" },
fail: "ghij",
pass: "ghik"
}
, max : {
spec: { args: 23 },
fail: "24",
pass: "23"
}
, max : {
spec: 23,
fail: "24",
pass: "23"
}
, min : {
spec: { args: 23 },
fail: "22",
pass: "23"
}
, min : {
spec: 23,
fail: "22",
pass: "23"
}
, isArray : {
fail: 22,
pass: [22]
}
, isCreditCard : {
fail: "401288888888188f",
pass: "4012888888881881"
}
}
for (var validator in checks) {
if (checks.hasOwnProperty(validator)) {
var validatorDetails = checks[validator]
if (!validatorDetails.hasOwnProperty("raw")) {
validatorDetails.fail = [ validatorDetails.fail ]
validatorDetails.pass = [ validatorDetails.pass ]
}
//////////////////////////
// test the error cases //
//////////////////////////
for (var i = 0; i < validatorDetails.fail.length; i++) {
var failingValue = validatorDetails.fail[i]
it('correctly specifies an instance as invalid using a value of "' + failingValue + '" for the validation "' + validator + '"', function() {
var validations = {}
, message = validator + "(" + failingValue + ")"
if (validatorDetails.hasOwnProperty('spec')) {
validations[validator] = validatorDetails.spec
} else {
validations[validator] = {}
}
validations[validator].msg = message
var UserFail = this.sequelize.define('User' + Math.random(), {
name: {
type: Sequelize.STRING,
validate: validations
}
})
var failingUser = UserFail.build({ name : failingValue })
, errors = failingUser.validate()
expect(errors).not.toBeNull()
expect(errors).toEqual({ name : [message] })
})
}
////////////////////////////
// test the success cases //
////////////////////////////
for (var j = 0; j < validatorDetails.pass.length; j++) {
var succeedingValue = validatorDetails.pass[j]
it('correctly specifies an instance as valid using a value of "' + succeedingValue + '" for the validation "' + validator + '"', function() {
var validations = {}
if (validatorDetails.hasOwnProperty('spec')) {
validations[validator] = validatorDetails.spec
} else {
validations[validator] = {}
}
validations[validator].msg = validator + "(" + succeedingValue + ")"
var UserSuccess = this.sequelize.define('User' + Math.random(), {
name: {
type: Sequelize.STRING,
validate: validations
}
})
var successfulUser = UserSuccess.build({ name: succeedingValue })
expect(successfulUser.validate()).toBeNull()
})
}
}
}
it('correctly validates using custom validation methods', function() {
var User = this.sequelize.define('User' + Math.random(), {
name: {
type: Sequelize.STRING,
validate: {
customFn: function(val) {
if (val !== "2") {
throw new Error("name should equal '2'")
}
}
}
}
})
var failingUser = User.build({ name : "3" })
, errors = failingUser.validate()
expect(errors).not.toBeNull(null)
expect(errors).toEqual({ name: ["name should equal '2'"] })
var successfulUser = User.build({ name : "2" })
expect(successfulUser.validate()).toBeNull()
})
})
})
......@@ -2,13 +2,14 @@ if(typeof require === 'function') {
const buster = require("buster")
, QueryChainer = require("../lib/query-chainer")
, CustomEventEmitter = require("../lib/emitters/custom-event-emitter")
, Helpers = require('./buster-helpers')
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
buster.testRunner.timeout = 1000
describe('QueryChainer', function() {
describe("[" + Helpers.getTestDialectTeaser() + "] QueryChainer", function() {
before(function() {
this.queryChainer = new QueryChainer()
})
......
if(typeof require === 'function') {
const buster = require("buster")
, Helpers = require('./buster-helpers')
const buster = require("buster")
, Helpers = require('./buster-helpers')
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
describe('Sequelize', function() {
describe("[" + Helpers.getTestDialectTeaser() + "] Sequelize", function() {
before(function(done) {
var self = this
Helpers.initTests({
beforeComplete: function(sequelize) { self.sequelize = sequelize },
beforeComplete: function(sequelize) { this.sequelize = sequelize }.bind(this),
onComplete: done
})
})
describe('isDefined', function() {
it("returns false if the dao wasn't defined before", function() {
expect(this.sequelize.isDefined('Project')).toBeFalse()
})
it("returns true if the dao was defined before", function() {
this.sequelize.define('Project', {
name: Helpers.Sequelize.STRING
})
expect(this.sequelize.isDefined('Project')).toBeTrue()
})
})
describe('query', function() {
it("returns the expected results as json", function() {
expect(1).toEqual(1)
before(function(done) {
this.User = this.sequelize.define('User', {
username: Helpers.Sequelize.STRING
})
this.insertQuery = "INSERT INTO " + this.User.tableName + " (username, createdAt, updatedAt) VALUES ('john', '2012-01-01 10:10:10', '2012-01-01 10:10:10')"
this.User.sync().success(done).error(function(err) {
console(err)
done()
})
})
it('executes a query the internal way', function(done) {
this.sequelize.query(this.insertQuery, null, { raw: true }).success(function(result) {
expect(result).toBeNull()
done()
})
.error(function(err) {
console.log(err)
expect(err).not.toBeDefined()
done()
})
})
it('executes a query if only the sql is passed', function(done) {
this.sequelize.query(this.insertQuery).success(function(result) {
expect(result).not.toBeDefined()
done()
})
.error(function(err) {
console.log(err)
expect(err).not.toBeDefined()
done()
})
})
it('executes select queries correctly', function(done) {
this.sequelize.query(this.insertQuery).success(function() {
this.sequelize
.query("select * from " + this.User.tableName)
.success(function(users) {
expect(users.map(function(u){ return u.username })).toEqual(['john'])
done()
})
.error(function(err) {
console.log(err)
expect(err).not.toBeDefined()
done()
})
}.bind(this))
})
it('executes stored procedures', function(done) {
this.sequelize.query(this.insertQuery).success(function() {
this.sequelize.query('DROP PROCEDURE IF EXISTS foo').success(function() {
this.sequelize.query(
"CREATE PROCEDURE foo()\nSELECT * FROM " + this.User.tableName + ";"
).success(function() {
this.sequelize.query('CALL foo()').success(function(users) {
expect(users.map(function(u){ return u.username })).toEqual(['john'])
done()
})
}.bind(this))
}.bind(this))
}.bind(this))
})
it('uses the passed DAOFactory', function(done) {
this.sequelize.query(this.insertQuery).success(function() {
this.sequelize.query("SELECT * FROM " + this.User.tableName + ";", this.User).success(function(users) {
expect(users[0].__factory).toEqual(this.User)
done()
}.bind(this))
}.bind(this))
})
})
})
if(typeof require === 'function') {
const buster = require("buster")
, Helpers = require('../buster-helpers')
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
describe('DAO@sqlite', function() {
before(function(done) {
var self = this
if (dialect === 'sqlite') {
describe('[SQLITE] DAO', function() {
before(function(done) {
var self = this
Helpers.initTests({
dialect: 'sqlite',
beforeComplete: function(sequelize, DataTypes) {
self.sequelize = sequelize
Helpers.initTests({
dialect: 'sqlite',
beforeComplete: function(sequelize, DataTypes) {
self.sequelize = sequelize
self.User = sequelize.define('User', {
username: DataTypes.STRING
})
},
onComplete: function(sequelize) {
self.User.sync({ force: true }).success(done)
}
self.User = sequelize.define('User', {
username: DataTypes.STRING
})
},
onComplete: function() {
self.User.sync({ force: true }).success(done)
}
})
})
})
describe('findAll', function() {
it("handles dates correctly", function(done) {
var self = this
describe('findAll', function() {
it("handles dates correctly", function(done) {
var self = this
this.User
.create({ username: 'user', createdAt: new Date(2011, 04, 04) })
.success(function(oldUser) {
self.User
.create({ username: 'new user' })
.success(function(newUser) {
self.User.findAll({
where: ['createdAt > ?', new Date(2012, 01, 01)]
}).success(function(users) {
expect(users.length).toEqual(1)
done()
this.User
.create({ username: 'user', createdAt: new Date(2011, 04, 04) })
.success(function(oldUser) {
self.User
.create({ username: 'new user' })
.success(function(newUser) {
self.User.findAll({
where: ['createdAt > ?', new Date(2012, 01, 01)]
}).success(function(users) {
expect(users.length).toEqual(1)
done()
})
})
})
})
.error(function(err) {
console.log(err)
})
})
.error(function(err) {
console.log(err)
})
})
})
})
})
}
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!