Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Evented models

  • Loading branch information...
commit 44faed0a0cf52f89d758597857090013f4e4d38a 1 parent 26c9b80
mde authored
Showing with 189 additions and 16 deletions.
  1. +19 −5 lib/adapters/base_adapter.js
  2. +62 −11 lib/index.js
  3. +108 −0 test/events.js
24 lib/adapters/base_adapter.js
View
@@ -9,15 +9,29 @@ BaseAdapter = function () {
BaseAdapter.prototype = new EventEmitter();
utils.mixin(BaseAdapter.prototype, new (function () {
- this.load = function (query, callback) {};
+ this.load = function (query, callback) {
+ callback(null, []);
+ };
- this.update = function (data, query, callback) {};
+ this.update = function (data, query, callback) {
+ callback(null, true);
+ };
- this.remove = function (query, callback) {};
+ this.remove = function (query, callback) {
+ callback(null, true);
+ };
- this.insert = function (data, opts, callback) {};
+ this.insert = function (data, opts, callback) {
+ callback(null, data);
+ };
- this.createTable = function (names, callback) {};
+ this.createTable = function (names, callback) {
+ callback(null, null);
+ };
+
+ this.dropTable = function (names, callback) {
+ callback(null, null);
+ };
})());
73 lib/index.js
View
@@ -42,6 +42,7 @@ User = model.register('User', User);
*/
var model = {}
+ , EventEmitter = require('events').EventEmitter
, utils = require('utilities')
, adapters = require('./adapters')
, query; // Lazy-load query; it depends on model/index
@@ -313,6 +314,9 @@ utils.mixin(model, new (function () {
*/
obj.create = function () {
var args = Array.prototype.slice.call(arguments);
+ args.unshift('beforeCreate');
+ this.emit.apply(this, args);
+ args.shift();
args.unshift(name);
return model.createItem.apply(model, args);
};
@@ -354,21 +358,31 @@ utils.mixin(model, new (function () {
obj.save = function () {
var args = Array.prototype.slice.call(arguments)
- , callback = args.pop()
- , data = args.shift()
- , opts = args.shift() || {}
+ , data
+ , callback
+ , opts
, adapt
- , saved;
+ , saved
+ , isCollection;
+
+ args.unshift('beforeSave');
+ model[name].emit.apply(model[name], args);
+ args.shift();
+
+ data = args.shift()
+ callback = args.pop()
+ opts = args.shift() || {}
adapt = model.adapters[name];
if (!adapt) {
throw new Error('Adapter not found for ' + name);
}
+ isCollection = Array.isArray(data);
// Collection
// Bulk save only works on new items -- existing item should only
// be when doing instance.save
- if (Array.isArray(data)) {
+ if (isCollection) {
saved = false;
for (var i = 0, ii = data.length; i < ii; i++) {
item = data[i];
@@ -384,6 +398,8 @@ utils.mixin(model, new (function () {
}
// Single item
else {
+ data.emit('beforeSave');
+
saved = data._saved;
// Bail out if instance isn't valid
if (!data.isValid()) {
@@ -400,15 +416,25 @@ utils.mixin(model, new (function () {
}
}
- return adapt.insert.apply(adapt, [data, opts, callback]);
+ return adapt.insert.apply(adapt, [data, opts, function (err, data) {
+
+ if (!err) {
+ model[name].emit('save', data);
+ if (!isCollection) {
+ data.emit('save');
+ }
+ }
+
+ callback.apply(null, args);
+ }]);
};
obj.update = function () {
var args = Array.prototype.slice.call(arguments)
, Query = Query || require('./query/query').Query
, query
+ , data = args.shift()
, callback = args.pop()
- , data = args.shift() || {}
, query = args.shift() || {}
, opts = args.shift() || {}
, adapt;
@@ -424,7 +450,14 @@ utils.mixin(model, new (function () {
throw new Error('Adapter not found for ' + name);
}
- return adapt.update.apply(adapt, [data, query, callback]);
+ return adapt.update.apply(adapt, [data, query, function (err, data) {
+ if (!err) {
+ model[name].emit('save', data);
+ data.emit('save');
+ }
+
+ callback.apply(null, args);
+ }]);
};
obj.remove = function () {
@@ -477,6 +510,8 @@ utils.mixin(model, new (function () {
utils.mixin(ModelCtor, _createStaticMethodsMixin(name));
// Same with statics
utils.mixin(ModelCtor, defined);
+ // Same with EventEmitter methods
+ utils.enhance(ModelCtor, new EventEmitter());
// Mix any functions defined directly in the model-item definition
// 'constructor' into the original prototype, and set it as the prototype of the
@@ -484,6 +519,8 @@ utils.mixin(model, new (function () {
utils.mixin(origProto, defined);
ModelCtor.prototype = origProto;
+ // Add eventing to instances
+ utils.enhance(ModelCtor.prototype, new EventEmitter());
model[name] = ModelCtor;
@@ -504,11 +541,17 @@ utils.mixin(model, new (function () {
if (typeof item.afterCreate === 'function') {
item.afterCreate();
}
+ model[name].emit('create', item);
return item;
};
this.updateItem = function (item, params, opts) {
- var data = {};
+ var data = {}
+ , name = item.type;
+
+ item.emit('beforeUpdate')
+ model[name].emit('beforeUpdate', item);
+
utils.mixin(data, item);
utils.mixin(data, params);
this.validateAndUpdateFromParams(item, data, opts);
@@ -517,18 +560,24 @@ utils.mixin(model, new (function () {
if (typeof item.afterUpdate === 'function') {
item.afterUpdate();
}
+ item.emit('update')
+ model[name].emit('update', item);
return item;
};
this.validateAndUpdateFromParams = function (item, passedParams, opts) {
var params
- , type = model.descriptionRegistry[item.type]
+ , name = item.type
+ , type = model.descriptionRegistry[name]
, properties = type.properties
, validated = null
, errs = null
, camelizedKey
, val;
+ item.emit('beforeValidate')
+ model[name].emit('beforeValidate', item, passedParams);
+
// May be revalidating, clear errors
delete item.errors;
@@ -579,6 +628,9 @@ utils.mixin(model, new (function () {
item.errors = errs;
}
+ item.emit('validate')
+ model[name].emit('validate', item);
+
return item;
};
@@ -712,7 +764,6 @@ model.ModelDefinitionBase = function (name) {
}
};
});
-
};
model.ModelDescription = function (name) {
108 test/events.js
View
@@ -0,0 +1,108 @@
+var model = require('../lib')
+ , utils = require('utilities')
+ , assert = require('assert')
+ , User = require('./fixtures/user').User
+ , BaseAdapter = require('../lib/adapters/base_adapter').BaseAdapter
+ , EventEmitter = require('events').EventEmitter
+ , _params
+ , tests;
+
+_params = {
+ login: 'zzz',
+ password: 'asdf',
+ confirmPassword: 'asdf',
+ firstName: 'Neil'
+};
+
+model.adapters.User = new BaseAdapter();
+
+tests = {
+ 'static has EventEmitter emit': function () {
+ assert.equal('function', typeof User.emit);
+ }
+
+, 'static has EventEmitter addListener': function () {
+ assert.equal('function', typeof User.addListener);
+ }
+
+, 'static has EventEmitter once': function () {
+ assert.equal('function', typeof User.once);
+ }
+
+, 'emit static beforeCreate': function (next) {
+ User.once('beforeCreate', function (data) {
+ assert.equal('zzz', data.login);
+ next();
+ });
+ var user = User.create(_params);
+ }
+
+, 'emit static create': function (next) {
+ User.once('create', function (u) {
+ assert.ok(u instanceof User);
+ next();
+ });
+ var user = User.create(_params);
+ }
+
+, 'emit static beforeUpdate': function (next) {
+ User.once('beforeUpdate', function (u) {
+ assert.ok(u instanceof User);
+ assert.equal('zzz', u.login);
+ next();
+ });
+ var user = User.create(_params);
+ user.updateProperties({login: 'yyz'});
+ }
+
+, 'emit static update': function (next) {
+ User.once('update', function (u) {
+ assert.ok(u instanceof User);
+ assert.equal('yyz', u.login);
+ next();
+ });
+ var user = User.create(_params);
+ user.updateProperties({login: 'yyz'});
+ }
+
+, 'emit static beforeValidate': function (next) {
+ User.once('beforeValidate', function (u, p) {
+ assert.ok(u instanceof User);
+ assert.equal('zzz', p.login);
+ next();
+ });
+ var user = User.create(_params);
+ }
+
+, 'emit static validate': function (next) {
+ User.once('validate', function (u) {
+ assert.ok(u instanceof User);
+ assert.equal('zzz', u.login);
+ next();
+ });
+ var user = User.create(_params);
+ }
+
+, 'emit static beforeSave': function (next) {
+ User.once('beforeSave', function (u) {
+ assert.ok(u instanceof User);
+ next();
+ });
+ var user = User.create(_params);
+ user.save(function () {});
+ }
+
+, 'emit static save': function (next) {
+ User.once('save', function (u) {
+ assert.ok(u instanceof User);
+ next();
+ });
+ var user = User.create(_params);
+ user.save(function () {});
+ }
+
+, 'last': function () {}
+
+};
+
+module.exports = tests;
Please sign in to comment.
Something went wrong with that request. Please try again.