Browse files

added updateAttributes + refactored isNewRecord method + documentation

  • Loading branch information...
1 parent 3ee9e12 commit cb9c088c9edc6ede5b1c9af6c93a99575c3088c6 @sdepold committed Apr 28, 2011
View
4 doc/views/index/code/instances/models-1.ejs → doc/views/index/code/instances/build.ejs
@@ -1,9 +1,9 @@
-var project = new Project({
+var project = Project.build({
title: 'my awesome project',
description: 'woot woot. this will make me a rich man'
})
-var task = new Task({
+var task = Task.build({
title: 'specify the project idea',
description: 'bla',
deadline: new Date()
View
12 doc/views/index/code/instances/defaults.ejs
@@ -0,0 +1,12 @@
+// first define the model
+var Task = sequelize.define('Project', {
+ title: Sequelize.STRING,
+ rating: {type: Sequelize.STRING, defaultValue: 3}
+})
+
+// now instantiate an object
+var task = Task.build({title: 'very important task'})
+task.title
+// ==> 'very important task'
+task.rating
+// ==> 3
View
12 doc/views/index/code/instances/models-2.ejs
@@ -1,12 +0,0 @@
-project.save(function() {
- // my nice callback stuff
-})
-
-task.save(function() {
- // some other stuff
-})
-
-new Task({ title: 'foo', description: 'bar', deadline: new Date()})
-.save(function(anotherTask) {
- // you can now access the currently saved task with the variable <i>anotherTask</i>... nice!
-})
View
13 doc/views/index/code/instances/save.ejs
@@ -0,0 +1,13 @@
+project.save().on('success', function() {
+ // my nice callback stuff
+})
+
+task.save().on('failure', function() {
+ // mhhh, wth!
+})
+
+// you can also build, save and access the object with chaining:
+Task.build({ title: 'foo', description: 'bar', deadline: new Date()})
+.save().on('success', function(anotherTask) {
+ // you can now access the currently saved task with the variable <i>anotherTask</i>... nice!
+})
View
17 doc/views/index/code/sync-with-db/model-sync.ejs
@@ -0,0 +1,17 @@
+// create my nice tables:
+Project.sync() // will emit success or failure event
+Task.sync() // will emit success or failure event
+
+// force the creation!
+Project.sync({force: true}) // this will drop the table first and re-create it afterwards
+
+// drop the tables:
+Project.drop() // will emit success or failure event
+Task.drop() // will emit success or failure event
+
+// event handling:
+Project.[sync|drop]().on('success', function() {
+ // ok ... everything is nice!
+}).on('failure', function() {
+ // oooh, did you entered wrong database credentials?
+})
View
15 doc/views/index/code/sync-with-db/sequelize-sync.ejs
@@ -0,0 +1,15 @@
+// create all tables... now!
+sequelize.sync() // will emit success or failure
+
+// force it!
+sequelize.sync({force: true}) // emit ... nomnomnom
+
+// want to drop 'em all?
+sequelize.drop() // I guess you've got it (emit)
+
+// emit handling:
+sequelize.[sync|drop]().on('success', function() {
+ // woot woot
+}).on('failure', function() {
+ // whooops
+})
View
13 doc/views/index/code/sync-with-db/sync-1.ejs
@@ -1,13 +0,0 @@
-// create my nice tables:
-Project.sync(callback)
-Task.sync(callback)
-
-// drop the tables:
-Project.drop(callback)
-Task.drop(callback)
-
-// with error handling:
-Project.[sync|drop](function(table, error) {
- if(error) // oooh, did you entered wrong database credentials?
- else // ok ... everything is nice!
-})
View
11 doc/views/index/code/sync-with-db/sync-2.ejs
@@ -1,11 +0,0 @@
-// create all tables... now!
-sequelize.sync(callback)
-
-// and drop it!
-sequelize.drop(callback)
-
-// with error handling:
-sequelize.[sync|drop](function(errors) {
- if(errors.length > 0) // whooops
- else // woot woot
-})
View
10 doc/views/index/code/usage/basic-mapping.ejs
@@ -12,13 +12,13 @@ var Task = sequelize.define('Task', {
// You can also set some options:
var Foo = sequelize.define('Foo', {
// instantiating will automatically set the flag to true if not set
- flag: { dataType: Sequelize.BOOLEAN, allowNull: false, defaultValue: true},
+ flag: { type: Sequelize.BOOLEAN, allowNull: false, defaultValue: true},
// setting no title will throw an error when trying to save
- title: { dataType: Sequelize.STRING, allowNull: false},
+ title: { type: Sequelize.STRING, allowNull: false},
// creating 2 objects with the same value will throw an error
- someUnique: {dataType: Sequelize.STRING, unique: false},
+ someUnique: {type: Sequelize.STRING, unique: false},
// go on reading for further information about primary keys
- identifier: { dataType: Sequelize.String, primaryKey: true},
+ identifier: { type: Sequelize.String, primaryKey: true},
// autoIncrement can be used to create auto_incrementing integer columns
- incrementMe: { dataType: Sequelize.INTEGER, autoIncrement: true }
+ incrementMe: { type: Sequelize.INTEGER, autoIncrement: true }
})
View
13 doc/views/index/instances.ejs
@@ -1,10 +1,15 @@
-In order to create instances of defined classes just do it as follows:
+In order to create instances of defined classes just do as follows. You might recognize the syntax if you coded Ruby in the past.
+Using the <i>build</i>-method will return an unsaved object, which you explicitly have to save.
-<pre><%- koala(".js", partial("code/instances/models-1.ejs")) %></pre>
+<pre><%- koala(".js", partial("code/instances/build.ejs")) %></pre>
-To save it in the database use the save method and pass a callback to it, if needed:
+Builded instances will automatically get default values when they were defined:
-<pre><%- koala(".js", partial("code/instances/models-2.ejs")) %></pre>
+<pre><%- koala('.js', partial("code/instances/defaults.ejs")) %></pre>
+
+To get it stored in the database, use the save method and catch the events, ..., if needed:
+
+<pre><%- koala(".js", partial("code/instances/save.ejs")) %></pre>
Now lets change some values and save changes to the database... There are two ways to do that:
View
4 doc/views/index/sync-with-db.ejs
@@ -3,9 +3,9 @@ your model structures and let the library do the rest.<br><br>
Currently supported is the creation and deletion of tables:
-<pre><%- koala(".js", partial("code/sync-with-db/sync-1.ejs")) %></pre>
+<pre><%- koala(".js", partial("code/sync-with-db/model-sync.ejs")) %></pre>
Because synchronizing and dropping all of your tables might be a lot of lines to write, you can also let
Sequelize do the work for you:
-<pre><%- koala(".js", partial("code/sync-with-db/sync-2.ejs")) %></pre>
+<pre><%- koala(".js", partial("code/sync-with-db/sequelize-sync.ejs")) %></pre>
View
5 lib/sequelize/model-definition.js
@@ -99,10 +99,11 @@ ModelDefinition.prototype.find = function(options) {
return this.query(query, this, {plain: true})
}
-ModelDefinition.prototype.build = function(values) {
+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) {
@@ -122,6 +123,8 @@ ModelDefinition.prototype.build = function(values) {
association.injectSetter(instance)
})
+ instance.isNewRecord = options.hasOwnProperty('isNewRecord') ? options.isNewRecord : true
+
return instance
}
View
52 lib/sequelize/model.js
@@ -57,14 +57,43 @@ Model.prototype.save = function() {
if(this.hasOwnProperty(attr))
this[attr] = new Date()
- if(this.isNewRecord)
- return this.query(QueryGenerator.insertQuery(this.definition.tableName, this.values))
- else {
- var identifier = this.options.hasPrimaryKeys ? {where: this.definition.primaryKeys} : this.id
+ 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) })
+ })
+ return eventEmitter.run()
+ } else {
+ var identifier = this.options.hasPrimaryKeys ? this.definition.primaryKeys : 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()
@@ -75,8 +104,19 @@ Model.prototype.destroy = function() {
}
}
-Model.prototype.__defineGetter__('isNewRecord', function() {
- return this.id == null
+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() {
View
2 lib/sequelize/query-generator.js
@@ -164,7 +164,7 @@ var QueryGenerator = module.exports = {
return Utils._.map(hash, function(value, key) {
var _value = Utils.escape(value)
, _key = Utils.addTicks(key)
-
+
return (_value == 'NULL') ? _key + " IS NULL" : [_key, _value].join("=")
}).join(" AND ")
}
View
2 lib/sequelize/query.js
@@ -40,7 +40,7 @@ Query.prototype.onSuccess = function(query, results, fields) {
// 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) })
+ result = results.map(function(result) { return self.callee.build(result, {isNewRecord: false}) })
if(this.options.plain)
result = (result.length == 0) ? null : result[0]
View
2 test/Model/belongsTo.js
@@ -63,7 +63,7 @@ module.exports = {
var Task = sequelize.define('Task' + parseInt(Math.random() * 99999999), { title: Sequelize.STRING })
Task.belongsTo(User, {as: 'User'})
-
+
User.sync({force: true}).on('success', function() {
Task.sync({force: true}).on('success', function() {
User.create({username: 'asd'}).on('success', function(u) {
View
10 test/Model/build.js
@@ -25,11 +25,11 @@ module.exports = {
},
'build should fill the object with default values': function() {
var Task = sequelize.define('Task' + parseInt(Math.random() * 99999999), {
- title: {dataType: Sequelize.STRING, defaultValue: 'a task!'},
- foo: {dataType: Sequelize.INTEGER, defaultValue: 2},
- bar: {dataType: Sequelize.DATE},
- foobar: {dataType: Sequelize.TEXT, defaultValue: 'asd'},
- flag: {dataType: Sequelize.BOOLEAN, defaultValue: false}
+ title: {type: Sequelize.STRING, defaultValue: 'a task!'},
+ foo: {type: Sequelize.INTEGER, defaultValue: 2},
+ bar: {type: Sequelize.DATE},
+ foobar: {type: Sequelize.TEXT, defaultValue: 'asd'},
+ flag: {type: Sequelize.BOOLEAN, defaultValue: false}
})
assert.eql(Task.build().title, 'a task!')
assert.eql(Task.build().foo, 2)
View
43 test/Model/is-new-record.js
@@ -20,5 +20,48 @@ module.exports = {
assert.eql(users[0].isNewRecord, true)
exit(function(){})
})
+ },
+ 'should be false for saved objects': function(exit) {
+ initUsers(1, function(users, User) {
+ users[0].save().on('success', function(user) {
+ assert.eql(user.isNewRecord, false)
+ exit(function(){})
+ })
+ })
+ },
+ 'should be false for created objects': function(exit) {
+ initUsers(1, function(users, User) {
+ User.create({name: 'user'}).on('success', function(user) {
+ assert.eql(user.isNewRecord, false)
+ exit(function(){})
+ })
+ })
+ },
+ 'should be false for find': function(exit) {
+ initUsers(1, function(users, User) {
+ User.create({name: 'user'}).on('success', function(user) {
+ User.find(user.id).on('success', function(user) {
+ assert.eql(user.isNewRecord, false)
+ exit(function(){})
+ })
+ })
+ })
+ },
+ 'should be false for findAll': function(exit) {
+ var chainer = new Sequelize.Utils.QueryChainer
+
+ initUsers(10, function(users, User) {
+ users.forEach(function(user) {
+ chainer.add(user.save())
+ })
+ chainer.run().on('success', function() {
+ User.findAll().on('success', function(users) {
+ users.forEach(function(u) {
+ assert.eql(u.isNewRecord, false)
+ })
+ exit(function() {})
+ })
+ })
+ })
}
}
View
48 test/Model/update-attributes.js
@@ -0,0 +1,48 @@
+var assert = require("assert")
+ , config = require("./../config")
+ , Sequelize = require("./../../index")
+ , sequelize = new Sequelize(config.database, config.username, config.password, {logging: false})
+ , User = sequelize.define('User' + parseInt(Math.random() * 9999999999999), { name: Sequelize.STRING, bio: Sequelize.TEXT })
+
+module.exports = {
+ 'it should update the attributes': function(exit) {
+ User.sync({force:true}).on('success', function() {
+ User.create({name: 'snafu'}).on('success', function(user) {
+ assert.eql(user.name, 'snafu')
+ user.updateAttributes({name: 'foobar'}).on('success', function(user) {
+ assert.eql(user.name, 'foobar')
+ exit(function(){})
+ })
+ })
+ })
+ },
+ 'it should not set attributes which were not defined': function(exit) {
+ User.sync({force:true}).on('success', function() {
+ User.create({name: 'snafu'}).on('success', function(user) {
+ user.updateAttributes({name: 'foobar', foo: 'bar'}).on('success', function(user) {
+ assert.eql(user.name, 'foobar')
+ assert.isUndefined(user.foo)
+ exit(function(){})
+ })
+ })
+ })
+ },
+ 'it should not set primary keys or timestamps': function(exit) {
+ User = sequelize.define('User' + parseInt(Math.random() * 9999999999999), {
+ name: Sequelize.STRING, bio: Sequelize.TEXT, identifier: {type: Sequelize.STRING, primaryKey: true}
+ })
+
+ User.sync({force:true}).on('success', function() {
+ User.create({name: 'snafu', identifier: 'identifier'}).on('success', function(user) {
+ var oldCreatedAt = user.createdAt
+ , oldIdentifier = user.identifier
+
+ user.updateAttributes({name: 'foobar', createdAt: new Date(2000, 1, 1), identifier: 'another identifier'}).on('success', function(user) {
+ assert.eql(user.createdAt, oldCreatedAt)
+ assert.eql(user.identifier, oldIdentifier)
+ exit(function(){})
+ })
+ })
+ })
+ }
+}

0 comments on commit cb9c088

Please sign in to comment.