Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

added updateAttributes + refactored isNewRecord method + documentation

  • Loading branch information...
commit cb9c088c9edc6ede5b1c9af6c93a99575c3088c6 1 parent 3ee9e12
Sascha Depold authored
4 doc/views/index/code/instances/models-1.ejs → doc/views/index/code/instances/build.ejs
... ... @@ -1,9 +1,9 @@
1   -var project = new Project({
  1 +var project = Project.build({
2 2 title: 'my awesome project',
3 3 description: 'woot woot. this will make me a rich man'
4 4 })
5 5
6   -var task = new Task({
  6 +var task = Task.build({
7 7 title: 'specify the project idea',
8 8 description: 'bla',
9 9 deadline: new Date()
12 doc/views/index/code/instances/defaults.ejs
... ... @@ -0,0 +1,12 @@
  1 +// first define the model
  2 +var Task = sequelize.define('Project', {
  3 + title: Sequelize.STRING,
  4 + rating: {type: Sequelize.STRING, defaultValue: 3}
  5 +})
  6 +
  7 +// now instantiate an object
  8 +var task = Task.build({title: 'very important task'})
  9 +task.title
  10 +// ==> 'very important task'
  11 +task.rating
  12 +// ==> 3
12 doc/views/index/code/instances/models-2.ejs
... ... @@ -1,12 +0,0 @@
1   -project.save(function() {
2   - // my nice callback stuff
3   -})
4   -
5   -task.save(function() {
6   - // some other stuff
7   -})
8   -
9   -new Task({ title: 'foo', description: 'bar', deadline: new Date()})
10   -.save(function(anotherTask) {
11   - // you can now access the currently saved task with the variable <i>anotherTask</i>... nice!
12   -})
13 doc/views/index/code/instances/save.ejs
... ... @@ -0,0 +1,13 @@
  1 +project.save().on('success', function() {
  2 + // my nice callback stuff
  3 +})
  4 +
  5 +task.save().on('failure', function() {
  6 + // mhhh, wth!
  7 +})
  8 +
  9 +// you can also build, save and access the object with chaining:
  10 +Task.build({ title: 'foo', description: 'bar', deadline: new Date()})
  11 +.save().on('success', function(anotherTask) {
  12 + // you can now access the currently saved task with the variable <i>anotherTask</i>... nice!
  13 +})
17 doc/views/index/code/sync-with-db/model-sync.ejs
... ... @@ -0,0 +1,17 @@
  1 +// create my nice tables:
  2 +Project.sync() // will emit success or failure event
  3 +Task.sync() // will emit success or failure event
  4 +
  5 +// force the creation!
  6 +Project.sync({force: true}) // this will drop the table first and re-create it afterwards
  7 +
  8 +// drop the tables:
  9 +Project.drop() // will emit success or failure event
  10 +Task.drop() // will emit success or failure event
  11 +
  12 +// event handling:
  13 +Project.[sync|drop]().on('success', function() {
  14 + // ok ... everything is nice!
  15 +}).on('failure', function() {
  16 + // oooh, did you entered wrong database credentials?
  17 +})
15 doc/views/index/code/sync-with-db/sequelize-sync.ejs
... ... @@ -0,0 +1,15 @@
  1 +// create all tables... now!
  2 +sequelize.sync() // will emit success or failure
  3 +
  4 +// force it!
  5 +sequelize.sync({force: true}) // emit ... nomnomnom
  6 +
  7 +// want to drop 'em all?
  8 +sequelize.drop() // I guess you've got it (emit)
  9 +
  10 +// emit handling:
  11 +sequelize.[sync|drop]().on('success', function() {
  12 + // woot woot
  13 +}).on('failure', function() {
  14 + // whooops
  15 +})
13 doc/views/index/code/sync-with-db/sync-1.ejs
... ... @@ -1,13 +0,0 @@
1   -// create my nice tables:
2   -Project.sync(callback)
3   -Task.sync(callback)
4   -
5   -// drop the tables:
6   -Project.drop(callback)
7   -Task.drop(callback)
8   -
9   -// with error handling:
10   -Project.[sync|drop](function(table, error) {
11   - if(error) // oooh, did you entered wrong database credentials?
12   - else // ok ... everything is nice!
13   -})
11 doc/views/index/code/sync-with-db/sync-2.ejs
... ... @@ -1,11 +0,0 @@
1   -// create all tables... now!
2   -sequelize.sync(callback)
3   -
4   -// and drop it!
5   -sequelize.drop(callback)
6   -
7   -// with error handling:
8   -sequelize.[sync|drop](function(errors) {
9   - if(errors.length > 0) // whooops
10   - else // woot woot
11   -})
10 doc/views/index/code/usage/basic-mapping.ejs
@@ -12,13 +12,13 @@ var Task = sequelize.define('Task', {
12 12 // You can also set some options:
13 13 var Foo = sequelize.define('Foo', {
14 14 // instantiating will automatically set the flag to true if not set
15   - flag: { dataType: Sequelize.BOOLEAN, allowNull: false, defaultValue: true},
  15 + flag: { type: Sequelize.BOOLEAN, allowNull: false, defaultValue: true},
16 16 // setting no title will throw an error when trying to save
17   - title: { dataType: Sequelize.STRING, allowNull: false},
  17 + title: { type: Sequelize.STRING, allowNull: false},
18 18 // creating 2 objects with the same value will throw an error
19   - someUnique: {dataType: Sequelize.STRING, unique: false},
  19 + someUnique: {type: Sequelize.STRING, unique: false},
20 20 // go on reading for further information about primary keys
21   - identifier: { dataType: Sequelize.String, primaryKey: true},
  21 + identifier: { type: Sequelize.String, primaryKey: true},
22 22 // autoIncrement can be used to create auto_incrementing integer columns
23   - incrementMe: { dataType: Sequelize.INTEGER, autoIncrement: true }
  23 + incrementMe: { type: Sequelize.INTEGER, autoIncrement: true }
24 24 })
13 doc/views/index/instances.ejs
... ... @@ -1,10 +1,15 @@
1   -In order to create instances of defined classes just do it as follows:
  1 +In order to create instances of defined classes just do as follows. You might recognize the syntax if you coded Ruby in the past.
  2 +Using the <i>build</i>-method will return an unsaved object, which you explicitly have to save.
2 3
3   -<pre><%- koala(".js", partial("code/instances/models-1.ejs")) %></pre>
  4 +<pre><%- koala(".js", partial("code/instances/build.ejs")) %></pre>
4 5
5   -To save it in the database use the save method and pass a callback to it, if needed:
  6 +Builded instances will automatically get default values when they were defined:
6 7
7   -<pre><%- koala(".js", partial("code/instances/models-2.ejs")) %></pre>
  8 +<pre><%- koala('.js', partial("code/instances/defaults.ejs")) %></pre>
  9 +
  10 +To get it stored in the database, use the save method and catch the events, ..., if needed:
  11 +
  12 +<pre><%- koala(".js", partial("code/instances/save.ejs")) %></pre>
8 13
9 14 Now lets change some values and save changes to the database... There are two ways to do that:
10 15
4 doc/views/index/sync-with-db.ejs
@@ -3,9 +3,9 @@ your model structures and let the library do the rest.<br><br>
3 3
4 4 Currently supported is the creation and deletion of tables:
5 5
6   -<pre><%- koala(".js", partial("code/sync-with-db/sync-1.ejs")) %></pre>
  6 +<pre><%- koala(".js", partial("code/sync-with-db/model-sync.ejs")) %></pre>
7 7
8 8 Because synchronizing and dropping all of your tables might be a lot of lines to write, you can also let
9 9 Sequelize do the work for you:
10 10
11   -<pre><%- koala(".js", partial("code/sync-with-db/sync-2.ejs")) %></pre>
  11 +<pre><%- koala(".js", partial("code/sync-with-db/sequelize-sync.ejs")) %></pre>
5 lib/sequelize/model-definition.js
@@ -99,10 +99,11 @@ ModelDefinition.prototype.find = function(options) {
99 99 return this.query(query, this, {plain: true})
100 100 }
101 101
102   -ModelDefinition.prototype.build = function(values) {
  102 +ModelDefinition.prototype.build = function(values, options) {
103 103 var instance = new Model(values, Utils._.extend(this.options, {hasPrimaryKeys: this.hasPrimaryKeys}))
104 104 , self = this
105 105
  106 + options = options || {}
106 107 instance.definition = this
107 108
108 109 Utils._.map(this.attributes, function(definition, name) {
@@ -122,6 +123,8 @@ ModelDefinition.prototype.build = function(values) {
122 123 association.injectSetter(instance)
123 124 })
124 125
  126 + instance.isNewRecord = options.hasOwnProperty('isNewRecord') ? options.isNewRecord : true
  127 +
125 128 return instance
126 129 }
127 130
52 lib/sequelize/model.js
@@ -57,14 +57,43 @@ Model.prototype.save = function() {
57 57 if(this.hasOwnProperty(attr))
58 58 this[attr] = new Date()
59 59
60   - if(this.isNewRecord)
61   - return this.query(QueryGenerator.insertQuery(this.definition.tableName, this.values))
62   - else {
63   - var identifier = this.options.hasPrimaryKeys ? {where: this.definition.primaryKeys} : this.id
  60 + if(this.isNewRecord) {
  61 + var self = this
  62 + var eventEmitter = new Utils.CustomEventEmitter(function() {
  63 + self.query(QueryGenerator.insertQuery(self.definition.tableName, self.values))
  64 + .on('success', function(obj) {
  65 + obj.isNewRecord = false
  66 + eventEmitter.emit('success', obj)
  67 + })
  68 + .on('failure', function(err) { eventEmitter.emit('failure', err) })
  69 + })
  70 + return eventEmitter.run()
  71 + } else {
  72 + var identifier = this.options.hasPrimaryKeys ? this.definition.primaryKeys : this.id
64 73 return this.query(QueryGenerator.updateQuery(this.definition.tableName, this.values, identifier))
65 74 }
66 75 }
67 76
  77 +Model.prototype.updateAttributes = function(updates) {
  78 + var self = this
  79 +
  80 + var readOnlyAttributes = Utils._.keys(this.definition.primaryKeys)
  81 + readOnlyAttributes.push('id')
  82 + readOnlyAttributes.push('createdAt')
  83 + readOnlyAttributes.push('updatedAt')
  84 + readOnlyAttributes.push('deletedAt')
  85 +
  86 + Utils._.each(updates, function(value, attr) {
  87 + var updateAllowed = (
  88 + (readOnlyAttributes.indexOf(attr) == -1) &&
  89 + (readOnlyAttributes.indexOf(Utils._.underscored(attr)) == -1) &&
  90 + (self.attributes.indexOf(attr) > -1)
  91 + )
  92 + updateAllowed && (self[attr] = value)
  93 + })
  94 + return this.save()
  95 +}
  96 +
68 97 Model.prototype.destroy = function() {
69 98 if(this.options.timestamps && this.options.paranoid) {
70 99 this[this.options.underscored ? 'deleted_at' : 'deletedAt'] = new Date()
@@ -75,8 +104,19 @@ Model.prototype.destroy = function() {
75 104 }
76 105 }
77 106
78   -Model.prototype.__defineGetter__('isNewRecord', function() {
79   - return this.id == null
  107 +Model.prototype.__defineGetter__("identifiers", function() {
  108 + var primaryKeys = Utils._.keys(this.definition.primaryKeys)
  109 + , result = {}
  110 + , self = this
  111 +
  112 + if(!this.definition.hasPrimaryKeys)
  113 + primaryKeys = ['id']
  114 +
  115 + primaryKeys.forEach(function(identifier) {
  116 + result[identifier] = self[identifier]
  117 + })
  118 +
  119 + return result
80 120 })
81 121
82 122 Model.prototype.__defineGetter__('isDeleted', function() {
2  lib/sequelize/query-generator.js
@@ -164,7 +164,7 @@ var QueryGenerator = module.exports = {
164 164 return Utils._.map(hash, function(value, key) {
165 165 var _value = Utils.escape(value)
166 166 , _key = Utils.addTicks(key)
167   -
  167 +
168 168 return (_value == 'NULL') ? _key + " IS NULL" : [_key, _value].join("=")
169 169 }).join(" AND ")
170 170 }
2  lib/sequelize/query.js
@@ -40,7 +40,7 @@ Query.prototype.onSuccess = function(query, results, fields) {
40 40 // transform results into real model instances
41 41 // return the first real model instance if options.plain is set (e.g. Model.find)
42 42 if (query.indexOf('SELECT') == 0) {
43   - result = results.map(function(result) { return self.callee.build(result) })
  43 + result = results.map(function(result) { return self.callee.build(result, {isNewRecord: false}) })
44 44
45 45 if(this.options.plain)
46 46 result = (result.length == 0) ? null : result[0]
2  test/Model/belongsTo.js
@@ -63,7 +63,7 @@ module.exports = {
63 63 var Task = sequelize.define('Task' + parseInt(Math.random() * 99999999), { title: Sequelize.STRING })
64 64
65 65 Task.belongsTo(User, {as: 'User'})
66   -
  66 +
67 67 User.sync({force: true}).on('success', function() {
68 68 Task.sync({force: true}).on('success', function() {
69 69 User.create({username: 'asd'}).on('success', function(u) {
10 test/Model/build.js
@@ -25,11 +25,11 @@ module.exports = {
25 25 },
26 26 'build should fill the object with default values': function() {
27 27 var Task = sequelize.define('Task' + parseInt(Math.random() * 99999999), {
28   - title: {dataType: Sequelize.STRING, defaultValue: 'a task!'},
29   - foo: {dataType: Sequelize.INTEGER, defaultValue: 2},
30   - bar: {dataType: Sequelize.DATE},
31   - foobar: {dataType: Sequelize.TEXT, defaultValue: 'asd'},
32   - flag: {dataType: Sequelize.BOOLEAN, defaultValue: false}
  28 + title: {type: Sequelize.STRING, defaultValue: 'a task!'},
  29 + foo: {type: Sequelize.INTEGER, defaultValue: 2},
  30 + bar: {type: Sequelize.DATE},
  31 + foobar: {type: Sequelize.TEXT, defaultValue: 'asd'},
  32 + flag: {type: Sequelize.BOOLEAN, defaultValue: false}
33 33 })
34 34 assert.eql(Task.build().title, 'a task!')
35 35 assert.eql(Task.build().foo, 2)
43 test/Model/is-new-record.js
@@ -20,5 +20,48 @@ module.exports = {
20 20 assert.eql(users[0].isNewRecord, true)
21 21 exit(function(){})
22 22 })
  23 + },
  24 + 'should be false for saved objects': function(exit) {
  25 + initUsers(1, function(users, User) {
  26 + users[0].save().on('success', function(user) {
  27 + assert.eql(user.isNewRecord, false)
  28 + exit(function(){})
  29 + })
  30 + })
  31 + },
  32 + 'should be false for created objects': function(exit) {
  33 + initUsers(1, function(users, User) {
  34 + User.create({name: 'user'}).on('success', function(user) {
  35 + assert.eql(user.isNewRecord, false)
  36 + exit(function(){})
  37 + })
  38 + })
  39 + },
  40 + 'should be false for find': function(exit) {
  41 + initUsers(1, function(users, User) {
  42 + User.create({name: 'user'}).on('success', function(user) {
  43 + User.find(user.id).on('success', function(user) {
  44 + assert.eql(user.isNewRecord, false)
  45 + exit(function(){})
  46 + })
  47 + })
  48 + })
  49 + },
  50 + 'should be false for findAll': function(exit) {
  51 + var chainer = new Sequelize.Utils.QueryChainer
  52 +
  53 + initUsers(10, function(users, User) {
  54 + users.forEach(function(user) {
  55 + chainer.add(user.save())
  56 + })
  57 + chainer.run().on('success', function() {
  58 + User.findAll().on('success', function(users) {
  59 + users.forEach(function(u) {
  60 + assert.eql(u.isNewRecord, false)
  61 + })
  62 + exit(function() {})
  63 + })
  64 + })
  65 + })
23 66 }
24 67 }
48 test/Model/update-attributes.js
... ... @@ -0,0 +1,48 @@
  1 +var assert = require("assert")
  2 + , config = require("./../config")
  3 + , Sequelize = require("./../../index")
  4 + , sequelize = new Sequelize(config.database, config.username, config.password, {logging: false})
  5 + , User = sequelize.define('User' + parseInt(Math.random() * 9999999999999), { name: Sequelize.STRING, bio: Sequelize.TEXT })
  6 +
  7 +module.exports = {
  8 + 'it should update the attributes': function(exit) {
  9 + User.sync({force:true}).on('success', function() {
  10 + User.create({name: 'snafu'}).on('success', function(user) {
  11 + assert.eql(user.name, 'snafu')
  12 + user.updateAttributes({name: 'foobar'}).on('success', function(user) {
  13 + assert.eql(user.name, 'foobar')
  14 + exit(function(){})
  15 + })
  16 + })
  17 + })
  18 + },
  19 + 'it should not set attributes which were not defined': function(exit) {
  20 + User.sync({force:true}).on('success', function() {
  21 + User.create({name: 'snafu'}).on('success', function(user) {
  22 + user.updateAttributes({name: 'foobar', foo: 'bar'}).on('success', function(user) {
  23 + assert.eql(user.name, 'foobar')
  24 + assert.isUndefined(user.foo)
  25 + exit(function(){})
  26 + })
  27 + })
  28 + })
  29 + },
  30 + 'it should not set primary keys or timestamps': function(exit) {
  31 + User = sequelize.define('User' + parseInt(Math.random() * 9999999999999), {
  32 + name: Sequelize.STRING, bio: Sequelize.TEXT, identifier: {type: Sequelize.STRING, primaryKey: true}
  33 + })
  34 +
  35 + User.sync({force:true}).on('success', function() {
  36 + User.create({name: 'snafu', identifier: 'identifier'}).on('success', function(user) {
  37 + var oldCreatedAt = user.createdAt
  38 + , oldIdentifier = user.identifier
  39 +
  40 + user.updateAttributes({name: 'foobar', createdAt: new Date(2000, 1, 1), identifier: 'another identifier'}).on('success', function(user) {
  41 + assert.eql(user.createdAt, oldCreatedAt)
  42 + assert.eql(user.identifier, oldIdentifier)
  43 + exit(function(){})
  44 + })
  45 + })
  46 + })
  47 + }
  48 +}

0 comments on commit cb9c088

Please sign in to comment.
Something went wrong with that request. Please try again.