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

Commit 5703c754 by Meg Sharkey

merged

2 parents d4d1b11f 370c22d7
Showing with 3149 additions and 1544 deletions
test*.js
.idea
.DS_STORE
node_modules
\ No newline at end of file
node_modules
config
......@@ -4,10 +4,17 @@ before_script:
script:
- "node_modules/.bin/jasmine-node spec"
- "node_modules/.bin/expresso -s test/**/*"
notifications:
- sascha@depold.com
email:
- sascha@depold.com
env:
- DB=mysql
language: node_js
node_js:
- 0.4
- 0.5
- 0.6
......@@ -2,6 +2,11 @@
The Sequelize library provides easy access to a MySQL database by mapping database entries to objects and vice versa. To put it in a nutshell... it's an ORM (Object-Relational-Mapper). The library is written entirely in JavaScript and can be used in the Node.JS environment.
## Blogposts/Changes ##
- [v1.3.0](http://blog.depold.com/post/15283366633/changes-in-sequelize-1-3-0): migrations, cross-database, validations, new listener notation, ...
- [v1.2.1](http://blog.depold.com/post/12319530694/changes-in-sequelize-1-2-1): changes some defaults and some interfaces
- [v1.0.0](http://blog.depold.com/post/5936116582/changes-in-sequelize-1-0-0): complete rewrite
## Features ##
- Schema definition
......@@ -16,7 +21,7 @@ The Sequelize library provides easy access to a MySQL database by mapping databa
You can find the documentation and announcements of updates on the [project's website](http://www.sequelizejs.com).
If you want to know about latest development and releases, follow me on [Twitter](http://twitter.com/sdepold).
Also make sure to take a look at the examples in the repository. The website will contain them soon, as well. For a (more or less) complete overview of changes in 1.0.0. take a look at [this blogpost](http://blog.depold.com/post/5936116582/changes-in-sequelize-1-0-0).
Also make sure to take a look at the examples in the repository. The website will contain them soon, as well.
## Collaboration ##
......@@ -24,10 +29,8 @@ I'm glad to get pull request if any functionality is missing or something is bug
## Tests ##
In order to run the tests, just do ```npm install```, which will install expresso and jasmine. I am switching from
expresso to jasmine, so please add according tests to your pull requests. This is how you start the tests:
In order to run the tests, just do ```npm install```, which will install jasmine. Please add tests to your pull requests. This is how you start the tests:
node_modules/.bin/expresso -s test/**/*
node_modules/.bin/jasmine-node spec/
Current build status on travis-ci: [![Build Status](https://secure.travis-ci.org/sdepold/sequelize.png)](http://travis-ci.org/sdepold/sequelize)
#!/usr/bin/env node
const path = require("path")
, fs = require("fs")
, program = require("commander")
, Sequelize = require(__dirname + '/../index')
, _ = Sequelize.Utils._
var configPath = process.cwd() + '/config'
, migrationsPath = process.cwd() + '/migrations'
, configFile = configPath + '/config.json'
, configPathExists = path.existsSync(configPath)
, configFileExists = path.existsSync(configFile)
var writeConfig = function(config) {
!configPathExists && fs.mkdirSync(configPath)
config = JSON.stringify(config)
config = config.replace('{', '{\n ')
config = config.replace(/,/g, ",\n ")
config = config.replace('}', "\n}")
fs.writeFileSync(configFile, config)
}
var createMigrationsFolder = function(force) {
if(force) {
console.log('Deleting the migrations folder.')
try {
fs.readdirSync(migrationsPath).forEach(function(filename) {
fs.unlinkSync(migrationsPath + '/' + filename)
})
} catch(e) {}
try {
fs.rmdirSync(migrationsPath)
console.log('Successfully deleted the migrations folder.')
} catch(e) {}
}
console.log('Creating migrations folder.')
try {
fs.mkdirSync(migrationsPath)
console.log('Successfully create migrations folder.')
} catch(e) {
console.log('Migrations folder already exist.')
}
}
var readConfig = function() {
try {
return JSON.parse(fs.readFileSync(configFile))
} catch(e) {
throw new Error('The config.json is not available or contains invalid JSON.')
}
}
program
.version('1.3.0')
.option('-i, --init', 'Initializes the project. Creates a config/config.json')
.option('-m, --migrate', 'Runs undone migrations')
.option('-u, --undo', 'Redo the last migration.')
.option('-f, --force', 'Forces the action to be done.')
.parse(process.argv)
if(program.migrate) {
if(configFileExists) {
var config = readConfig()
, options = {}
_.each(config, function(value, key) {
if(['database', 'username', 'password'].indexOf(key) == -1) {
options[key] = value
}
})
options = _.extend(options, { logging: false })
var sequelize = new Sequelize(config.database, config.username, config.password, options)
, migratorOptions = { path: __dirname + '/../migrations' }
, migrator = sequelize.getMigrator(migratorOptions)
if(program.undo) {
sequelize.migrator.findOrCreateSequelizeMetaModel().success(function(Meta) {
Meta.find({ order: 'id DESC' }).success(function(meta) {
if(meta) {
migrator = sequelize.getMigrator(_.extend(migratorOptions, meta), true)
}
migrator.migrate({ method: 'down' })
})
})
} else {
sequelize.migrate()
}
} else {
throw new Error('Please add a configuration file under config/config.json. You might run "sequelize --init".')
}
} else if(program.init) {
if(!configFileExists || !!program.force) {
writeConfig({
username: "root",
password: null,
database: 'database',
host: '127.0.0.1'
})
console.log('Successfully created config.json')
} else {
console.log('A config.json already exists. Run "sequelize --init --force" to overwrite it.')
}
createMigrationsFolder(program.force)
} else {
console.log('Please define any params!')
}
# v0.1.0 #
- first stable version
- implemented all basic functions
- associations are working
# v1.3.1 #
- [REFACTORING] renamed ModelManager to ModelFactoryManager
# v1.3.0 #
- [REFACTORING] Model#all is now a function and not a getter.
- [REFACTORING] Renamed ModelDefinition to ModelFactory
- [REFACTORING] Private method scoping; Attributes are still public
- [REFACTORING] Use the new util module for node 0.6.2
- [FEATURE] QueryChainer can now run serially
- [FEATURE] Association definition is chainable: Person.hasOne(House).hasMany(Address)
- [FEATURE] Validations (Thanks to [hiddentao](https://github.com/hiddentao))
- [FEATURE] jQuery-like event listeners: .success(callback) and .error(callback)
- [FEATURE] aliasing for select queries: Model.find({ where: 'id = 1', attributes: ['id', ['name', 'username']] }) ==> will return the user's name as username
- [FEATURE] cross-database support. currently supported: mysql, sqlite
- [FEATURE] migrations
- [TEST] removed all expresso tests and converted them to jasmine
# v0.2.0 #
- added methods for setting associations
- added method for chaining an arbitraty amount of queries
# v1.2.1 #
- [REFACTORING] renamed the global options for sync, query and define on sequelize; before: options.queryOptions; now: options.query
- [FEATURE] allow definition of charset via global define option in sequelize or via charset option in sequelize.define
- [FEATURE] allow definition of mysql engine via global define option in sequelize or via engine option in sequelize.define; default is InnoDB now
- [FEATURE] find and findAll will now search in a list of values via: Model.findAll({where: { id: [1,2,3] }}); will return all models with id 1, 2 and 3
- [TEST] force latin1 charset for travis
# v0.2.1 #
- fixed date bug
# v1.2.0 #
- [FEATURE] min/max function for models, which return the min/max value in a column
- [FEATURE] getModel for modelManager for getting a model without storing it in a variable; use it via sequelize.modelManager.getModel('User')
- [TEST] test suite refactoring for jasmine
# v0.2.2 #
- released project as npm package
# v1.1.4 #
- [BUG] tables with identical prefix (e.g. wp_) can now be used in many-to-many associations
# v0.2.3 #
- added latest mysql connection library
- fixed id handling on save
- fixed text handling (varchar > 255; text)
- using the inflection library for naming tables more convenient
- Sequelize.TEXT is now using MySQL datatype TEXT instead of varchar(4000)
# v1.1.3 #
- [BUG] scoped options in model => a model can now have the attribute options
- [FEATURE] added drop method for sequelize, that drops all currently registered tables
# v0.2.4 #
- fixed bug when using cross associated tables (many to many associations)
# v1.1.2 #
- [BUG] prevent malfunction after being idle
# v0.2.5 #
- added BOOLEAN type
- added FLOAT type
- fixed DATE type issue
- fixed npm package
# v1.1.1 #
- [BUG] fixed memory leaks
- [FEATURE] added query queueing (adjustable via maxConcurrentQueries in config; default: 50)
# v0.2.6 #
- refactored Sequelize to fit CommonJS module conventions
# v1.1.0 #
- [BUG] defaultValue 0 is now working
- [REMOVED] mysql-pool usage (will give it a new try later)
- [CHORE] updated node-mysql to 0.9.4
# v0.3.0 #
- added possibility to define class and instance methods for models
- added import method for loading model definition from a file
# v1.0.2 #
- [BUG] Fixed where clause generation for models with explicit primary keys (allanca)
- [BUG] Set insertId for non-default auto increment fields (allanca)
# v0.4.0 #
- added error handling when defining invalid database credentials
- Sequelize#sync, Sequelize#drop, model#sync, model#drop returns errors via callback
- code is now located under lib/sequelize to use it with nDistro
- added possibility to use non default mysql database (host/port)
- added error handling when defining invalid database port/host
- schema definitions can now contain default values and null allowance
- database credentials can now also contain an empty / no password
# v1.0.1 #
- [FEATURE] Added Model.count(callback), which returns the number of elements saved in the database
- [BUG] Fixed self associations
# v1.0.0 #
- complete rewrite
- added new emitter syntax
- sql injection protection
- select now supports hash usage of where
- select now supports array usage of where
- added a lot of options to find/findAll
- Wrapped queries correctly using `foo`
- using expresso 0.7.2
- moved config for test database into seperated config file
- Added method for adding and deleting single associations
# v0.4.3 #
- renamed loadAssociatedData to fetchAssociations
- renamed Model#associatedData to fetchedAssociations
- added fetchAssociations to finder methods
- store data found by finder method in the associatedData hash + grep them from there if reload is not forced
- added option to sequelize constructor for disabling the pluralization of tablenames: disableTableNameModification
- allow array as value for chainQueries => Sequelize.chainQueries([save: [a,b,c]], callback)
- remove the usage of an array => Sequelize.chainQueries({save: a}, {destroy: b}, callback)
# v0.4.2 #
- fixed bugs from 0.4.1
- added the model instance method loadAssociatedData which adds the hash Model#associatedData to an instance which contains all associated data
# v0.4.1 #
- THIS UPDATE CHANGES TABLE STRUCTURES MASSIVELY!
......@@ -56,66 +90,50 @@
- added hasOneAndBelongsTo
- nodejs-mysql-native 0.4.2
# v0.4.2 #
- fixed bugs from 0.4.1
- added the model instance method loadAssociatedData which adds the hash Model#associatedData to an instance which contains all associated data
# v0.4.3 #
- renamed loadAssociatedData to fetchAssociations
- renamed Model#associatedData to fetchedAssociations
- added fetchAssociations to finder methods
- store data found by finder method in the associatedData hash + grep them from there if reload is not forced
- added option to sequelize constructor for disabling the pluralization of tablenames: disableTableNameModification
- allow array as value for chainQueries => Sequelize.chainQueries([save: [a,b,c]], callback)
- remove the usage of an array => Sequelize.chainQueries({save: a}, {destroy: b}, callback)
# v0.4.0 #
- added error handling when defining invalid database credentials
- Sequelize#sync, Sequelize#drop, model#sync, model#drop returns errors via callback
- code is now located under lib/sequelize to use it with nDistro
- added possibility to use non default mysql database (host/port)
- added error handling when defining invalid database port/host
- schema definitions can now contain default values and null allowance
- database credentials can now also contain an empty / no password
# v1.0.0 #
- complete rewrite
- added new emitter syntax
- sql injection protection
- select now supports hash usage of where
- select now supports array usage of where
- added a lot of options to find/findAll
- Wrapped queries correctly using `foo`
- using expresso 0.7.2
- moved config for test database into seperated config file
- Added method for adding and deleting single associations
# v0.3.0 #
- added possibility to define class and instance methods for models
- added import method for loading model definition from a file
# v1.0.1 #
- Added Model.count(callback), which returns the number of elements saved in the database
- Fixed self associations
# v0.2.6 #
- refactored Sequelize to fit CommonJS module conventions
# v1.0.2 #
- Fixed where clause generation for models with explicit primary keys (allanca)
- Set insertId for non-default auto increment fields (allanca)
# v0.2.5 #
- added BOOLEAN type
- added FLOAT type
- fixed DATE type issue
- fixed npm package
# v1.1.0 #
- defaultValue 0 is now working
- REVERTED mysql-pool usage (will give it a new try later)
- updated node-mysql to 0.9.4
# v0.2.4 #
- fixed bug when using cross associated tables (many to many associations)
# v1.1.1 #
- fixed memory leaks
- added query queueing (adjustable via maxConcurrentQueries in config; default: 50)
# v0.2.3 #
- added latest mysql connection library
- fixed id handling on save
- fixed text handling (varchar > 255; text)
- using the inflection library for naming tables more convenient
- Sequelize.TEXT is now using MySQL datatype TEXT instead of varchar(4000)
# v1.1.2 #
- prevent malfunction after being idle
# v0.2.2 #
- released project as npm package
# v1.1.3 #
- [BUG] scoped options in model => a model can now have the attribute options
- [FEATURE] added drop method for sequelize, that drops all currently registered tables
# v0.2.1 #
- fixed date bug
# v1.1.4 #
- [BUG] tables with identical prefix (e.g. wp_) can now be used in many-to-many associations
# v0.2.0 #
- added methods for setting associations
- added method for chaining an arbitraty amount of queries
# v1.2.0 #
- [FEATURE] min/max function for models, which return the min/max value in a column
- [FEATURE] getModel for modelManager for getting a model without storing it in a variable; use it via sequelize.modelManager.getModel('User')
- [TEST] test suite refactoring for jasmine
# v0.1.0 #
- first stable version
- implemented all basic functions
- associations are working
# v1.2.1 #
- [REFACTORING] renamed the global options for sync, query and define on sequelize; before: options.queryOptions; now: options.query
- [FEATURE] allow definition of charset via global define option in sequelize or via charset option in sequelize.define
- [FEATURE] allow definition of mysql engine via global define option in sequelize or via engine option in sequelize.define; default is InnoDB now
- [FEATURE] find and findAll will now search in a list of values via: Model.findAll({where: { id: [1,2,3] }}); will return all models with id 1, 2 and 3
- [TEST] force latin1 charset for travis
var Sequelize = require(__dirname + "/../../lib/sequelize/Sequelize").Sequelize,
sequelize = new Sequelize("sequelize_test", "root", null, {disableLogging: false})
var Person = sequelize.define('person', {
name: Sequelize.STRING
})
var Pet = sequelize.define('pet', {
name: Sequelize.STRING
})
Person.hasManyAndBelongsTo('pets', Pet, 'owner')
Sequelize.chainQueries([{drop: sequelize}, {sync: sequelize}], function() {
var person = new Person({ name: 'Luke' }),
pet1 = new Pet({ name: 'Bob' }),
pet2 = new Pet({ name: 'Aaron' })
Sequelize.chainQueries([{save: person}, {save: pet1}, {save: pet2}], function() {
person.setPets([pet1], function(pets) {
Sequelize.Helper.log('my pet: ' + pets[0].name )
Sequelize.Helper.log("Now let's get the same data with fetchData!")
person.fetchAssociations(function(data) {
Sequelize.Helper.log("And here we are: " + data.pets[0].name)
Sequelize.Helper.log("The object should now also contain the data: " + person.fetchedAssociations.pets[0].name)
Sequelize.Helper.log('This won\'t do a database request!')
person.getPets(function(pets) {
Sequelize.Helper.log("Pets: " + pets.map(function(pet) { return pet.name }).join(", "))
Sequelize.Helper.log("Let's associate with another pet...")
person.setPets([pet1, pet2], function() {
Sequelize.Helper.log("The set call has stored the pets as associated data!")
Sequelize.Helper.log("And now let's find the pets again! This will make no new database request but serve the already stored pets Bob and Aaron!")
person.getPets(function(pets) {
Sequelize.Helper.log("Pets: " + pets.map(function(pet) { return pet.name }).join(", "))
Sequelize.Helper.log("Now let's force the reloading of pets!")
person.getPets({refetchAssociations: true}, function(pets) {
Sequelize.Helper.log("Pets: " + pets.map(function(pet) { return pet.name }).join(", "))
Person.find(person.id, { fetchAssociations: true }, function(p) {
var petNames = p.fetchedAssociations.pets.map(function(pet) { return pet.name }).join(", ")
Sequelize.Helper.log('Works with find as well: ' + petNames)
})
Person.findAll({ fetchAssociations: true }, function(people) {
var petNames = people[0].fetchedAssociations.pets.map(function(pet) { return pet.name }).join(", ")
Sequelize.Helper.log('And also with findAll: ' + petNames)
})
})
})
})
})
})
})
})
})
\ No newline at end of file
module.exports = require("./lib/sequelize/sequelize")
\ No newline at end of file
module.exports = require("./lib/sequelize")
var Utils = require("./../utils")
, DataTypes = require('./../data-types')
var BelongsTo = module.exports = function(srcModel, targetModel, options) {
this.source = srcModel
this.target = targetModel
this.options = options
this.isSelfAssociation = (this.source.tableName == this.target.tableName)
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)
: this.target.tableName
}
// the id is in the source table
BelongsTo.prototype.injectAttributes = function() {
var newAttributes = {}
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.attributes, Utils.simplifyAttributes(newAttributes))
return this
}
BelongsTo.prototype.injectGetter = function(obj) {
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)
module.exports = (function() {
var BelongsTo = function(srcModel, targetModel, options) {
this.source = srcModel
this.target = targetModel
this.options = options
this.isSelfAssociation = (this.source.tableName == this.target.tableName)
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)
: this.target.tableName
}
return this
}
BelongsTo.prototype.injectSetter = function(obj) {
var self = this
, accessor = Utils._.camelize('set_' + (this.options.as || Utils.singularize(this.target.tableName)))
obj[accessor] = function(associatedObject) {
obj[self.identifier] = associatedObject ? associatedObject.id : null
return obj.save()
// the id is in the source table
BelongsTo.prototype.injectAttributes = function() {
var newAttributes = {}
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)
return this
}
return this
}
\ No newline at end of file
BelongsTo.prototype.injectGetter = function(obj) {
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)
}
return this
}
BelongsTo.prototype.injectSetter = function(obj) {
var self = this
, accessor = Utils._.camelize('set_' + (this.options.as || Utils.singularize(this.target.tableName)))
obj[accessor] = function(associatedObject) {
obj[self.identifier] = associatedObject ? associatedObject.id : null
return obj.save()
}
return this
}
return BelongsTo
})()
var Utils = require('./../utils')
var HasManyDoubleLinked = module.exports = function(definition, instance) {
this.__definition = definition
this.instance = instance
}
HasManyDoubleLinked.prototype.injectGetter = function() {
var self = this
var customEventEmitter = new Utils.CustomEventEmitter(function() {
var where = {}
//connectorModel = join table
//fully qualify
where[self.__definition.connectorModel.tableName+"."+self.__definition.identifier] = self.instance.id
var primaryKeys = Utils._.keys(self.__definition.connectorModel.rawAttributes)
, foreignKey = primaryKeys.filter(function(pk) { return pk != self.__definition.identifier })[0]
where[self.__definition.connectorModel.tableName+"."+foreignKey] = {join: self.__definition.target.tableName+".id"}
self.__definition.target.findAllJoin(self.__definition.connectorModel.tableName, {where: where})
.on('success', function(objects) { customEventEmitter.emit('success', objects) })
.on('failure', function(err){ customEventEmitter.emit('failure', err) })
})
return customEventEmitter.run()
}
HasManyDoubleLinked.prototype.destroyObsoleteAssociations = function(oldAssociations, newAssociations) {
var self = this
var emitter = new Utils.CustomEventEmitter(function() {
var chainer = new Utils.QueryChainer
var foreignIdentifier = self.__definition.target.associations[self.__definition.associationAccessor].identifier
var obsoleteAssociations = oldAssociations.filter(function(obj) { return !obj.equalsOneOf(newAssociations) })
if(obsoleteAssociations.length == 0)
return emitter.emit('success', null)
obsoleteAssociations.forEach(function(associatedObject) {
var where = {}
, primaryKeys = Utils._.keys(self.__definition.connectorModel.rawAttributes)
, foreignKey = primaryKeys.filter(function(pk) { return pk != self.__definition.identifier })[0]
where[self.__definition.identifier] = self.instance.id
where[foreignKey] = associatedObject.id
self.__definition.connectorModel.find({where: where}).on('success', function(connector) {
chainer.add(connector.destroy())
if(chainer.emitters.length == obsoleteAssociations.length) {
// found all obsolete connectors and will delete them now
chainer
.run()
.on('success', function() { emitter.emit('success', null) })
.on('failure', function(err) { emitter.emit('failure', err) })
}
})
module.exports = (function() {
var HasManyDoubleLinked = function(definition, instance) {
this.__factory = definition
this.instance = instance
}
HasManyDoubleLinked.prototype.injectGetter = function() {
var self = this
var customEventEmitter = new Utils.CustomEventEmitter(function() {
var where = {}
//fully qualify
where[self.__factory.connectorModel.tableName+"."+self.__factory.identifier] = self.instance.id
var primaryKeys = Utils._.keys(self.__factory.connectorModel.rawAttributes)
, foreignKey = primaryKeys.filter(function(pk) { return pk != self.__factory.identifier })[0]
where[self.__factory.connectorModel.tableName+"."+foreignKey] = {join: self.__factory.target.tableName+".id"}
self.__factory.target.findAllJoin(self.__factory.connectorModel.tableName, {where: where})
.on('success', function(objects) { customEventEmitter.emit('success', objects) })
.on('failure', function(err){ customEventEmitter.emit('failure', err) })
})
})
return emitter.run()
}
HasManyDoubleLinked.prototype.injectSetter = function(emitter, oldAssociations, newAssociations) {
var self = this
this.destroyObsoleteAssociations(oldAssociations, newAssociations)
.on('failure', function(err) { emitter.emit('failure', err) })
.on('success', function() {
var chainer = new Utils.QueryChainer
, association = self.__definition.target.associations[self.__definition.associationAccessor]
, foreignIdentifier = association.isSelfAssociation ? association.foreignIdentifier : association.identifier
, unassociatedObjects = newAssociations.filter(function(obj) { return !obj.equalsOneOf(oldAssociations) })
unassociatedObjects.forEach(function(unassociatedObject) {
var attributes = {}
attributes[self.__definition.identifier] = self.instance.id
attributes[foreignIdentifier] = unassociatedObject.id
chainer.add(self.__definition.connectorModel.create(attributes))
return customEventEmitter.run()
}
HasManyDoubleLinked.prototype.injectSetter = function(emitter, oldAssociations, newAssociations) {
var self = this
destroyObsoleteAssociations.call(this, oldAssociations, newAssociations)
.error(function(err) { emitter.emit('failure', err) })
.success(function() {
var chainer = new Utils.QueryChainer
, association = self.__factory.target.associations[self.__factory.associationAccessor]
, foreignIdentifier = association.isSelfAssociation ? association.foreignIdentifier : association.identifier
, unassociatedObjects = newAssociations.filter(function(obj) { return !obj.equalsOneOf(oldAssociations) })
unassociatedObjects.forEach(function(unassociatedObject) {
var attributes = {}
attributes[self.__factory.identifier] = self.instance.id
attributes[foreignIdentifier] = unassociatedObject.id
chainer.add(self.__factory.connectorModel.create(attributes))
})
chainer
.run()
.success(function() { emitter.emit('success', newAssociations) })
.error(function(err) { emitter.emit('failure', err) })
})
}
// private
chainer
.run()
.on('success', function() { emitter.emit('success', newAssociations) })
.on('failure', function(err) { emitter.emit('failure', err) })
var destroyObsoleteAssociations = function(oldAssociations, newAssociations) {
var self = this
var emitter = new Utils.CustomEventEmitter(function() {
var chainer = new Utils.QueryChainer
var foreignIdentifier = self.__factory.target.associations[self.__factory.associationAccessor].identifier
var obsoleteAssociations = oldAssociations.filter(function(obj) { return !obj.equalsOneOf(newAssociations) })
if(obsoleteAssociations.length == 0)
return emitter.emit('success', null)
obsoleteAssociations.forEach(function(associatedObject) {
var where = {}
, primaryKeys = Utils._.keys(self.__factory.connectorModel.rawAttributes)
, foreignKey = primaryKeys.filter(function(pk) { return pk != self.__factory.identifier })[0]
where[self.__factory.identifier] = self.instance.id
where[foreignKey] = associatedObject.id
self.__factory.connectorModel.find({where: where}).success(function(connector) {
chainer.add(connector.destroy())
if(chainer.emitters.length == obsoleteAssociations.length) {
// found all obsolete connectors and will delete them now
chainer
.run()
.success(function() { emitter.emit('success', null) })
.error(function(err) { emitter.emit('failure', err) })
}
})
})
})
}
return emitter.run()
}
return HasManyDoubleLinked
})()
var Utils = require('./../utils')
var HasManySingleLinked = module.exports = function(definition, instance) {
this.__definition = definition
this.instance = instance
}
module.exports = (function() {
var HasManySingleLinked = function(definition, instance) {
this.__factory = definition
this.instance = instance
}
HasManySingleLinked.prototype.injectGetter = function() {
var where = {}
HasManySingleLinked.prototype.injectGetter = function() {
var where = {}
where[this.__definition.identifier] = this.instance.id
return this.__definition.target.findAll({where: where})
}
where[this.__factory.identifier] = this.instance.id
return this.__factory.target.findAll({where: where})
}
HasManySingleLinked.prototype.injectSetter = function(emitter, oldAssociations, newAssociations) {
var self = this
HasManySingleLinked.prototype.injectSetter = function(emitter, oldAssociations, newAssociations) {
var self = this
// clear the old associations
oldAssociations.forEach(function(associatedObject) {
associatedObject[self.__definition.identifier] = null
associatedObject.save()
})
// clear the old associations
oldAssociations.forEach(function(associatedObject) {
associatedObject[self.__factory.identifier] = null
associatedObject.save()
})
// set the new one
var chainer = new Utils.QueryChainer
newAssociations.forEach(function(associatedObject) {
associatedObject[self.__definition.identifier] = self.instance.id
chainer.add(associatedObject.save())
})
chainer
.run()
.on('success', function() { emitter.emit('success', newAssociations) })
.on('failure', function(err) { emitter.emit('failure', err) })
}
// set the new one
var chainer = new Utils.QueryChainer
newAssociations.forEach(function(associatedObject) {
associatedObject[self.__factory.identifier] = self.instance.id
chainer.add(associatedObject.save())
})
chainer
.run()
.success(function() { emitter.emit('success', newAssociations) })
.error(function(err) { emitter.emit('failure', err) })
}
return HasManySingleLinked
})()
......@@ -4,124 +4,128 @@ var Utils = require("./../utils")
var HasManySingleLinked = require("./has-many-single-linked")
, HasManyMultiLinked = require("./has-many-double-linked")
var HasMany = module.exports = function(srcModel, targetModel, options) {
this.source = srcModel
this.target = targetModel
this.options = options
this.isSelfAssociation = (this.source.tableName == this.target.tableName)
this.associationAccessor = this.combinedName = this.options.joinTableName || Utils.combineTableNames(
this.source.tableName,
this.isSelfAssociation ? (this.options.as || this.target.tableName) : this.target.tableName
)
var as = (this.options.as || Utils.pluralize(this.target.tableName))
this.accessors = {
get: Utils._.camelize('get_' + as),
set: Utils._.camelize('set_' + as),
add: Utils._.camelize(Utils.singularize('add_' + as)),
remove: Utils._.camelize(Utils.singularize('remove_' + as))
module.exports = (function() {
var HasMany = function(srcModel, targetModel, options) {
this.source = srcModel
this.target = targetModel
this.options = options
this.isSelfAssociation = (this.source.tableName == this.target.tableName)
this.associationAccessor = this.combinedName = this.options.joinTableName || Utils.combineTableNames(
this.source.tableName,
this.isSelfAssociation ? (this.options.as || this.target.tableName) : this.target.tableName
)
var as = (this.options.as || Utils.pluralize(this.target.tableName))
this.accessors = {
get: Utils._.camelize('get_' + as),
set: Utils._.camelize('set_' + as),
add: Utils._.camelize(Utils.singularize('add_' + as)),
remove: Utils._.camelize(Utils.singularize('remove_' + as))
}
}
}
// the id is in the target table
// or in an extra table which connects two tables
HasMany.prototype.injectAttributes = function() {
var multiAssociation = this.target.associations.hasOwnProperty(this.associationAccessor)
this.identifier = this.options.foreignKey || Utils._.underscoredIf(Utils.singularize(this.source.tableName) + "Id", this.options.underscored)
// is there already a single sided association between the source and the target?
// or is the association on the model itself?
if (this.isSelfAssociation || multiAssociation) {
// remove the obsolete association identifier from the source
if(this.isSelfAssociation) {
this.foreignIdentifier = Utils._.underscoredIf((this.options.as || this.target.tableName) + 'Id', this.options.underscored)
// the id is in the target table
// or in an extra table which connects two tables
HasMany.prototype.injectAttributes = function() {
var multiAssociation = this.target.associations.hasOwnProperty(this.associationAccessor)
this.identifier = this.options.foreignKey || Utils._.underscoredIf(Utils.singularize(this.source.tableName) + "Id", this.options.underscored)
// is there already a single sided association between the source and the target?
// or is the association on the model itself?
if (this.isSelfAssociation || multiAssociation) {
// remove the obsolete association identifier from the source
if(this.isSelfAssociation) {
this.foreignIdentifier = Utils._.underscoredIf((this.options.as || this.target.tableName) + 'Id', this.options.underscored)
} else {
this.foreignIdentifier = this.target.associations[this.associationAccessor].identifier
delete this.source.attributes[this.foreignIdentifier]
}
// define a new model, which connects the models
var combinedTableAttributes = {}
combinedTableAttributes[this.identifier] = {type:DataTypes.INTEGER, primaryKey: true}
combinedTableAttributes[this.foreignIdentifier] = {type:DataTypes.INTEGER, primaryKey: true}
this.connectorModel = this.source.modelFactoryManager.sequelize.define(this.combinedName, combinedTableAttributes, this.options)
if(!this.isSelfAssociation) this.target.associations[this.associationAccessor].connectorModel = this.connectorModel
this.connectorModel.sync()
} else {
this.foreignIdentifier = this.target.associations[this.associationAccessor].identifier
delete this.source.attributes[this.foreignIdentifier]
var newAttributes = {}
newAttributes[this.identifier] = { type: DataTypes.INTEGER }
Utils._.extend(this.target.rawAttributes, newAttributes)
}
// define a new model, which connects the models
var combinedTableAttributes = {}
combinedTableAttributes[this.identifier] = {type:DataTypes.INTEGER, primaryKey: true}
combinedTableAttributes[this.foreignIdentifier] = {type:DataTypes.INTEGER, primaryKey: true}
this.connectorModel = this.source.modelManager.sequelize.define(this.combinedName, combinedTableAttributes, this.options)
if(!this.isSelfAssociation) this.target.associations[this.associationAccessor].connectorModel = this.connectorModel
this.connectorModel.sync()
} else {
var newAttributes = {}
newAttributes[this.identifier] = { type: DataTypes.INTEGER }
Utils._.extend(this.target.attributes, Utils.simplifyAttributes(newAttributes))
return this
}
return this
}
HasMany.prototype.injectGetter = function(obj) {
var self = this
HasMany.prototype.injectGetter = function(obj) {
var self = this
obj[this.accessors.get] = function() {
var Class = self.connectorModel ? HasManyMultiLinked : HasManySingleLinked
return new Class(self, this).injectGetter()
obj[this.accessors.get] = function() {
var Class = self.connectorModel ? HasManyMultiLinked : HasManySingleLinked
return new Class(self, this).injectGetter()
}
return this
}
return this
}
HasMany.prototype.injectSetter = function(obj) {
var self = this
HasMany.prototype.injectSetter = function(obj) {
var self = this
obj[this.accessors.set] = function(newAssociatedObjects) {
var instance = this
obj[this.accessors.set] = function(newAssociatedObjects) {
var instance = this
// define the returned customEventEmitter, which will emit the success event once everything is done
var customEventEmitter = new Utils.CustomEventEmitter(function() {
instance[self.accessors.get]().on('success', function(oldAssociatedObjects) {
var Class = self.connectorModel ? HasManyMultiLinked : HasManySingleLinked
new Class(self, instance).injectSetter(customEventEmitter, oldAssociatedObjects, newAssociatedObjects)
})
})
return customEventEmitter.run()
}
obj[this.accessors.add] = function(newAssociatedObject) {
var instance = this
var customEventEmitter = new Utils.CustomEventEmitter(function() {
instance[self.accessors.get]()
.on('failure', function(err){ customEventEmitter.emit('failure', err)})
.on('success', function(currentAssociatedObjects) {
if(!newAssociatedObject.equalsOneOf(currentAssociatedObjects))
currentAssociatedObjects.push(newAssociatedObject)
instance[self.accessors.set](currentAssociatedObjects)
.on('success', function(instances) { customEventEmitter.emit('success', instances) })
.on('failure', function(err) { customEventEmitter.emit('failure', err) })
})
})
return customEventEmitter.run()
}
obj[this.accessors.remove] = function(oldAssociatedObject) {
var instance = this
var customEventEmitter = new Utils.CustomEventEmitter(function() {
instance[self.accessors.get]().on('success', function(currentAssociatedObjects) {
var newAssociations = []
currentAssociatedObjects.forEach(function(association) {
if(!Utils._.isEqual(oldAssociatedObject.identifiers, association.identifiers))
newAssociations.push(association)
// define the returned customEventEmitter, which will emit the success event once everything is done
var customEventEmitter = new Utils.CustomEventEmitter(function() {
instance[self.accessors.get]().success(function(oldAssociatedObjects) {
var Class = self.connectorModel ? HasManyMultiLinked : HasManySingleLinked
new Class(self, instance).injectSetter(customEventEmitter, oldAssociatedObjects, newAssociatedObjects)
})
})
return customEventEmitter.run()
}
obj[this.accessors.add] = function(newAssociatedObject) {
var instance = this
var customEventEmitter = new Utils.CustomEventEmitter(function() {
instance[self.accessors.get]()
.error(function(err){ customEventEmitter.emit('failure', err)})
.success(function(currentAssociatedObjects) {
if(!newAssociatedObject.equalsOneOf(currentAssociatedObjects))
currentAssociatedObjects.push(newAssociatedObject)
instance[self.accessors.set](currentAssociatedObjects)
.success(function(instances) { customEventEmitter.emit('success', instances) })
.error(function(err) { customEventEmitter.emit('failure', err) })
})
})
return customEventEmitter.run()
}
obj[this.accessors.remove] = function(oldAssociatedObject) {
var instance = this
var customEventEmitter = new Utils.CustomEventEmitter(function() {
instance[self.accessors.get]().success(function(currentAssociatedObjects) {
var newAssociations = []
instance[self.accessors.set](newAssociations)
.on('success', function() { customEventEmitter.emit('success', null) })
.on('failure', function(err) { customEventEmitter.emit('failure', err) })
currentAssociatedObjects.forEach(function(association) {
if(!Utils._.isEqual(oldAssociatedObject.identifiers, association.identifiers))
newAssociations.push(association)
})
instance[self.accessors.set](newAssociations)
.success(function() { customEventEmitter.emit('success', null) })
.error(function(err) { customEventEmitter.emit('failure', err) })
})
})
})
return customEventEmitter.run()
return customEventEmitter.run()
}
return this
}
return this
}
\ No newline at end of file
return HasMany
})()
var Utils = require("./../utils")
, DataTypes = require('./../data-types')
var HasOne = module.exports = function(srcModel, targetModel, options) {
this.source = srcModel
this.target = targetModel
this.options = options
this.isSelfAssociation = (this.source.tableName == this.target.tableName)
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)
: this.target.tableName
this.accessors = {
get: Utils._.camelize('get_' + (this.options.as || Utils.singularize(this.target.tableName))),
set: Utils._.camelize('set_' + (this.options.as || Utils.singularize(this.target.tableName)))
module.exports = (function() {
var HasOne = function(srcModel, targetModel, options) {
this.source = srcModel
this.target = targetModel
this.options = options
this.isSelfAssociation = (this.source.tableName == this.target.tableName)
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)
: this.target.tableName
this.accessors = {
get: Utils._.camelize('get_' + (this.options.as || Utils.singularize(this.target.tableName))),
set: Utils._.camelize('set_' + (this.options.as || Utils.singularize(this.target.tableName)))
}
}
// the id is in the target table
HasOne.prototype.injectAttributes = function() {
var newAttributes = {}
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)
return this
}
}
// the id is in the target table
HasOne.prototype.injectAttributes = function() {
var newAttributes = {}
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.attributes, Utils.simplifyAttributes(newAttributes))
return this
}
HasOne.prototype.injectGetter = function(obj) {
var self = this
obj[this.accessors.get] = function() {
var id = obj.id
, where = {}
where[self.identifier] = id
return self.target.find({where: where})
HasOne.prototype.injectGetter = function(obj) {
var self = this
obj[this.accessors.get] = function() {
var id = obj.id
, where = {}
where[self.identifier] = id
return self.target.find({where: where})
}
return this
}
return this
}
HasOne.prototype.injectSetter = function(obj) {
var self = this
obj[this.accessors.set] = function(associatedObject) {
var customEventEmitter = new Utils.CustomEventEmitter(function() {
obj[self.accessors.get]().on('success', function(oldObj) {
if(oldObj) {
oldObj[self.identifier] = null
oldObj.save()
}
associatedObject[self.identifier] = obj.id
associatedObject.save()
.on('success', function() { customEventEmitter.emit('success', associatedObject) })
.on('failure', function(err) { customEventEmitter.emit('failure', err) })
HasOne.prototype.injectSetter = function(obj) {
var self = this
obj[this.accessors.set] = function(associatedObject) {
var customEventEmitter = new Utils.CustomEventEmitter(function() {
obj[self.accessors.get]().success(function(oldObj) {
if(oldObj) {
oldObj[self.identifier] = null
oldObj.save()
}
associatedObject[self.identifier] = obj.id
associatedObject.save()
.success(function() { customEventEmitter.emit('success', associatedObject) })
.error(function(err) { customEventEmitter.emit('failure', err) })
})
})
})
return customEventEmitter.run()
return customEventEmitter.run()
}
return this
}
return this
}
\ No newline at end of file
return HasOne
})()
var Utils = require("./../utils")
var Utils = require("./../utils")
, HasOne = require('./has-one')
, HasMany = require("./has-many")
, BelongsTo = require("./belongs-to")
/*
Defines Mixin for all models.
*/
var Associations = module.exports = {
classMethods: {
hasOne: function(associatedModel, options) {
// the id is in the foreign table
var HasOne = require('./has-one')
var association = new HasOne(this, associatedModel, Utils._.extend((options||{}), this.options))
this.associations[association.associationAccessor] = association.injectAttributes()
},
belongsTo: function(associatedModel, options) {
// the id is in this table
var BelongsTo = require("./belongs-to")
var association = new BelongsTo(this, associatedModel, Utils._.extend((options||{}), this.options))
this.associations[association.associationAccessor] = association.injectAttributes()
},
hasMany: function(associatedModel, options) {
// the id is in the foreign table or in a connecting table
var HasMany = require("./has-many")
var association = new HasMany(this, associatedModel, Utils._.extend((options||{}), this.options))
this.associations[association.associationAccessor] = association.injectAttributes()
}
},
instanceMethods: {
}
/* Defines Mixin for all models. */
var Mixin = module.exports = function(){}
Mixin.hasOne = function(associatedModel, options) {
// the id is in the foreign table
var association = new HasOne(this, associatedModel, Utils._.extend((options||{}), this.options))
this.associations[association.associationAccessor] = association.injectAttributes()
return this
}
Mixin.belongsTo = function(associatedModel, options) {
// the id is in this table
var association = new BelongsTo(this, associatedModel, Utils._.extend((options||{}), this.options))
this.associations[association.associationAccessor] = association.injectAttributes()
return this
}
Mixin.hasMany = function(associatedModel, options) {
// the id is in the foreign table or in a connecting table
var association = new HasMany(this, associatedModel, Utils._.extend((options||{}), this.options))
this.associations[association.associationAccessor] = association.injectAttributes()
return this
}
/* example for instance methods:
Mixin.prototype.test = function() {
console.log('asd')
}
*/
module.exports = {
STRING: 'VARCHAR(255)',
TEXT: 'TEXT',
INTEGER: 'INT',
INTEGER: 'INTEGER',
DATE: 'DATETIME',
BOOLEAN: 'TINYINT(1)',
FLOAT: 'FLOAT'
}
\ No newline at end of file
}
module.exports = (function(){
var ConnectorManager = function(sequelize, config) {
throw new Error('Define the constructor!')
}
ConnectorManager.prototype.query = function(sql, callee, options) {
throw new Error('Define the query method!')
}
ConnectorManager.prototype.connect = function() {
throw new Error('Define the connect method!')
}
ConnectorManager.prototype.disconnect = function() {
throw new Error('Define the disconnect method!')
}
ConnectorManager.prototype.reconnect = function() {
this.disconnect()
this.connect()
}
return ConnectorManager
})()
var Query = require("./query")
, Utils = require("../../utils")
, without = function(arr, elem) { return arr.filter(function(e) { return e != elem }) }
var ConnectorManager = module.exports = function(config) {
this.client = null
this.config = config
this.disconnectTimeoutId = null
this.queue = []
this.activeQueue = []
this.maxConcurrentQueries = (this.config.maxConcurrentQueries || 50)
}
ConnectorManager.prototype.connect = function() {
var self = this
this.client = require("mysql").createClient({
user: this.config.username,
password: this.config.password,
host: this.config.host,
port: this.config.port,
database: this.config.database
})
module.exports = (function() {
var ConnectorManager = function(sequelize, config) {
this.sequelize = sequelize
this.client = null
this.config = config || {}
this.disconnectTimeoutId = null
this.queue = []
this.activeQueue = []
this.maxConcurrentQueries = (this.config.maxConcurrentQueries || 50)
}
Utils._.extend(ConnectorManager.prototype, require("../connector-manager").prototype)
this.client.setMaxListeners(this.maxConcurrentQueries)
}
ConnectorManager.prototype.query = function(sql, callee, options) {
if(!this.isConnected) this.connect()
ConnectorManager.prototype.query = function(sql, callee, options) {
if(!this.isConnected) this.connect()
var queueItem = {
query: new Query(this.client, callee, options || {}),
sql: sql
}
var queueItem = {
query: new Query(this.client, callee, options || {}),
sql: sql
enqueue.call(this, queueItem)
return queueItem.query
}
this._enqueue(queueItem)
ConnectorManager.prototype.connect = function() {
var self = this
return queueItem.query
}
this.client = require("mysql").createClient({
user: this.config.username,
password: this.config.password,
host: this.config.host,
port: this.config.port,
database: this.config.database
})
ConnectorManager.prototype.disconnect = function() {
var self = this
this.client.end(function() { self.client = null })
}
this.client.setMaxListeners(this.maxConcurrentQueries)
}
ConnectorManager.prototype.reconnect = function() {
this.disconnect()
this.connect()
}
ConnectorManager.prototype.disconnect = function() {
var self = this
this.client.end(function() { self.client = null })
}
// private
// private
var enqueue = function(queueItem) {
if(this.activeQueue.length < this.maxConcurrentQueries) {
this.activeQueue.push(queueItem)
execQueueItem.call(this, queueItem)
} else {
this.queue.push(queueItem)
}
}
ConnectorManager.prototype._enqueue = function(queueItem) {
if(this.activeQueue.length < this.maxConcurrentQueries) {
this.activeQueue.push(queueItem)
this._execQueueItem(queueItem)
} else {
this.queue.push(queueItem)
var dequeue = function(queueItem) {
this.activeQueue = without(this.activeQueue, queueItem)
}
}
ConnectorManager.prototype._dequeue = function(queueItem) {
this.activeQueue = without(this.activeQueue, queueItem)
}
ConnectorManager.prototype._transferQueuedItems = function(count) {
for(var i = 0; i < count; i++) {
var queueItem = this.queue[0]
if(queueItem) {
this._enqueue(queueItem)
this.queue = without(this.queue, queueItem)
var transferQueuedItems = function(count) {
for(var i = 0; i < count; i++) {
var queueItem = this.queue[0]
if(queueItem) {
enqueue.call(this, queueItem)
this.queue = without(this.queue, queueItem)
}
}
}
}
ConnectorManager.prototype._afterQuery = function(queueItem) {
var self = this
var afterQuery = function(queueItem) {
var self = this
dequeue.call(this, queueItem)
transferQueuedItems.call(this, this.maxConcurrentQueries - this.activeQueue.length)
disconnectIfNoConnections.call(this)
}
this._dequeue(queueItem)
this._transferQueuedItems(this.maxConcurrentQueries - this.activeQueue.length)
this._disconnectIfNoConnections()
}
var execQueueItem = function(queueItem) {
var self = this
queueItem.query
.success(function(){ afterQuery.call(self, queueItem) })
.error(function(){ afterQuery.call(self, queueItem) })
ConnectorManager.prototype._execQueueItem = function(queueItem) {
var self = this
queueItem.query.run(queueItem.sql)
}
queueItem.query
.on('success', function(){ self._afterQuery(queueItem) })
.on('failure', function(){ self._afterQuery(queueItem) })
ConnectorManager.prototype.__defineGetter__('hasNoConnections', function() {
return (this.queue.length == 0) && (this.activeQueue.length == 0) && this.client._queue && (this.client._queue.length == 0)
})
queueItem.query.run(queueItem.sql)
}
ConnectorManager.prototype.__defineGetter__('isConnected', function() {
return this.client != null
})
ConnectorManager.prototype.__defineGetter__('hasNoConnections', function() {
return (this.queue.length == 0) && (this.activeQueue.length == 0) && this.client._queue && (this.client._queue.length == 0)
})
var disconnectIfNoConnections = function() {
var self = this
ConnectorManager.prototype.__defineGetter__('isConnected', function() {
return this.client != null
})
this.disconnectTimeoutId && clearTimeout(this.disconnectTimeoutId)
this.disconnectTimeoutId = setTimeout(function() {
self.isConnected && self.hasNoConnections && self.disconnect()
}, 100)
}
ConnectorManager.prototype._disconnectIfNoConnections = function() {
var self = this
this.disconnectTimeoutId && clearTimeout(this.disconnectTimeoutId)
this.disconnectTimeoutId = setTimeout(function() {
self.isConnected && self.hasNoConnections && self.disconnect()
}, 100)
}
return ConnectorManager
})()
var Utils = require("../../utils")
module.exports = (function() {
var Query = function(client, callee, options) {
var self = this
this.client = client
this.callee = callee
this.options = Utils._.extend({
logging: true,
plain: false,
raw: false
}, options || {})
this.bindClientFunction = function(err) { onFailure.call(self, err) }
}
Utils._.extend(Query.prototype, require("../query").prototype)
Query.prototype.run = function(sql) {
var self = this
this.sql = sql
bindClient.call(this)
if(this.options.logging)
console.log('Executing: ' + this.sql)
this.client.query(this.sql, function(err, results, fields) {
//allow clients to listen to sql to do their own logging or whatnot
self.emit('sql', self.sql)
err ? onFailure.call(self, err) : onSuccess.call(self, results, fields)
}).setMaxListeners(100)
return this
}
//private
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
} else {
result = results.map(function(result) {
return self.callee.build(result, { isNewRecord: false })
})
}
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('failure', err, this.callee)
}
return Query
})()
module.exports = (function() {
var QueryGenerator = {
/*
Returns a query for creating a table.
Parameters:
- tableName: Name of the new table.
- attributes: An object with containing attribute-attributeType-pairs.
Attributes should have the format:
{attributeName: type, attr2: type2}
--> e.g. {title: 'VARCHAR(255)'}
- options: An object with options.
Defaults: { engine: 'InnoDB', charset: null }
*/
createTableQuery: function(tableName, attributes, options) {
throwMethodUndefined('createTableQuery')
},
/*
Returns a query for dropping a table.
*/
dropTableQuery: function(tableName, options) {
throwMethodUndefined('dropTableQuery')
},
/*
Returns a rename table query.
Parameters:
- originalTableName: Name of the table before execution.
- futureTableName: Name of the table after execution.
*/
renameTableQuery: function(originalTableName, futureTableName) {
throwMethodUndefined('renameTableQuery')
},
/*
Returns a query, which gets all available table names in the database.
*/
showTablesQuery: function() {
throwMethodUndefined('showTablesQuery')
},
/*
Returns a query, which adds an attribute to an existing table.
Parameters:
- tableName: Name of the existing table.
- attributes: A hash with attribute-attributeOptions-pairs.
- key: attributeName
- value: A hash with attribute specific options:
- type: DataType
- defaultValue: A String with the default value
- allowNull: Boolean
*/
addColumnQuery: function(tableName, attributes) {
throwMethodUndefined('addColumnQuery')
},
/*
Returns a query, which removes an attribute from an existing table.
Parameters:
- tableName: Name of the existing table
- attributeName: Name of the obsolete attribute.
*/
removeColumnQuery: function(tableName, attributeName) {
throwMethodUndefined('removeColumnQuery')
},
/*
Returns a query, which modifies an existing attribute from a table.
Parameters:
- tableName: Name of the existing table.
- attributes: A hash with attribute-attributeOptions-pairs.
- key: attributeName
- value: A hash with attribute specific options:
- type: DataType
- defaultValue: A String with the default value
- allowNull: Boolean
*/
changeColumnQuery: function(tableName, attributes) {
throwMethodUndefined('changeColumnQuery')
},
/*
Returns a query, which renames an existing attribute.
Parameters:
- tableName: Name of an existing table.
- attrNameBefore: The name of the attribute, which shall be renamed.
- attrNameAfter: The name of the attribute, after renaming.
*/
renameColumnQuery: function(tableName, attrNameBefore, attrNameAfter) {
throwMethodUndefined('renameColumnQuery')
},
/*
Returns a query for selecting elements in the table <tableName>.
Options:
- attributes -> An array of attributes (e.g. ['name', 'birthday']). Default: *
- where -> A hash with conditions (e.g. {name: 'foo'})
OR an ID as integer
OR a string with conditions (e.g. 'name="foo"').
If you use a string, you have to escape it on your own.
- order -> e.g. 'id DESC'
- group
- limit -> The maximum count you want to get.
- offset -> An offset value to start from. Only useable with limit!
*/
selectQuery: function(tableName, options) {
throwMethodUndefined('selectQuery')
},
/*
Returns an insert into command. Parameters: table name + hash of attribute-value-pairs.
*/
insertQuery: function(tableName, attrValueHash) {
throwMethodUndefined('insertQuery')
},
/*
Returns an update query.
Parameters:
- tableName -> Name of the table
- values -> A hash with attribute-value-pairs
- where -> A hash with conditions (e.g. {name: 'foo'})
OR an ID as integer
OR a string with conditions (e.g. 'name="foo"').
If you use a string, you have to escape it on your own.
*/
updateQuery: function(tableName, values, where) {
throwMethodUndefined('updateQuery')
},
/*
Returns a deletion query.
Parameters:
- tableName -> Name of the table
- where -> A hash with conditions (e.g. {name: 'foo'})
OR an ID as integer
OR a string with conditions (e.g. 'name="foo"').
If you use a string, you have to escape it on your own.
Options:
- limit -> Maximaum count of lines to delete
*/
deleteQuery: function(tableName, where, options) {
throwMethodUndefined('deleteQuery')
},
/*
Returns an add index query.
Parameters:
- tableName -> Name of an existing table.
- attributes:
An array of attributes as string or as hash.
If the attribute is a hash, it must have the following content:
- attribute: The name of the attribute/column
- length: An integer. Optional
- order: 'ASC' or 'DESC'. Optional
- options:
- indicesType: UNIQUE|FULLTEXT|SPATIAL
- indexName: The name of the index. Default is <tableName>_<attrName1>_<attrName2>
- parser
*/
addIndexQuery: function(tableName, attributes, options) {
throwMethodUndefined('addIndexQuery')
},
/*
Returns an show index query.
Parameters:
- tableName: Name of an existing table.
- options:
- database: Name of the database.
*/
showIndexQuery: function(tableName, options) {
throwMethodUndefined('showIndexQuery')
},
/*
Returns a remove index query.
Parameters:
- tableName: Name of an existing table.
- indexNameOrAttributes: The name of the index as string or an array of attribute names.
*/
removeIndexQuery: function(tableName, indexNameOrAttributes) {
throwMethodUndefined('removeIndexQuery')
},
/*
Takes something and transforms it into values of a where condition.
*/
getWhereConditions: function(smth) {
throwMethodUndefined('getWhereConditions')
},
/*
Takes a hash and transforms it into a mysql where condition: {key: value, key2: value2} ==> key=value AND key2=value2
The values are transformed by the relevant datatype.
*/
hashToWhereConditions: function(hash) {
throwMethodUndefined('hashToWhereConditions')
},
/*
This method transforms an array of attribute hashes into equivalent
sql attribute definition.
*/
attributesToSQL: function(attributes) {
throwMethodUndefined('attributesToSQL')
},
/*
Returns all auto increment fields of a factory.
*/
findAutoIncrementField: function(factory) {
throwMethodUndefined('findAutoIncrementField')
}
}
var throwMethodUndefined = function(methodName) {
throw new Error('The method "' + methodName + '" is not defined! Please add it to your sql dialect.')
}
return QueryGenerator
})()
var Utils = require("../utils")
module.exports = (function() {
var Query = function(database, callee, options) {
throw new Error('Constructor was not overwritten!')
}
Utils.addEventEmitter(Query)
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('failure', fct)
return this
}
return Query
})()
var Utils = require("../../utils")
, sqlite3 = require('sqlite3').verbose()
, Query = require("./query")
module.exports = (function() {
var ConnectorManager = function(sequelize) {
this.sequelize = sequelize
this.database = new sqlite3.Database(sequelize.options.storage || ':memory:')
}
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 ConnectorManager
})()
var Utils = require("../../utils")
, util = require("util")
module.exports = (function() {
var QueryGenerator = {
createTableQuery: function(tableName, attributes, options) {
options = options || {}
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 : "")
}
return Utils._.template(query)(values).trim() + ";"
},
showTablesQuery: function() {
return "SELECT name FROM sqlite_master WHERE type='table';"
},
deleteQuery: function(tableName, where, options) {
options = options || {}
var query = "DELETE FROM <%= table %> WHERE <%= where %>"
var replacements = {
table: Utils.addTicks(tableName),
where: this.getWhereConditions(where),
limit: Utils.escape(options.limit)
}
return Utils._.template(query)(replacements)
},
attributesToSQL: function(attributes) {
var result = {}
Utils._.map(attributes, function(dataType, name) {
if(Utils.isHash(dataType)) {
var template = "<%= type %>"
, replacements = { type: dataType.type }
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"
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
})
return Utils._.compact(fields)
}
}
var MySqlQueryGenerator = Utils._.extend(
Utils._.clone(require("../query-generator")),
Utils._.clone(require("../mysql/query-generator"))
)
return Utils._.extend(MySqlQueryGenerator, QueryGenerator)
})()
var Utils = require("../../utils")
module.exports = (function() {
var Query = function(database, callee, options) {
var self = this
this.database = database
this.callee = callee
this.options = Utils._.extend({
logging: true,
plain: false,
raw: false
}, options || {})
}
Utils._.extend(Query.prototype, require("../query").prototype)
Query.prototype.run = function(sql) {
var self = this
this.sql = sql
if(this.options.logging)
console.log('Executing: ' + this.sql)
this.database.serialize(function() {
var isInsertCommand = (self.sql.toLowerCase().indexOf('insert') == 0)
, isUpdateCommand = (self.sql.toLowerCase().indexOf('update') == 0)
, databaseMethod = (isInsertCommand || isUpdateCommand) ? 'run' : 'all'
self.database[databaseMethod](self.sql, function(err, results) {
err ? onFailure.call(self, err) : onSuccess.call(self, results, this)
})
})
return this
}
//private
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.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)
if(this.options.raw) {
result = results
} else {
result = results.map(function(result) {
return self.callee.build(result, { isNewRecord: false })
})
}
if(this.options.plain)
result = (result.length == 0) ? null : result[0]
} else if((this.sql.indexOf('SHOW') == 0) || (this.sql.indexOf('DESCRIBE') == 0))
result = results
this.emit('success', result)
}
var onFailure = function(err) {
this.emit('failure', err, this.callee)
}
return Query
})()
var util = require("util")
, EventEmitter = require("events").EventEmitter
module.exports = (function() {
var CustomEventEmitter = function(fct) {
this.fct = fct
}
util.inherits(CustomEventEmitter, EventEmitter)
CustomEventEmitter.prototype.run = function() {
var self = this
// delay the function call and return the emitter
setTimeout(function(){
self.fct.call(self, self)
}, 5)
return this
}
CustomEventEmitter.prototype.success = CustomEventEmitter.prototype.ok = function(fct) {
this.on('success', fct)
return this
}
CustomEventEmitter.prototype.failure =
CustomEventEmitter.prototype.fail =
CustomEventEmitter.prototype.error =
function(fct) {
this.on('failure', fct)
return this
}
return CustomEventEmitter
})()
var Utils = require("./utils")
var NullEmitter = module.exports = function(delay) {
var self = this
delay = delay || 10
setTimeout(function() { self.emitNull() }, delay)
}
Utils.addEventEmitter(NullEmitter)
NullEmitter.prototype.emitNull = function() {
this.emit('success', null)
this.emit('failure', null)
}
\ No newline at end of file
var Utils = require("../utils")
module.exports = (function(){
var NullEmitter = function(delay) {
var self = this
delay = delay || 10
setTimeout(function() { self.emitNull() }, delay)
}
Utils.addEventEmitter(NullEmitter)
NullEmitter.prototype.emitNull = function() {
this.emit('success', null)
this.emit('failure', null)
}
return NullEmitter
})()
var moment = require("moment")
, Utils = require("./utils")
, DataTypes = require("./data-types")
, QueryInterface = require("./query-interface")
module.exports = (function() {
var Migration = function(migrator, path) {
var split = path.split('/')
this.migrator = migrator
this.path = path
this.filename = Utils._.last(this.path.split('/'))
this.migrationId = parseInt(this.filename.match(/(.*)-.*/)[1])
this.date = Migration.stringToDate(this.filename)
this.queryInterface = this.migrator.queryInterface
this.undoneMethods = 0
}
///////////////
// static /////
///////////////
Migration.getFormattedDateString = function(s) {
var result = null
try {
result = s.match(/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/).slice(1, 6).join('-')
} catch(e) {
throw new Error(s + ' is no valid migration timestamp format! Use YYYYMMDDHHmmss!')
}
return result
}
Migration.stringToDate = function(s) {
return moment(Migration.getFormattedDateString(s), "YYYYMMDDHHmmss")
}
Migration.migrationHasInterfaceCalls = function(func) {
var functionString = Utils.removeCommentsFromFunctionString(func.toString())
, hasCalls = false
for(var method in QueryInterface.prototype) {
var regex = new RegExp('[\\s\\n\\r]*\\.[\\s\\n\\r]*' + method)
hasCalls = hasCalls || regex.test(functionString)
}
return hasCalls
}
///////////////
// member /////
///////////////
Object.defineProperty(Migration.prototype, 'migration', {
get: function() {
return require(this.path)
}
})
Migration.prototype.execute = function(options) {
var self = this
return new Utils.CustomEventEmitter(function(emitter) {
options = Utils._.extend({
method: 'up'
}, options || {})
var onSuccess = function() { emitter.emit('success', null) }
, func = self.migration[options.method]
extendMigrationWithQueryInterfaceMethods.call(self, onSuccess)
func.call(null, self, DataTypes)
if(!Migration.migrationHasInterfaceCalls(func))
onSuccess()
}).run()
}
Migration.prototype.isBefore = function(dateString, options) {
options = Utils._.extend({
withoutEquals: false
}, options || {})
var date = Migration.stringToDate(dateString.toString())
return options.withoutEqual ? (date > this.date) : (date >= this.date)
}
Migration.prototype.isAfter = function(dateString, options) {
options = Utils._.extend({
withoutEquals: false
}, options || {})
var date = Migration.stringToDate(dateString.toString())
return options.withoutEqual ? (date < this.date) : (date <= this.date)
}
// extends the Migration prototype with all methods of QueryInterface.prototype
// with additional tracking of start and finish. this is done in order to minimize
// asynchronous handling in migrations
var extendMigrationWithQueryInterfaceMethods = function(callback) {
var self = this
for(var method in QueryInterface.prototype) {
(function(_method) {
self[_method] = function() {
var emitter = self.QueryInterface
, args = Utils._.map(arguments, function(arg, _) { return arg })
self.undoneMethods++
// bind listeners to the query interface
// the event will have the same name like the method
self.queryInterface.on(_method, function(err) {
self.undoneMethods--
if(err)
throw new Error(err)
else
(self.undoneMethods == 0) && callback && callback()
})
self.queryInterface[_method].apply(self.queryInterface, args)
}
})(method)
}
}
return Migration
})()
const fs = require("fs")
, path = require("path")
, moment = require("moment")
var Utils = require("./utils")
, Migration = require("./migration")
, DataTypes = require("./data-types")
module.exports = (function() {
var Migrator = function(sequelize, options) {
this.sequelize = sequelize
this.options = Utils._.extend({
path: __dirname + '/../migrations',
from: null,
to: null,
logging: true
}, options || {})
}
Object.defineProperty(Migrator.prototype, "queryInterface", {
get: function() {
return this.sequelize.getQueryInterface()
}
})
Migrator.prototype.migrate = function(options) {
var self = this
options = Utils._.extend({
method: 'up'
}, options || {})
return new Utils.CustomEventEmitter(function(emitter) {
self.getUndoneMigrations(function(err, migrations) {
if(err) {
emitter.emit('failure', err)
} else {
var chainer = new Utils.QueryChainer
if(options.method == 'down')
migrations.reverse()
migrations.forEach(function(migration) {
chainer.add(migration, 'execute', [options], {
before: function(migration) {
if(self.options.logging)
console.log('Executing migration: ' + migration.filename)
},
after: function(migration) {
if(self.options.logging)
console.log('Executed migration: ' + migration.filename)
},
success: function(migration, callback) {
saveSuccessfulMigration.call(self, migrations[0], migration, callback)
}
})
})
chainer
.runSerially({ skipOnError: true })
.success(function() { emitter.emit('success', null) })
.error(function(err) { emitter.emit('failure', err) })
}
})
}).run()
}
Migrator.prototype.getUndoneMigrations = function(callback) {
var self = this
var filterFrom = function(migrations, from, callback, options) {
var result = migrations.filter(function(migration) { return migration.isAfter(from, options) })
callback && callback(null, result)
}
var filterTo = function(migrations, to, callback, options) {
var result = migrations.filter(function(migration) { return migration.isBefore(to, options) })
callback && callback(null, result)
}
var migrationFiles = fs.readdirSync(this.options.path)
var migrations = migrationFiles.map(function(file) {
return new Migration(self, self.options.path + '/' + file)
})
migrations = migrations.sort(function(a,b){
return parseInt(a.filename.split('-')[0]) - parseInt(b.filename.split('-')[0])
})
if(this.options.from) {
filterFrom(migrations, this.options.from, function(err, migrations) {
if(self.options.to)
filterTo(migrations, self.options.to, callback)
else
callback && callback(null, migrations)
})
} else {
getLastMigrationIdFromDatabase.call(this).success(function(lastMigrationId) {
if(lastMigrationId) {
filterFrom(migrations, lastMigrationId, function(err, migrations) {
if(self.options.to)
filterTo(migrations, self.options.to, callback)
else
callback && callback(null, migrations)
}, { withoutEqual: true })
} else {
if(self.options.to)
filterTo(migrations, self.options.to, callback)
else
callback && callback(null, migrations)
}
}).error(function(err) {
callback && callback(err, null)
})
}
}
Migrator.prototype.findOrCreateSequelizeMetaModel = function(syncOptions) {
var self = this
return new Utils.CustomEventEmitter(function(emitter) {
var storedModel = self.sequelize.modelFactoryManager.getModel('SequelizeMeta')
, SequelizeMeta = storedModel
if(!storedModel) {
SequelizeMeta = self.sequelize.define('SequelizeMeta', {
from: DataTypes.STRING,
to: DataTypes.STRING
})
}
// force sync when model has newly created or if syncOptions are passed
if(!storedModel || syncOptions) {
SequelizeMeta
.sync(syncOptions || {})
.success(function() { emitter.emit('success', SequelizeMeta) })
.error(function(err) { emitter.emit('failure', err) })
} else {
emitter.emit('success', SequelizeMeta)
}
}).run()
}
// private
var getLastMigrationFromDatabase = function() {
var self = this
return new Utils.CustomEventEmitter(function(emitter) {
self.findOrCreateSequelizeMetaModel().success(function(SequelizeMeta) {
SequelizeMeta.find({ order: 'id DESC' }).success(function(meta) {
emitter.emit('success', meta ? meta : null)
}).error(function(err) { emitter.emit('failure', err) })
}).error(function(err) { emitter.emit(err) })
}).run()
}
var getLastMigrationIdFromDatabase = function() {
var self = this
return new Utils.CustomEventEmitter(function(emitter) {
getLastMigrationFromDatabase.call(self)
.success(function(meta) {
emitter.emit('success', meta ? meta.to : null)
})
.error(function(err) {
emitter.emit('failure', err)
})
}).run()
}
var getFormattedDateString = function(s) {
var result = null
try {
result = s.match(/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/).slice(1, 6).join('-')
} catch(e) {
throw new Error(s + ' is no valid migration timestamp format! Use YYYYMMDDHHmmss!')
}
return result
}
var stringToDate = function(s) {
return moment(getFormattedDateString(s), "YYYYMMDDHHmmss")
}
var saveSuccessfulMigration = function(from, to, callback) {
var self = this
self.findOrCreateSequelizeMetaModel().success(function(SequelizeMeta) {
SequelizeMeta
.create({ from: from.migrationId, to: to.migrationId })
.success(callback)
})
}
return Migrator
})()
var ModelManager = module.exports = function(sequelize) {
this.models = []
this.sequelize = sequelize
}
module.exports = (function() {
var ModelFactoryManager = function(sequelize) {
this.models = []
this.sequelize = sequelize
}
ModelManager.prototype.addModel = function(model) {
model.modelManager = this
this.models.push(model)
return model
}
ModelFactoryManager.prototype.addModel = function(model) {
this.models.push(model)
ModelManager.prototype.removeModel = function(model) {
this.models = this.models.filter(function(_model) {
return _model.name != model.name
})
}
return model
}
ModelFactoryManager.prototype.removeModel = function(model) {
this.models = this.models.filter(function(_model) {
return _model.name != model.name
})
}
ModelFactoryManager.prototype.getModel = function(modelName) {
var model = this.models.filter(function(model) {
return model.name == modelName
})
return !!model ? model[0] : null
}
ModelManager.prototype.getModel = function(modelName) {
var model = this.models.filter(function(model) {
return model.name == modelName
ModelFactoryManager.prototype.__defineGetter__('all', function() {
return this.models
})
return !!model ? model[0] : null
}
ModelManager.prototype.__defineGetter__('all', function() {
return this.models
})
return ModelFactoryManager
})()
var Utils = require("./utils")
, Model = require("./model")
, DataTypes = require("./data-types")
module.exports = (function() {
var ModelFactory = function(name, attributes, options) {
var self = this
this.options = Utils._.extend({
timestamps: true,
instanceMethods: {},
classMethods: {},
validate: {},
freezeTableName: false,
underscored: false,
paranoid: false
}, options || {})
this.name = name
this.tableName = this.options.freezeTableName ? name : Utils.pluralize(name)
this.rawAttributes = attributes
this.modelFactoryManager = null // defined in init function
this.associations = {}
// extract validation
this.validate = this.options.validate || {}
}
Object.defineProperty(ModelFactory.prototype, 'attributes', {
get: function() {
return this.QueryGenerator.attributesToSQL(this.rawAttributes)
}
})
Object.defineProperty(ModelFactory.prototype, 'QueryInterface', {
get: function() { return this.modelFactoryManager.sequelize.getQueryInterface() }
})
Object.defineProperty(ModelFactory.prototype, 'QueryGenerator', {
get: function() { return this.QueryInterface.QueryGenerator }
})
Object.defineProperty(ModelFactory.prototype, 'primaryKeyCount', {
get: function() { return Utils._.keys(this.primaryKeys).length }
})
Object.defineProperty(ModelFactory.prototype, 'hasPrimaryKeys', {
get: function() { return this.primaryKeyCount > 0 }
})
ModelFactory.prototype.init = function(modelFactoryManager) {
this.modelFactoryManager = modelFactoryManager
addDefaultAttributes.call(this)
addOptionalClassMethods.call(this)
findAutoIncrementField.call(this)
return this
}
ModelFactory.prototype.sync = function(options) {
options = Utils.merge(options || {}, this.options)
var self = this
return new Utils.CustomEventEmitter(function(emitter) {
var doQuery = function() {
self.QueryInterface
.createTable(self.tableName, self.attributes, options)
.success(function() { emitter.emit('success', self) })
.error(function(err) { emitter.emit('failure', err) })
}
if(options.force)
self.drop().success(doQuery).error(function(err) { emitter.emit('failure', err) })
else
doQuery()
}).run()
}
ModelFactory.prototype.drop = function() {
return this.QueryInterface.dropTable(this.tableName)
}
ModelFactory.prototype.all = function() {
return this.findAll()
}
ModelFactory.prototype.findAll = function(options) {
return this.QueryInterface.select(this, this.tableName, options)
}
//right now, the caller (has-many-double-linked) is in charge of the where clause
ModelFactory.prototype.findAllJoin = function(joinTableName, options) {
optcpy = Utils._.clone(options)
optcpy.attributes = optcpy.attributes || [Utils.addTicks(this.tableName)+".*"]
return this.query(QueryGenerator.selectQuery([this.tableName, joinTableName], optcpy))
}
ModelFactory.prototype.find = function(options) {
if([null, undefined].indexOf(options) > -1) {
var NullEmitter = require("./emitters/null-emitter")
return new NullEmitter()
}
// options is not a hash but an id
if(typeof options == 'number')
options = { where: options }
else if (Utils.argsArePrimaryKeys(arguments, this.primaryKeys)) {
var where = {}
, self = this
Utils._.each(arguments, function(arg, i) {
var key = Utils._.keys(self.primaryKeys)[i]
where[key] = arg
})
options = { where: where }
}
options.limit = 1
return this.QueryInterface.select(this, this.tableName, options, {plain: true})
}
ModelFactory.prototype.count = function(options) {
options = Utils._.extend({ attributes: [] }, options || {})
options.attributes.push(['count(*)', 'count'])
return this.QueryInterface.rawSelect(this.tableName, options, 'count')
}
ModelFactory.prototype.max = function(field, options) {
options = Utils._.extend({ attributes: [] }, options || {})
options.attributes.push(['max(' + field + ')', 'max'])
return this.QueryInterface.rawSelect(this.tableName, options, 'max')
}
ModelFactory.prototype.min = function(field, options) {
options = Utils._.extend({ attributes: [] }, options || {})
options.attributes.push(['min(' + field + ')', 'min'])
return this.QueryInterface.rawSelect(this.tableName, options, 'min')
}
ModelFactory.prototype.build = function(values, options) {
var instance = new Model(values, Utils._.extend(this.options, this.attributes, { hasPrimaryKeys: this.hasPrimaryKeys }))
, self = this
options = options || {}
instance.__factory = this
Utils._.map(this.attributes, function(definition, name) {
if(typeof instance[name] == 'undefined') {
var value = null
if(self.rawAttributes.hasOwnProperty(name) && self.rawAttributes[name].hasOwnProperty('defaultValue')) {
value = 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)
})
instance.isNewRecord = options.hasOwnProperty('isNewRecord') ? options.isNewRecord : true
return instance
}
ModelFactory.prototype.create = function(values) {
return this.build(values).save()
}
ModelFactory.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.modelFactoryManager.sequelize
// add this as the second argument
if(arguments.length == 1) args.push(this)
return s.query.apply(s, args)
}
var addOptionalClassMethods = function() {
var self = this
Utils._.each(this.options.classMethods || {}, function(fct, name) { self[name] = fct })
}
var addDefaultAttributes = function() {
var self = this
, defaultAttributes = {
id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true
}
}
if(this.hasPrimaryKeys) defaultAttributes = {}
if(this.options.timestamps) {
defaultAttributes[Utils._.underscoredIf('createdAt', this.options.underscored)] = {type: DataTypes.DATE, allowNull: false}
defaultAttributes[Utils._.underscoredIf('updatedAt', this.options.underscored)] = {type: DataTypes.DATE, allowNull: false}
if(this.options.paranoid)
defaultAttributes[Utils._.underscoredIf('deletedAt', this.options.underscored)] = {type: DataTypes.DATE}
}
Utils._.each(defaultAttributes, function(value, attr) {
self.rawAttributes[attr] = value
})
}
var findAutoIncrementField = function() {
var self = this
, fields = this.QueryGenerator.findAutoIncrementField(this)
this.autoIncrementField = null
fields.forEach(function(field) {
if(self.autoIncrementField)
throw new Error('Invalid model definition. Only one autoincrement field allowed.')
else
self.autoIncrementField = field
})
}
Utils._.extend(ModelFactory.prototype, require("./associations/mixin"))
return ModelFactory
})()
var Utils = require("./utils")
, Mixin = require("./associations/mixin")
, Validator = require("validator")
module.exports = (function() {
var Model = function(values, options) {
var self = this
this.attributes = []
this.validators = {} // holds validation settings for each attribute
this.__factory = null // will be set in Model.build
this.__options = Utils._.extend({
underscored: false,
hasPrimaryKeys: false,
timestamps: true,
paranoid: false
}, options || {})
initAttributes.call(this, values)
}
Utils._.extend(Model.prototype, Mixin.prototype)
Object.defineProperty(Model.prototype, 'sequelize', {
get: function(){ return this.__factory.modelFactoryManager.sequelize }
})
Object.defineProperty(Model.prototype, 'QueryInterface', {
get: function(){ return this.sequelize.getQueryInterface() }
})
Object.defineProperty(Model.prototype, 'isDeleted', {
get: function() {
var result = this.__options.timestamps && this.__options.paranoid
result = result && this[this.__options.underscored ? 'deleted_at' : 'deletedAt'] != null
return result
}
})
Object.defineProperty(Model.prototype, 'values', {
get: function() {
var result = {}
, self = this
this.attributes.forEach(function(attr) {
result[attr] = self[attr]
})
return result
}
})
Object.defineProperty(Model.prototype, 'primaryKeyValues', {
get: function() {
var result = {}
, self = this
Utils._.each(this.__factory.primaryKeys, function(_, attr) {
result[attr] = self[attr]
})
return result
}
})
Object.defineProperty(Model.prototype, "identifiers", {
get: function() {
var primaryKeys = Utils._.keys(this.__factory.primaryKeys)
, result = {}
, self = this
if(!this.__factory.hasPrimaryKeys)
primaryKeys = ['id']
primaryKeys.forEach(function(identifier) {
result[identifier] = self[identifier]
})
return result
}
})
Model.prototype.save = function() {
var updatedAtAttr = this.__options.underscored ? 'updated_at' : 'updatedAt'
if(this.hasOwnProperty(updatedAtAttr))
this[updatedAtAttr] = new Date()
if(this.isNewRecord) {
return this.QueryInterface.insert(this, this.__factory.tableName, this.values)
} else {
var identifier = this.__options.hasPrimaryKeys ? this.primaryKeyValues : this.id
, tableName = this.__factory.tableName
, query = this.QueryInterface.update(this, tableName, this.values, identifier)
return query
}
}
/*
* Validate this model's attribute values according to validation rules set in the model definition.
*
* @return null if and only if validation successful; otherwise an object containing { field name : [error msgs] } entries.
*/
Model.prototype.validate = function() {
var self = this
var failures = {}
// for each field and value
Utils._.each(self.values, function(value, field) {
// if field has validators
if (self.validators.hasOwnProperty(field)) {
// for each validator
Utils._.each(self.validators[field], function(details, validatorType) {
var is_custom_fn = false // if true then it's a custom validation method
var fn_method = null // the validation function to call
var fn_args = [] // extra arguments to pass to validation function
var fn_msg = "" // the error message to return if validation fails
// is it a custom validator function?
if (Utils._.isFunction(details)) {
is_custom_fn = true
fn_method = Utils._.bind(details, self, value)
}
// is it a validator module function?
else {
// extra args
fn_args = details.hasOwnProperty("args") ? details.args : []
if (!Utils._.isArray(fn_args))
fn_args = [fn_args]
// error msg
fn_msg = details.hasOwnProperty("msg") ? details.msg : false
// check method exists
var v = Validator.check(value, fn_msg)
if (!Utils._.isFunction(v[validatorType]))
throw new Error("Invalid validator function: " + validatorType)
// bind to validator obj
fn_method = Utils._.bind(v[validatorType], v)
}
try {
fn_method.apply(null, fn_args)
} catch (err) {
err = err.message
// if we didn't provide a custom error message then augment the default one returned by the validator
if (!fn_msg && !is_custom_fn)
err += ": " + field
// each field can have multiple validation failures stored against it
if (failures.hasOwnProperty(field)) {
failures[field].push(err)
} else {
failures[field] = [err]
}
}
}) // for each validator for this field
} // if field has validator set
}) // for each field
return (Utils._.isEmpty(failures) ? null : failures)
}
Model.prototype.updateAttributes = function(updates) {
var self = this
var readOnlyAttributes = Utils._.keys(this.__factory.primaryKeys)
readOnlyAttributes.push('id')
readOnlyAttributes.push('createdAt')
readOnlyAttributes.push('updatedAt')
readOnlyAttributes.push('deletedAt')
Utils._.each(updates, function(value, attr) {
var updateAllowed = (
(readOnlyAttributes.indexOf(attr) == -1) &&
(readOnlyAttributes.indexOf(Utils._.underscored(attr)) == -1) &&
(self.attributes.indexOf(attr) > -1)
)
updateAllowed && (self[attr] = value)
})
return this.save()
}
Model.prototype.destroy = function() {
if(this.__options.timestamps && this.__options.paranoid) {
var attr = this.__options.underscored ? 'deleted_at' : 'deletedAt'
this[attr] = new Date()
return this.save()
} else {
var identifier = this.__options.hasPrimaryKeys ? this.primaryKeyValues : this.id
return this.QueryInterface.delete(this, this.__factory.tableName, identifier)
}
}
Model.prototype.equals = function(other) {
var result = true
, self = this
Utils._.each(this.values, function(value, key) {
result = result && (value == other[key])
})
return result
}
Model.prototype.equalsOneOf = function(others) {
var result = false
, self = this
others.forEach(function(other) { result = result || self.equals(other) })
return result
}
Model.prototype.addAttribute = function(attribute, value) {
this[attribute] = value
this.attributes.push(attribute)
}
Model.prototype.setValidators = function(attribute, validators) {
this.validators[attribute] = validators
}
// private
var initAttributes = function(values) {
var self = this
// add all passed values to the model and store the attribute names in this.attributes
Utils._.map(values, function(value, key) { self.addAttribute(key, value) })
// set id to null if not passed as value
// a newly created model has no id
var defaults = this.__options.hasPrimaryKeys ? {} : { id: null }
if(this.__options.timestamps) {
defaults[this.__options.underscored ? 'created_at' : 'createdAt'] = new Date()
defaults[this.__options.underscored ? 'updated_at' : 'updatedAt'] = new Date()
if(this.__options.paranoid)
defaults[this.__options.underscored ? 'deleted_at' : 'deletedAt'] = null
}
Utils._.map(defaults, function(value, attr) {
if(!self.hasOwnProperty(attr))
self.addAttribute(attr, value)
})
}
/* Add the instance methods to Model */
Utils._.extend(Model.prototype, Mixin.prototype)
return Model
})()
var Utils = require("./utils")
module.exports = (function() {
var QueryChainer = function(emitters) {
var self = this
this.finishedEmits = 0
this.emitters = []
this.serials = []
this.fails = []
this.finished = false
this.wasRunning = false
this.eventEmitter = null
emitters = emitters || []
emitters.forEach(function(emitter) { self.add(emitter) })
}
Utils.addEventEmitter(QueryChainer)
QueryChainer.prototype.add = function(emitterOrKlass, method, params, options) {
if(!!method) {
this.serials.push({ klass: emitterOrKlass, method: method, params: params, options: options })
} else {
observeEmitter.call(this, emitterOrKlass)
this.emitters.push(emitterOrKlass)
}
return this
}
QueryChainer.prototype.run = function() {
var self = this
this.eventEmitter = new Utils.CustomEventEmitter(function() {
self.wasRunning = true
finish.call(self)
})
return this.eventEmitter.run()
}
QueryChainer.prototype.runSerially = function(options) {
var self = this
options = Utils._.extend({
skipOnError: false
}, options)
var exec = function() {
var serial = self.serials.pop()
if(serial) {
serial.options = serial.options || {}
serial.options.before && serial.options.before(serial.klass)
var onSuccess = function() {
serial.options.after && serial.options.after(serial.klass)
self.finishedEmits++
exec()
}
var onError = function(err) {
serial.options.after && serial.options.after(serial.klass)
self.finishedEmits++
self.fails.push(err)
exec()
}
if(options.skipOnError && (self.fails.length > 0)) {
onError('Skipped due to earlier error!')
} else {
var emitter = serial.klass[serial.method].apply(serial.klass, serial.params)
emitter.success(function() {
if(serial.options.success)
serial.options.success(serial.klass, onSuccess)
else
onSuccess()
}).error(onError)
}
} else {
self.wasRunning = true
finish.call(self)
}
}
this.serials.reverse()
this.eventEmitter = new Utils.CustomEventEmitter(exec)
return this.eventEmitter.run()
}
// private
var observeEmitter = function(emitter) {
var self = this
emitter
.success(function(){
self.finishedEmits++
finish.call(self)
})
.error(function(err){
self.finishedEmits++
self.fails.push(err)
finish.call(self)
})
}
var finish = function() {
this.finished = true
if(this.emitters.length > 0)
this.finished = (this.finishedEmits == this.emitters.length)
else if(this.serials.length > 0)
this.finished = (this.finishedEmits == this.serials.length)
if(this.finished && this.wasRunning) {
var status = (this.fails.length == 0 ? 'success' : 'failure')
, result = (this.fails.length == 0 ? result : this.fails)
this.eventEmitter.emit(status, result)
}
}
return QueryChainer
})()
var Utils = require('./utils')
, DataTypes = require('./data-types')
module.exports = (function() {
var QueryInterface = function(sequelize) {
this.sequelize = sequelize
this.QueryGenerator = require('./dialects/' + this.sequelize.options.dialect + '/query-generator')
}
Utils.addEventEmitter(QueryInterface)
QueryInterface.prototype.createTable = function(tableName, attributes, options) {
var attributeHashes = {}
Utils._.each(attributes, function(dataTypeOrOptions, attributeName) {
if(Utils._.values(DataTypes).indexOf(dataTypeOrOptions) > -1)
attributeHashes[attributeName] = { type: dataTypeOrOptions }
else
attributeHashes[attributeName] = dataTypeOrOptions
})
attributes = this.QueryGenerator.attributesToSQL(attributeHashes)
var sql = this.QueryGenerator.createTableQuery(tableName, attributes, options)
return queryAndEmit.call(this, sql, 'createTable')
}
QueryInterface.prototype.dropTable = function(tableName) {
var sql = this.QueryGenerator.dropTableQuery(tableName)
return queryAndEmit.call(this, sql, 'dropTable')
}
QueryInterface.prototype.dropAllTables = function() {
var self = this
return new Utils.CustomEventEmitter(function(emitter) {
var chainer = new Utils.QueryChainer()
self.showAllTables().success(function(tableNames) {
tableNames.forEach(function(tableName) {
chainer.add(self.sequelize.getQueryInterface().dropTable(tableName))
})
chainer
.run()
.success(function() {
self.emit('dropAllTables', null)
emitter.emit('success', null)
})
.error(function(err) {
self.emit('dropAllTables', err)
emitter.emit('failure', err)
})
}).error(function(err) {
self.emit('dropAllTables', err)
emitter.emit('failure', err)
})
}).run()
}
QueryInterface.prototype.renameTable = function(before, after) {
var sql = this.QueryGenerator.renameTableQuery(before, after)
return queryAndEmit.call(this, sql, 'renameTable')
}
QueryInterface.prototype.showAllTables = function() {
var self = this
return new Utils.CustomEventEmitter(function(emitter) {
var showTablesSql = self.QueryGenerator.showTablesQuery()
self.sequelize.query(showTablesSql, null, { raw: true }).success(function(tableNames) {
self.emit('showAllTables', null)
emitter.emit('success', Sequelize.Utils._.flatten(tableNames))
}).error(function(err) {
self.emit('showAllTables', err)
emitter.emit('failure', err)
})
}).run()
}
QueryInterface.prototype.describeTable = function(tableName) {
var self = this
return new Utils.CustomEventEmitter(function(emitter) {
self.sequelize.query('DESCRIBE `' + tableName + '`;', null, { raw: true }).success(function(data) {
emitter.emit('success', data)
}).error(function(err) {
emitter.emit('failure', err)
})
}).run()
}
QueryInterface.prototype.addColumn = function(tableName, attributeName, dataTypeOrOptions) {
var attributes = {}
if(Utils._.values(DataTypes).indexOf(dataTypeOrOptions) > -1)
attributes[attributeName] = { type: dataTypeOrOptions, allowNull: false }
else
attributes[attributeName] = dataTypeOrOptions
var options = this.QueryGenerator.attributesToSQL(attributes)
, sql = this.QueryGenerator.addColumnQuery(tableName, options)
return queryAndEmit.call(this, sql, 'addColumn')
}
QueryInterface.prototype.removeColumn = function(tableName, attributeName) {
var sql = this.QueryGenerator.removeColumnQuery(tableName, attributeName)
return queryAndEmit.call(this, sql, 'removeColumn')
}
QueryInterface.prototype.changeColumn = function(tableName, attributeName, dataTypeOrOptions) {
var attributes = {}
if(Utils._.values(DataTypes).indexOf(dataTypeOrOptions) > -1)
attributes[attributeName] = { type: dataTypeOrOptions, allowNull: false }
else
attributes[attributeName] = dataTypeOrOptions
var options = this.QueryGenerator.attributesToSQL(attributes)
, sql = this.QueryGenerator.changeColumnQuery(tableName, options)
return queryAndEmit.call(this, sql, 'changeColumn')
}
QueryInterface.prototype.renameColumn = function(tableName, attrNameBefore, attrNameAfter) {
var self = this
return new Utils.CustomEventEmitter(function(emitter) {
self.describeTable(tableName).success(function(data) {
data = data.filter(function(h) { return h.Field == attrNameBefore })[0]
var options = {}
options[attrNameAfter] = {
type: data.Type,
allowNull: data.Null == 'YES',
defaultValue: data.Default
}
var sql = self.QueryGenerator.renameColumnQuery(tableName,
attrNameBefore,
self.QueryGenerator.attributesToSQL(options)
)
self.sequelize.query(sql).success(function() {
self.emit('renameColumn', null)
emitter.emit('success', null)
}).error(function(err) {
self.emit('renameColumn', err)
emitter.emit('failure', err)
})
}).error(function(err) {
self.emit('renameColumn', err)
emitter.emit('failure', err)
})
}).run()
}
QueryInterface.prototype.addIndex = function(tableName, attributes, options) {
var sql = this.QueryGenerator.addIndexQuery(tableName, attributes, options)
return queryAndEmit.call(this, sql, 'addIndex')
}
QueryInterface.prototype.showIndex = function(tableName, options) {
var sql = this.QueryGenerator.showIndexQuery(tableName, options)
return queryAndEmit.call(this, sql, 'showIndex')
}
QueryInterface.prototype.removeIndex = function(tableName, indexNameOrAttributes) {
var sql = this.QueryGenerator.removeIndexQuery(tableName, indexNameOrAttributes)
return queryAndEmit.call(this, sql, "removeIndex")
}
QueryInterface.prototype.insert = function(model, tableName, values) {
var sql = this.QueryGenerator.insertQuery(tableName, values)
return queryAndEmit.call(this, [sql, model], 'insert', {
success: function(obj) { obj.isNewRecord = false }
})
}
QueryInterface.prototype.update = function(model, tableName, values, identifier) {
var sql = this.QueryGenerator.updateQuery(tableName, values, identifier)
return queryAndEmit.call(this, [sql, model], 'update')
}
QueryInterface.prototype.delete = function(model, tableName, identifier) {
var sql = this.QueryGenerator.deleteQuery(tableName, identifier)
return queryAndEmit.call(this, [sql, model], 'delete')
}
QueryInterface.prototype.select = function(factory, tableName, options, queryOptions) {
var sql = this.QueryGenerator.selectQuery(tableName, options)
return queryAndEmit.call(this, [sql, factory, queryOptions], 'select')
}
QueryInterface.prototype.rawSelect = function(tableName, options, attributeSelector) {
var self = this
if(attributeSelector == undefined)
throw new Error('Please pass an attribute selector!')
return new Utils.CustomEventEmitter(function(emitter) {
var sql = self.QueryGenerator.selectQuery(tableName, options)
self.sequelize
.query(sql, null, { plain: true, raw: true })
.success(function(data) {
self.emit('rawSelect', null)
emitter.emit('success', data[attributeSelector])
})
.error(function(err) {
self.emit('rawSelect', err)
emitter.emit('failure', err)
})
}).run()
}
// private
var queryAndEmit = function(sqlOrQueryParams, methodName, options) {
var self = this
options = Utils._.extend({
success: function(obj){},
error: function(err){}
}, options || {})
return new Utils.CustomEventEmitter(function(emitter) {
var query = null
if(Array.isArray(sqlOrQueryParams))
query = self.sequelize.query.apply(self.sequelize, sqlOrQueryParams)
else
query = self.sequelize.query(sqlOrQueryParams)
// append the query for better testing
emitter.query = query
query.success(function(obj) {
options.success && options.success(obj)
self.emit(methodName, null)
emitter.emit('success', obj)
}).error(function(err) {
options.error && options.error(err)
self.emit(methodName, err)
emitter.emit('failure', err)
})
}).run()
}
return QueryInterface
})()
var Utils = require("./utils")
, ModelFactory = require("./model-factory")
, DataTypes = require('./data-types')
, ModelFactoryManager = require("./model-factory-manager")
, Migrator = require("./migrator")
, QueryInterface = require("./query-interface")
module.exports = (function() {
var Sequelize = function(database, username, password, options) {
this.options = Utils._.extend({
dialect: 'mysql',
host: 'localhost',
port: 3306,
define: {},
query: {},
sync: {}
}, options || {})
this.config = {
database: database,
username: username,
password: (( (["", null, false].indexOf(password) > -1) || (typeof password == 'undefined')) ? null : password),
host : this.options.host,
port : this.options.port
}
var ConnectorManager = require("./dialects/" + this.options.dialect + "/connector-manager")
this.modelFactoryManager = new ModelFactoryManager(this)
this.connectorManager = new ConnectorManager(this, this.config)
}
Sequelize.Utils = Utils
Sequelize.Utils._.map(DataTypes, function(sql, accessor) { Sequelize[accessor] = sql})
Sequelize.prototype.getQueryInterface = function() {
this.queryInterface = this.queryInterface || new QueryInterface(this)
return this.queryInterface
}
Sequelize.prototype.getMigrator = function(options, force) {
if(force)
this.migrator = new Migrator(this, options)
else
this.migrator = this.migrator || new Migrator(this, options)
return this.migrator
}
Sequelize.prototype.define = function(modelName, attributes, options) {
options = options || {}
if(this.options.define)
options = Sequelize.Utils.merge(options, this.options.define)
var factory = new ModelFactory(modelName, attributes, options)
this.modelFactoryManager.addModel(factory.init(this.modelFactoryManager))
return factory
}
Sequelize.prototype.import = function(path) {
var defineCall = require(path)
return defineCall(this, DataTypes)
}
Sequelize.prototype.migrate = function(options) {
this.getMigrator().migrate(options)
}
Sequelize.prototype.query = function(sql, callee, options) {
options = Utils._.extend(Utils._.clone(this.options.query), options || {})
options = Utils._.extend(options, {
logging: this.options.hasOwnProperty('logging') ? this.options.logging : true
})
return this.connectorManager.query(sql, callee, options)
}
Sequelize.prototype.sync = function(options) {
options = options || {}
if(this.options.sync)
options = Sequelize.Utils.merge(options, this.options.sync)
var self = this
return new Utils.CustomEventEmitter(function(emitter) {
var chainer = new Utils.QueryChainer
self.modelFactoryManager.models.forEach(function(model) { chainer.add(model.sync(options)) })
chainer
.run()
.success(function() { emitter.emit('success', null) })
.error(function(err) { emitter.emit('failure', err) })
}).run()
}
Sequelize.prototype.drop = function() {
var self = this
return new Utils.CustomEventEmitter(function(emitter) {
var chainer = new Utils.QueryChainer
self.modelFactoryManager.models.forEach(function(model) { chainer.add(model.drop()) })
chainer
.run()
.success(function() { emitter.emit('success', null) })
.error(function(err) { emitter.emit('failure', err) })
}).run()
}
return Sequelize
})()
var Utils = require("./utils")
, Model = require("./model")
, QueryGenerator = require("./query-generator")
, DataTypes = require("./data-types")
var ModelDefinition = module.exports = function(name, attributes, options) {
var self = this
this.options = options || {}
this.options.timestamps = this.options.hasOwnProperty('timestamps') ? this.options.timestamps : true
this.name = name
this.tableName = this.options.freezeTableName ? name : Utils.pluralize(name)
this.attributes = Utils.simplifyAttributes(attributes)
this.rawAttributes = attributes
this.modelManager = null // defined by model-manager during addModel
this.associations = {}
this.addDefaultAttributes()
this.addOptionalClassMethods()
this.findAutoIncrementField()
}
Utils.addEventEmitter(ModelDefinition)
ModelDefinition.prototype.addOptionalClassMethods = function() {
var self = this
Utils._.each(this.options.classMethods || {}, function(fct, name) { self[name] = fct })
}
ModelDefinition.prototype.addDefaultAttributes = function() {
var defaultAttributes = {id: {type: DataTypes.INTEGER, allowNull: false, primaryKey: true, autoIncrement: true}}
, self = this
if(this.hasPrimaryKeys) defaultAttributes = {}
if(this.options.timestamps) {
defaultAttributes[Utils._.underscoredIf('createdAt', this.options.underscored)] = {type: DataTypes.DATE, allowNull: false}
defaultAttributes[Utils._.underscoredIf('updatedAt', this.options.underscored)] = {type: DataTypes.DATE, allowNull: false}
if(this.options.paranoid)
defaultAttributes[Utils._.underscoredIf('deletedAt', this.options.underscored)] = {type: DataTypes.DATE}
}
defaultAttributes = Utils.simplifyAttributes(defaultAttributes)
Utils._.map(defaultAttributes, function(value, attr) { self.attributes[attr] = value })
}
ModelDefinition.prototype.findAutoIncrementField = function() {
var self = this
this.autoIncrementField = null
Utils._.map(this.attributes, function(definition, name) {
if (definition && (definition.indexOf('auto_increment') > -1)) {
if (self.autoIncrementField)
throw new Error('Invalid model definition. Only one autoincrement field allowed.')
else
self.autoIncrementField = name
}
})
}
ModelDefinition.prototype.query = function() {
var args = Utils._.map(arguments, function(arg, _) { return arg })
, s = this.modelManager.sequelize
// add this as the second argument
if(arguments.length == 1) args.push(this)
return s.query.apply(s, args)
}
ModelDefinition.prototype.sync = function(options) {
options = Utils.merge(options || {}, this.options)
var self = this
var doQuery = function() {
self.query(QueryGenerator.createTableQuery(self.tableName, self.attributes, options))
.on('success', function() { self.emit('success', self) })
.on('failure', function(err) { self.emit('failure', err) })
}
if(options.force) {
this.drop()
.on('success', function() { doQuery() })
.on('failure', function(err) { self.emit('failure', err) })
} else {
doQuery()
}
return this
}
ModelDefinition.prototype.drop = function() {
return this.query(QueryGenerator.dropTableQuery(this.tableName, this.id))
}
ModelDefinition.prototype.__defineGetter__('all', function() {
return this.query(QueryGenerator.selectQuery(this.tableName))
})
ModelDefinition.prototype.count = function(options) {
var self = this
var emitter = new Utils.CustomEventEmitter(function() {
self.query(QueryGenerator.countQuery(self.tableName, options), self, {plain: true}).on('success', function(obj) {
emitter.emit('success', obj['count(*)'])
})
})
return emitter.run()
}
ModelDefinition.prototype.max = function(field, options) {
var self = this
var emitter = new Utils.CustomEventEmitter(function() {
self.query(QueryGenerator.maxQuery(self.tableName, field,options), self, {plain: true}).on('success', function(obj) {
emitter.emit('success', obj['max'])
})
})
return emitter.run()
}
ModelDefinition.prototype.min = function(field, options) {
var self = this
var emitter = new Utils.CustomEventEmitter(function() {
self.query(QueryGenerator.minQuery(self.tableName, field,options), self, {plain: true}).on('success', function(obj) {
emitter.emit('success', obj['min'])
})
})
return emitter.run()
}
ModelDefinition.prototype.findAll = function(options) {
return this.query(QueryGenerator.selectQuery(this.tableName, options))
}
//right now, the caller (has-many-double-linked) is in charge of the where clause
ModelDefinition.prototype.findAllJoin = function(joinTableName, options) {
optcpy = Utils._.clone(options)
optcpy.attributes = optcpy.attributes || [Utils.addTicks(this.tableName)+".*"]
return this.query(QueryGenerator.selectQuery([this.tableName, joinTableName], optcpy))
}
ModelDefinition.prototype.find = function(options) {
// options is not a hash but an id
if(typeof options == 'number')
options = {where: options}
else if (Utils.argsArePrimaryKeys(arguments, this.primaryKeys)) {
var where = {}
, self = this
Utils._.each(arguments, function(arg, i) {
var key = Utils._.keys(self.primaryKeys)[i]
where[key] = arg
})
options = {where: where}
} else if((options == null) || (options == undefined)) {
var NullEmitter = require("./null-emitter")
return new NullEmitter()
}
options.limit = 1
var query = QueryGenerator.selectQuery(this.tableName, options)
return this.query(query, this, {plain: true})
}
ModelDefinition.prototype.build = function(values, options) {
var instance = new Model(values, Utils._.extend(this.options, {hasPrimaryKeys: this.hasPrimaryKeys}))
, self = this
options = options || {}
instance.__definition = this
Utils._.map(this.attributes, function(definition, name) {
if(typeof instance[name] == 'undefined') {
var value = null
if(self.rawAttributes.hasOwnProperty(name) && self.rawAttributes[name].hasOwnProperty('defaultValue'))
value = self.rawAttributes[name].defaultValue
instance[name] = value
instance.addAttribute(name, value)
}
})
Utils._.each(this.options.instanceMethods || {}, function(fct, name) { instance[name] = fct })
Utils._.each(this.associations, function(association, associationName) {
association.injectGetter(instance)
association.injectSetter(instance)
})
instance.isNewRecord = options.hasOwnProperty('isNewRecord') ? options.isNewRecord : true
return instance
}
ModelDefinition.prototype.create = function(values) {
return this.build(values).save()
}
ModelDefinition.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
})
ModelDefinition.prototype.__defineGetter__('primaryKeyCount', function() {
return Utils._.keys(this.primaryKeys).length
})
ModelDefinition.prototype.__defineGetter__('hasPrimaryKeys', function() {
return this.primaryKeyCount > 0
})
Utils._.map(require("./associations/mixin").classMethods, function(fct, name) { ModelDefinition.prototype[name] = fct })
var Utils = require("./utils")
, Mixin = require("./associations/mixin")
, QueryGenerator = require("./query-generator")
var Model = module.exports = function(values, options) {
var self = this
this.__definition = null // will be set in Model.build
this.attributes = []
this.__options = options || {}
// add all passed values to the model and store the attribute names in this.attributes
Utils._.map(values, function(value, key) { self.addAttribute(key, value) })
// set id to null if not passed as value
// a newly created model has no id
var defaults = this.__options.hasPrimaryKeys ? {} : { id: null }
if(this.__options.timestamps) {
defaults[this.__options.underscored ? 'created_at' : 'createdAt'] = new Date()
defaults[this.__options.underscored ? 'updated_at' : 'updatedAt'] = new Date()
if(this.__options.paranoid)
defaults[this.__options.underscored ? 'deleted_at' : 'deletedAt'] = null
}
Utils._.map(defaults, function(value, attr) {
if(!self.hasOwnProperty(attr))
self.addAttribute(attr, value)
})
}
Utils.addEventEmitter(Model)
Utils._.map(Mixin.classMethods, function(fct, name) { Model[name] = fct })
Model.Events = {
insert: 'InsertQuery',
update: 'UpdateQuery',
destroy: 'DestroyQuery'
}
Model.prototype.addAttribute = function(attribute, value) {
this[attribute] = value
this.attributes.push(attribute)
}
Model.prototype.query = function() {
var args = Utils._.map(arguments, function(arg, _) { return arg })
, s = this.__definition.modelManager.sequelize
args.push(this)
return s.query.apply(s, args)
}
Model.prototype.save = function() {
var attr = this.__options.underscored ? 'updated_at' : 'updatedAt'
if(this.hasOwnProperty(attr))
this[attr] = new Date()
if(this.isNewRecord) {
var self = this
var eventEmitter = new Utils.CustomEventEmitter(function() {
self.query(QueryGenerator.insertQuery(self.__definition.tableName, self.values))
.on('success', function(obj) {
obj.isNewRecord = false
eventEmitter.emit('success', obj)
})
.on('failure', function(err) { eventEmitter.emit('failure', err) })
.on('sql', function(sql) { eventEmitter.emit('sql', sql) })
})
return eventEmitter.run()
} else {
var identifier = this.__options.hasPrimaryKeys ? this.primaryKeyValues : this.id
return this.query(QueryGenerator.updateQuery(this.__definition.tableName, this.values, identifier))
}
}
Model.prototype.updateAttributes = function(updates) {
var self = this
var readOnlyAttributes = Utils._.keys(this.__definition.primaryKeys)
readOnlyAttributes.push('id')
readOnlyAttributes.push('createdAt')
readOnlyAttributes.push('updatedAt')
readOnlyAttributes.push('deletedAt')
Utils._.each(updates, function(value, attr) {
var updateAllowed = (
(readOnlyAttributes.indexOf(attr) == -1) &&
(readOnlyAttributes.indexOf(Utils._.underscored(attr)) == -1) &&
(self.attributes.indexOf(attr) > -1)
)
updateAllowed && (self[attr] = value)
})
return this.save()
}
Model.prototype.destroy = function() {
if(this.__options.timestamps && this.__options.paranoid) {
this[this.__options.underscored ? 'deleted_at' : 'deletedAt'] = new Date()
return this.save()
} else {
var identifier = this.__options.hasPrimaryKeys ? this.primaryKeyValues : this.id
return this.query(QueryGenerator.deleteQuery(this.__definition.tableName, identifier))
}
}
Model.prototype.__defineGetter__("identifiers", function() {
var primaryKeys = Utils._.keys(this.__definition.primaryKeys)
, result = {}
, self = this
if(!this.__definition.hasPrimaryKeys)
primaryKeys = ['id']
primaryKeys.forEach(function(identifier) {
result[identifier] = self[identifier]
})
return result
})
Model.prototype.__defineGetter__('isDeleted', function() {
var result = this.__options.timestamps && this.__options.paranoid
result = result && this[this.__options.underscored ? 'deleted_at' : 'deletedAt'] != null
return result
})
Model.prototype.__defineGetter__('values', function() {
var result = {}
, self = this
this.attributes.forEach(function(attr) {
result[attr] = self[attr]
})
return result
})
Model.prototype.__defineGetter__('primaryKeyValues', function() {
var result = {}
, self = this
Utils._.each(this.__definition.primaryKeys, function(_, attr) {
result[attr] = self[attr]
})
return result
})
Model.prototype.equals = function(other) {
var result = true
, self = this
Utils._.each(this.values, function(value, key) {
result = result && (value == other[key])
})
return result
}
Model.prototype.equalsOneOf = function(others) {
var result = false
, self = this
others.forEach(function(other) { result = result || self.equals(other) })
return result
}
/* Add the instance methods to Model */
Utils._.map(Mixin.instanceMethods, function(fct, name) { Model.prototype[name] = fct})
var Utils = require("./utils")
var QueryChainer = module.exports = function(emitters) {
var self = this
this.finishedEmits = 0
this.emitters = []
this.fails = []
this.finished = false
this.runned = false
this.eventEmitter = null
emitters = emitters || []
emitters.forEach(function(emitter) { self.add(emitter) })
}
Utils.addEventEmitter(QueryChainer)
QueryChainer.prototype.add = function(emitter) {
this.observeEmitter(emitter)
this.emitters.push(emitter)
return this
}
QueryChainer.prototype.observeEmitter = function(emitter) {
var self = this
emitter
.on('success', function(){
self.finishedEmits++
self.finish()
})
.on('failure', function(err){
self.finishedEmits++
self.fails.push(err)
self.finish()
})
}
QueryChainer.prototype.finish = function(result) {
this.finished = (this.finishedEmits == this.emitters.length)
if(this.finished && this.runned) {
var status = this.fails.length == 0 ? 'success' : 'failure'
result = this.fails.length == 0 ? result : this.fails
this.eventEmitter.emit(status, result)
}
}
QueryChainer.prototype.run = function() {
var self = this
this.eventEmitter = new Utils.CustomEventEmitter(function() {
self.runned = true
self.finish()
})
return this.eventEmitter.run()
}
\ No newline at end of file
var Utils = require("./utils")
var QueryGenerator = module.exports = {
/*
Returns a query for creating a table.
Attributes should have the format: {attributeName: type, attr2: type2} --> {title: 'VARCHAR(255)'}
*/
createTableQuery: function(tableName, attributes, options) {
options = options || {}
var query = "CREATE TABLE IF NOT EXISTS <%= table %> (<%= attributes%>) ENGINE=<%= engine %> <%= charset %>"
, primaryKeys = []
, attrStr = Utils._.map(attributes, function(dataType, attr) {
var dt = dataType
if (Utils._.includes(dt, 'PRIMARY KEY')) {
primaryKeys.push(attr)
return Utils.addTicks(attr) + " " + dt.replace(/PRIMARY KEY/, '')
} else {
return Utils.addTicks(attr) + " " + dt
}
}).join(", ")
, values = {
table: Utils.addTicks(tableName),
attributes: attrStr,
engine: options.engine || 'InnoDB',
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() + ";"
},
dropTableQuery: function(tableName, options) {
options = options || {}
var query = "DROP TABLE IF EXISTS <%= table %>;"
return Utils._.template(query)({table: Utils.addTicks(tableName)})
},
/*
Returns a query for selecting elements in the database <tableName>.
Options:
- attributes -> An array of attributes (e.g. ['name', 'birthday']). Default: *
- where -> A hash with conditions (e.g. {name: 'foo'})
OR an ID as integer
OR a string with conditions (e.g. 'name="foo"').
If you use a string, you have to escape it on your own.
- order -> e.g. 'id DESC'
- group
- limit -> The maximum count you want to get.
- offset -> An offset value to start from. Only useable with limit!
*/
selectQuery: function(tableName, options) {
options = options || {}
options.table = Array.isArray(tableName) ? tableName.map(function(tbl){return Utils.addTicks(tbl)}).join(", ") : Utils.addTicks(tableName)
options.attributes = options.attributes && options.attributes.map(function(attr){return attr.indexOf(Utils.TICK_CHAR)<0 ? Utils.addTicks(attr) : attr}).join(", ")
options.attributes = options.attributes || '*'
var query = "SELECT <%= attributes %> FROM <%= table %>"
if(options.where) {
options.where = QueryGenerator.getWhereConditions(options.where)
query += " WHERE <%= where %>"
}
if(options.order) query += " ORDER BY <%= order %>"
if(options.group) {
options.group = Utils.addTicks(options.group)
query += " GROUP BY <%= group %>"
}
if(options.limit) {
if(options.offset) query += " LIMIT <%= offset %>, <%= limit %>"
else query += " LIMIT <%= limit %>"
}
query += ";"
return Utils._.template(query)(options)
},
countQuery: function(tableName, options) {
return QueryGenerator.selectQuery(tableName, options).replace("*", "count(*)")
},
maxQuery: function(tableName, field,options) {
return QueryGenerator.selectQuery(tableName ,options).replace("*", "max("+field+") as max")
},
minQuery: function(tableName, field,options) {
return QueryGenerator.selectQuery(tableName ,options).replace("*", "min("+field+") as min")
},
/*
Returns an insert into command. Parameters: table name + hash of attribute-value-pairs.
*/
insertQuery: function(tableName, attrValueHash) {
var query = "INSERT INTO <%= table %> (<%= attributes %>) VALUES (<%= values %>);"
var replacements = {
table: Utils.addTicks(tableName),
attributes: Utils._.keys(attrValueHash).map(function(attr){return Utils.addTicks(attr)}).join(","),
values: Utils._.values(attrValueHash).map(function(value){
return Utils.escape((value instanceof Date) ? Utils.toSqlDate(value) : value)
}).join(",")
}
return Utils._.template(query)(replacements)
},
/*
Returns an update query.
Parameters:
- tableName -> Name of the table
- values -> A hash with attribute-value-pairs
- where -> A hash with conditions (e.g. {name: 'foo'})
OR an ID as integer
OR a string with conditions (e.g. 'name="foo"').
If you use a string, you have to escape it on your own.
*/
updateQuery: function(tableName, values, where) {
var query = "UPDATE <%= table %> SET <%= values %> WHERE <%= where %>"
var replacements = {
table: Utils.addTicks(tableName),
values: Utils._.map(values, function(value, key){
return Utils.addTicks(key) + "=" + Utils.escape((value instanceof Date) ? Utils.toSqlDate(value) : value)
}).join(","),
where: QueryGenerator.getWhereConditions(where)
}
return Utils._.template(query)(replacements)
},
/*
Returns a deletion query.
Parameters:
- tableName -> Name of the table
- where -> A hash with conditions (e.g. {name: 'foo'})
OR an ID as integer
OR a string with conditions (e.g. 'name="foo"').
If you use a string, you have to escape it on your own.
Options:
- limit -> Maximaum count of lines to delete
*/
deleteQuery: function(tableName, where, options) {
options = options || {}
options.limit = options.limit || 1
var query = "DELETE FROM <%= table %> WHERE <%= where %> LIMIT <%= limit %>"
var replacements = {
table: Utils.addTicks(tableName),
where: QueryGenerator.getWhereConditions(where),
limit: Utils.escape(options.limit)
}
return Utils._.template(query)(replacements)
},
/*
Takes something and transforms it into values of a where condition.
*/
getWhereConditions: function(smth) {
var result = null
if(Utils.isHash(smth))
result = QueryGenerator.hashToWhereConditions(smth)
else if(typeof smth == 'number')
result = Utils.addTicks('id') + "=" + Utils.escape(smth)
else if(typeof smth == "string")
result = smth
else if(Array.isArray(smth))
result = Utils.format(smth)
return result
},
/*
Takes a hash and transforms it into a mysql where condition: {key: value, key2: value2} ==> key=value AND key2=value2
The values are transformed by the relevant datatype.
*/
hashToWhereConditions: function(hash) {
return Utils._.map(hash, function(value, key) {
//handle qualified key names
var _key = key.split('.').map(function(col){return Utils.addTicks(col)}).join(".")
var _value = null
if(Array.isArray(value)) {
_value = "(" + Utils._.map(value, function(subvalue) {
return Utils.escape(subvalue);
}).join(',') + ")"
return [_key, _value].join(" IN ")
}
else if ((value) && (typeof value == 'object')) {
//using as sentinel for join column => value
_value = value.join.split('.').map(function(col){return Utils.addTicks(col)}).join(".")
return [_key, _value].join("=")
} else {
_value = Utils.escape(value)
return (_value == 'NULL') ? _key + " IS NULL" : [_key, _value].join("=")
}
}).join(" AND ")
}
}
var Utils = require("./utils")
var Query = module.exports = function(client, callee, options) {
var self = this
this.client = client
this.callee = callee
this.options = options || {}
this.bindClientFunction = function(err) { self.onFailure(err) }
}
Utils.addEventEmitter(Query)
Query.prototype.run = function(query) {
var self = this
this.sql = query
this.bindClient()
if(this.options.logging)
console.log('Executing: ' + this.sql)
this.client.query(this.sql, function(err, results, fields) {
//allow clients to listen to sql to do their own logging or whatnot
self.emit('sql', self.sql)
err ? self.onFailure(err) : self.onSuccess(self.sql, results, fields)
}).setMaxListeners(100)
return this
}
Query.prototype.bindClient = function() {
this.client.on('error', this.bindClientFunction)
}
Query.prototype.unbindClient = function() {
this.client.removeListener('error', this.bindClientFunction)
}
Query.prototype.onSuccess = function(query, results, fields) {
var result = this.callee
, self = this
// add the inserted row id to the instance
if (this.callee && (query.indexOf('INSERT INTO') == 0) && (results.hasOwnProperty('insertId')))
this.callee[this.callee.__definition.autoIncrementField] = results.insertId
// transform results into real model instances
// return the first real model instance if options.plain is set (e.g. Model.find)
if (query.indexOf('SELECT') == 0) {
result = results.map(function(result) { return self.callee.build(result, {isNewRecord: false}) })
if(this.options.plain)
result = (result.length == 0) ? null : result[0]
}
this.unbindClient()
this.emit('success', result)
}
Query.prototype.onFailure = function(err) {
this.unbindClient()
this.emit('failure', err, this.callee)
}
var Utils = require("./utils")
, ModelDefinition = require("./model-definition")
, DataTypes = require('./data-types')
, ModelManager = require("./model-manager")
, ConnectorManager = require('./connector-manager')
var Sequelize = module.exports = function(database, username, password, options) {
options = options || {}
Utils._.reject(options, function(_, key) {
return ["host", "port", "disableTableNameModification"].indexOf(key) > -1
})
this.options = options
this.config = {
database: database,
username: username,
password: (( (["", null, false].indexOf(password) > -1) || (typeof password == 'undefined')) ? null : password),
host : options.host || 'localhost',
port : options.port || 3306
}
this.modelManager = new ModelManager(this)
this.connectorManager = new ConnectorManager(this.config)
}
Sequelize.Utils = Utils
var instanceMethods = {
define: function(modelName, attributes, options) {
options = options || {}
if(this.options.define)
options = Sequelize.Utils.merge(options, this.options.define)
var model = this.modelManager.addModel(new ModelDefinition(modelName, attributes, options))
return model
},
import: function(path) {
var defineCall = require(path)
return defineCall(this, DataTypes)
},
query: function(sql, callee, options) {
options = options || {}
if(this.options.query)
options = Sequelize.Utils.merge(options, this.options.query)
options.logging = this.options.hasOwnProperty('logging') ? this.options.logging : true
return this.connectorManager.query(sql, callee, options)
},
sync: function(options) {
options = options || {}
if(this.options.sync)
options = Sequelize.Utils.merge(options, this.options.sync)
var self = this
var eventEmitter = new Utils.CustomEventEmitter(function() {
var chainer = new Utils.QueryChainer
self.modelManager.models.forEach(function(model) { chainer.add(model.sync(options)) })
chainer
.run()
.on('success', function() { eventEmitter.emit('success', null) })
.on('failure', function(err) { eventEmitter.emit('failure', err) })
})
return eventEmitter.run()
},
drop: function() {
var self = this
return new Utils.CustomEventEmitter(function(emitter) {
var chainer = new Utils.QueryChainer
self.modelManager.models.forEach(function(model) { chainer.add(model.drop()) })
chainer
.run()
.on('success', function() { emitter.emit('success', null) })
.on('failure', function(err) { emitter.emit('failure', err) })
}).run()
}
}
Sequelize.Utils._.map(DataTypes, function(sql, accessor) { Sequelize[accessor] = sql})
Sequelize.Utils._.map(instanceMethods, function(fct, name) { Sequelize.prototype[name] = fct})
var client = new (require("mysql").Client)()
, util = require("util")
var Utils = module.exports = {
_: (function() {
var _ = require("underscore")
var _ = require("underscore")
, _s = require('underscore.string')
_.mixin(require('underscore.string'))
_.mixin(_s.exports())
_.mixin({
includes: _s.include,
camelizeIf: function(string, condition) {
var result = string
if(condition) result = _.camelize(string)
......@@ -20,11 +24,11 @@ var Utils = module.exports = {
return _
})(),
addEventEmitter: function(_class) {
require("sys").inherits(_class, require('events').EventEmitter)
util.inherits(_class, require('events').EventEmitter)
},
TICK_CHAR: '`',
addTicks: function(s) {
return '`' + Utils.removeTicks(s) + '`'
return Utils.TICK_CHAR + Utils.removeTicks(s) + Utils.TICK_CHAR
},
removeTicks: function(s) {
return s.replace("`", "")
......@@ -41,71 +45,6 @@ var Utils = module.exports = {
isHash: function(obj) {
return (typeof obj == 'object') && !obj.hasOwnProperty('length')
},
getDataTypeForValue: function(value) {
var DataTypes = require("./data-types")
switch(typeof value) {
case 'number':
return (value.toString().indexOf('.') > -1) ? DataTypes.FLOAT : DataTypes.INTEGER
break
case 'boolean':
return DataTypes.BOOLEAN
break
case 'object':
return (value.getMilliseconds) ? DataTypes.DATE : "WTF!"
break
default:
return DataTypes.TEXT
break
}
},
transformValueByDataType: function(value, dataType) {
dataType = dataType || Utils.getDataTypeForValue(value)
var DataTypes = require("./data-types")
if((value == null)||(typeof value == 'undefined')||((dataType.indexOf(DataTypes.INTEGER) > -1) && isNaN(value)))
return "NULL"
if(dataType.indexOf(DataTypes.FLOAT) > -1)
return (typeof value == 'number') ? value : parseFloat(value.replace(",", "."))
if(dataType.indexOf(DataTypes.BOOLEAN) > -1)
return (value === true ? 1 : 0)
if(dataType.indexOf(DataTypes.INTEGER) > -1)
return value
if(dataType.indexOf(DataTypes.DATE) > -1)
return ("'" + Utils.asSqlDate(value) + "'")
return ("'" + value + "'")
},
simplifyAttributes: function(attributes) {
var result = {}
Utils._.map(attributes, function(dataType, name) {
if(Utils.isHash(dataType)) {
var template = "<%= type %>"
, replacements = { type: dataType.type }
if(dataType.hasOwnProperty('allowNull') && (!dataType.allowNull)) template += " NOT NULL"
if(dataType.autoIncrement) template +=" auto_increment"
if(dataType.defaultValue != undefined) {
template += " DEFAULT <%= defaultValue %>"
replacements.defaultValue = Utils.escape(dataType.defaultValue)
}
if(dataType.unique) template += " UNIQUE"
if(dataType.primaryKey) template += " PRIMARY KEY"
result[name] = Utils._.template(template)(replacements)
} else {
result[name] = dataType
}
})
return result
},
toSqlDate: function(date) {
return [
[
......@@ -143,20 +82,15 @@ var Utils = module.exports = {
a[key] = b[key]
}
return a
}
}
// Some nice class accessors
var CustomEventEmitter = Utils.CustomEventEmitter = function(fct) {
this.fct = fct
}
Utils.addEventEmitter(CustomEventEmitter)
},
removeCommentsFromFunctionString: function(s) {
s = s.replace(/\s*(\/\/.*)/g, '')
s = s.replace(/(\/\*[\n\r\s\S]*?\*\/)/mg, '')
CustomEventEmitter.prototype.run = function() {
var self = this
setTimeout(function(){ self.fct.call(self, self) }, 5) // delay the function call and return the emitter
return this
return s
}
}
Utils.CustomEventEmitter = require("./emitters/custom-event-emitter")
Utils.QueryChainer = require("./query-chainer")
Utils.Lingo = require("lingo")
{
"name": "sqlize",
"description": "Modified version of sequelize - MySQL ORM for Node.JS",
"version": "0.2.3",
"author": "Meg Sharkey <meg@metamx.com>",
"name": "sequelize",
"description": "MySQL ORM for Node.JS",
"version": "1.3.0",
"author": "Sascha Depold <sascha@depold.com>",
"contributors": [
{ "name": "Sascha Depold", "email": "sascha@depold.com" },
{ "name": "Meg Sharkey", "email": "meg@metamx.com" }
],
"dependencies": {
"mysql": "=0.9.4",
"underscore": "=1.1.5",
"underscore.string": "=1.1.3",
"lingo": "=0.0.4"
"mysql": "0.9.x",
"underscore": "1.2.x",
"underscore.string": "2.0.x",
"lingo": "0.0.x",
"validator": "0.3.x",
"moment": "1.1.x",
"commander": "0.5.x"
},
"devDependencies": {
"jasmine-node": "=1.0.12",
"expresso": "=0.9.2"
"jasmine-node": "1.0.x",
"sqlite3": ">=2.0.0"
},
"keywords": [],
"main": "index",
"scripts": {},
"bin": {},
"engines": { "node": ">= 0.1.93" }
"bin": {
"sequelize": "bin/sequelize"
},
"engines": {
"node": ">=0.4.6"
},
"license": "MIT"
}
module.exports = {
up: function(migration, DataTypes) {
migration.createTable('Person', {
name: DataTypes.STRING,
isBetaMember: {
type: DataTypes.BOOLEAN,
defaultValue: false,
allowNull: false
}
})
},
down: function(migration) {
migration.dropTable('Person')
}
}
module.exports = {
up: function() {},
down: function() {}
}
module.exports = {
up: function(migration, DataTypes) {
migration.renameTable('Person', 'User')
},
down: function(migration, DataTypes) {
migration.renameTable('User', 'Person')
}
}
module.exports = {
up: function(migration, DataTypes) {
migration.addColumn('User', 'signature', DataTypes.TEXT)
migration.addColumn('User', 'shopId', { type: DataTypes.INTEGER, allowNull: true })
migration.addColumn('User', 'isAdmin', { type: DataTypes.BOOLEAN, defaultValue: false, allowNull: false })
},
down: function(migration, DataTypes) {
migration.removeColumn('User', 'signature')
migration.removeColumn('User', 'shopId')
migration.removeColumn('User', 'isAdmin')
}
}
module.exports = {
up: function(migration, DataTypes) {
migration.removeColumn('User', 'shopId')
},
down: function(migration, DataTypes) {
migration.addColumn('User', 'shopId', { type: DataTypes.INTEGER, allowNull: true })
}
}
module.exports = {
up: function(migration, DataTypes) {
migration.changeColumn('User', 'signature', {
type: DataTypes.STRING,
allowNull: false,
defaultValue: 'Signature'
})
},
down: function(migration, DataTypes) {}
}
module.exports = {
up: function(migration, DataTypes) {
migration.renameColumn('User', 'signature', 'sig')
},
down: function(migration, DataTypes) {
migration.renameColumn('User', 'sig', 'signature')
}
}
var config = require("../config/config")
, Sequelize = require("../../index")
, sequelize = new Sequelize(config.database, config.username, config.password, { logging: false })
, Helpers = new (require("../config/helpers"))(sequelize)
describe('BelongsTo', function() {
var User = null
, Task = null
var setup = function() {
User = sequelize.define('User', { username: Sequelize.STRING })
Task = sequelize.define('Task', { title: Sequelize.STRING })
}
beforeEach(function() { Helpers.dropAllTables(); setup() })
afterEach(function() { Helpers.dropAllTables() })
it('adds the foreign key', function() {
Task.belongsTo(User)
expect(Task.attributes['UserId']).toEqual("INTEGER")
})
it("underscores the foreign key", function() {
Task = sequelize.define('Task', { title: Sequelize.STRING }, {underscored: true})
Task.belongsTo(User)
expect(Task.attributes['user_id']).toEqual("INTEGER")
})
it("uses the passed foreign key", function() {
Task.belongsTo(User, {foreignKey: 'person_id'})
expect(Task.attributes['person_id']).toEqual("INTEGER")
})
it("defines getters and setters", function() {
Task.belongsTo(User)
var task = Task.build({title: 'asd'})
expect(task.setUser).toBeDefined()
expect(task.getUser).toBeDefined()
})
it("aliases the getters and setters according to the passed 'as' option", function() {
Task.belongsTo(User, {as: 'Person'})
var task = Task.build({title: 'asd'})
expect(task.setPerson).toBeDefined()
expect(task.getPerson).toBeDefined()
})
it("intializes the foreign key with null", function() {
Task.belongsTo(User)
var task = Task.build({title: 'asd'})
expect(task['UserId']).toBeNull()
})
it("sets and gets the correct objects", function() {
Task.belongsTo(User, {as: 'User'})
Helpers.async(function(done) {
User.sync({force: true}).success(function() {
Task.sync({force: true}).success(done)
})
})
Helpers.async(function(done) {
User.create({username: 'asd'}).success(function(u) {
Task.create({title: 'a task'}).success(function(t) {
t.setUser(u).success(function() {
t.getUser().success(function(user) {
expect(user.username).toEqual('asd')
done()
})
})
})
})
})
})
it("handles self associations", function() {
Helpers.async(function(done) {
var Person = sequelize.define('Person', { name: Sequelize.STRING })
Person.belongsTo(Person, {as: 'Mother', foreignKey: 'MotherId'})
Person.belongsTo(Person, {as: 'Father', foreignKey: 'FatherId'})
Person.sync({force: true}).success(function() {
var p = Person.build()
expect(p.setFather).toBeDefined()
expect(p.setMother).toBeDefined()
done()
})
})
})
it("sets the foreign key in self associations", function() {
var Person = sequelize.define('Person', { name: Sequelize.STRING })
Person.belongsTo(Person, {as: 'Mother'})
expect(Person.associations.MotherPersons.options.foreignKey).toEqual('MotherId')
})
})
var config = require("../config/config")
, Sequelize = require("../../index")
, sequelize = new Sequelize(config.database, config.username, config.password, { logging: false })
, Helpers = new (require("../config/helpers"))(sequelize)
describe('HasMany', function() {
var User = null
, Task = null
, sequelize = null
, Helpers = null
var setup = function() {
sequelize = new Sequelize(config.database, config.username, config.password, { logging: false })
Helpers = new (require("../config/helpers"))(sequelize)
Helpers.dropAllTables()
User = sequelize.define('User', { username: Sequelize.STRING })
Task = sequelize.define('Task', { title: Sequelize.STRING })
}
beforeEach(function() { setup() })
afterEach(function() { Helpers.dropAllTables() })
describe('mono-directional', function() {
it("adds the foreign key", function() {
User.hasMany(Task)
expect(Task.attributes.UserId).toEqual("INTEGER")
})
it('adds the foreign key with underscore', function() {
User = sequelize.define('User', { username: Sequelize.STRING })
Task = sequelize.define('Task', { title: Sequelize.STRING }, { underscored: true })
Task.hasMany(User)
expect(User.attributes.task_id).toBeDefined()
})
it('uses the passed foreign key', function() {
User.hasMany(Task, { foreignKey: 'person_id' })
expect(Task.attributes.person_id).toEqual("INTEGER")
})
it('defines getters and setters', function() {
User.hasMany(Task)
var u = User.build({username: 'asd'})
expect(u.setTasks).toBeDefined()
expect(u.getTasks).toBeDefined()
})
it("defines getters and setters according to the 'as' option", function() {
User.hasMany(Task, {as: 'Tasks'})
var u = User.build({username: 'asd'})
expect(u.setTasks).toBeDefined()
expect(u.getTasks).toBeDefined()
})
it("sets and gets associated objects", function() {
var user, task1, task2;
User.hasMany(Task, { as: 'Tasks' })
Helpers.async(function(done) {
User.sync({ force: true }).success(function() {
Task.sync({ force: true }).success(done)
})
})
Helpers.async(function(done) {
User.create({username: 'name'}).success(function(_user) {
Task.create({title: 'task1'}).success(function(_task1) {
Task.create({title: 'task2'}).success(function(_task2) {
user = _user
task1 = _task1
task2 = _task2
done()
})
})
})
})
Helpers.async(function(done) {
user.setTasks([task1, task2]).success(function() {
user.getTasks().success(function(tasks) {
expect(tasks.length).toEqual(2)
done()
})
})
})
})
})
describe("when a join table name is specified", function() {
var Table2 = sequelize.define('ms_table1', {foo: Sequelize.STRING})
, Table1 = sequelize.define('ms_table2', {foo: Sequelize.STRING})
Table1.hasMany(Table2, {joinTableName: 'table1_to_table2'})
Table2.hasMany(Table1, {joinTableName: 'table1_to_table2'})
it("should not use a combined name", function() {
expect(sequelize.modelManager.getModel('ms_table1sms_table2s')).toBeUndefined()
})
it("should use the specified name", function() {
expect(sequelize.modelManager.getModel('table1_to_table2')).toBeDefined()
})
})
describe('bi-directional', function() {
it('adds the foreign key', function() {
Task.hasMany(User)
User.hasMany(Task)
expect(Task.attributes.UserId).toBeUndefined()
expect(User.attributes.UserId).toBeUndefined()
var models = sequelize.modelFactoryManager.models.filter(function(model) {
return (model.tableName == (Task.tableName + User.tableName))
})
models.forEach(function(model) {
expect(model.attributes.UserId).toBeDefined()
expect(model.attributes.TaskId).toBeDefined()
})
})
it("adds the foreign key with underscores", function() {
User = sequelize.define('User', { username: Sequelize.STRING }, { underscored: true })
Task = sequelize.define('Task', { title: Sequelize.STRING })
Task.hasMany(User)
User.hasMany(Task)
expect(Task.attributes.user_id).toBeUndefined()
expect(User.attributes.user_id).toBeUndefined()
var models = sequelize.modelFactoryManager.models.filter(function(model) {
return (model.tableName == (Task.tableName + User.tableName))
})
models.forEach(function(model) {
expect(model.attributes.user_id).toBeDefined()
expect(model.attributes.TaskId).toBeDefined()
})
})
it("uses the passed foreign keys", function() {
User.hasMany(Task, { foreignKey: 'person_id' })
Task.hasMany(User, { foreignKey: 'work_item_id' })
var models = sequelize.modelFactoryManager.models.filter(function(model) {
return (model.tableName == (Task.tableName + User.tableName))
})
models.forEach(function(model) {
expect(model.attributes.person_id).toBeDefined()
expect(model.attributes.work_item_id).toBeDefined()
})
})
it("defines getters and setters", function() {
User.hasMany(Task)
Task.hasMany(User)
var u = User.build({ username: 'asd' })
expect(u.setTasks).toBeDefined()
expect(u.getTasks).toBeDefined()
var t = Task.build({ title: 'foobar' })
expect(t.setUsers).toBeDefined()
expect(t.getUsers).toBeDefined()
})
it("defines getters and setters according to the 'as' option", function() {
User.hasMany(Task, { as: 'Tasks' })
Task.hasMany(User, { as: 'Users' })
var u = User.build({ username: 'asd' })
expect(u.setTasks).toBeDefined()
expect(u.getTasks).toBeDefined()
var t = Task.build({ title: 'asd' })
expect(t.setUsers).toBeDefined()
expect(t.getUsers).toBeDefined()
})
it("sets and gets the corrected associated objects", function() {
var users = []
, tasks = []
User.hasMany(Task, {as: 'Tasks'})
Task.hasMany(User, {as: 'Users'})
Helpers.async(function(done) {
User.sync({force: true}).success(function() {
Task.sync({force: true}).success(done)
})
})
Helpers.async(function(done) {
User.create({username: 'name'}).success(function(user1) {
User.create({username: 'name2'}).success(function(user2) {
Task.create({title: 'task1'}).success(function(task1) {
Task.create({title: 'task2'}).success(function(task2) {
users.push(user1)
users.push(user2)
tasks.push(task1)
tasks.push(task2)
done()
})
})
})
})
})
Helpers.async(function(done) {
users[0].setTasks(tasks).success(function() {
users[0].getTasks().success(function(_tasks) {
expect(_tasks.length).toEqual(2)
tasks[1].setUsers(users).success(function() {
tasks[1].getUsers().success(function(_users) {
expect(users.length).toEqual(2)
done()
})
})
})
})
})
})
})
it("build the connector models name", function() {
Helpers.async(function(done) {
var Person = sequelize.define('Person', { name: Sequelize.STRING })
Person.hasMany(Person, {as: 'Children'})
Person.hasMany(Person, {as: 'Friends'})
Person.hasMany(Person, {as: 'CoWorkers'})
Person.sync({force: true}).success(function() {
var modelNames = sequelize.modelFactoryManager.models.map(function(model) { return model.tableName })
, expectation = ["Persons", "ChildrenPersons", "CoWorkersPersons", "FriendsPersons"]
expectation.forEach(function(ex) {
expect(modelNames.indexOf(ex) > -1).toBeTruthy()
})
done()
})
})
})
it("gets and sets the connector models", function() {
Helpers.async(function(done) {
var Person = sequelize.define('Person', { name: Sequelize.STRING })
Person.hasMany(Person, {as: 'Children'})
Person.hasMany(Person, {as: 'Friends'})
Person.hasMany(Person, {as: 'CoWorkers'})
Person.sync({force: true}).success(function() {
Person.create({name: 'foobar'}).success(function(person) {
Person.create({name: 'friend'}).success(function(friend) {
person.setFriends([friend]).success(function() {
person.getFriends().success(function(friends) {
expect(friends.length).toEqual(1)
expect(friends[0].name).toEqual('friend')
done()
})
})
})
})
})
})
})
})
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!