Permalink
Browse files

No more ext dependency. Nested errors

  • Loading branch information...
1 parent 9882e75 commit 60bc5330dc5f7432e40dcec6e1aec1ca75abbecd @masylum committed Oct 17, 2010
Showing with 267 additions and 19 deletions.
  1. +2 −0 index.js
  2. +15 −5 lib/model.js
  3. +135 −0 lib/utils.js
  4. +35 −14 lib/validator.js
  5. +80 −0 test/validator.js
View
@@ -0,0 +1,2 @@
+exports.model = require('./lib/model');
+exports.validator = require('./lib/validator');
View
@@ -123,13 +123,23 @@ module.exports = new Class({
callback(null, element);
},
- updateInstance: function (model, update, callback) {
- var self = this;
+ updateInstance: function (model, data, options, callback) {
+ var self = this,
+ update = {};
+
+ if (typeof options === "function") {
+ callback = options;
+ options = {};
+ }
- self.validate(model, update, function (errors, validator) {
+
+ self.validate(model, data, function (errors, validator) {
if (!validator.hasErrors()) {
- self.onUpdateInstance(model, update, function (errors, update) {
- self.mongoCall('update', {'_id': model._id}, {'$set': update}, { upsert: true, multi: false}, function (error, element) {
+ self.onUpdateInstance(model, data, function (errors, new_data) {
+
+ update[options.verb || '$set'] = new_data;
+
+ self.mongoCall('update', {'_id': model._id}, update, { upsert: true, multi: false}, function (error, element) {
callback(null, validator);
});
});
View
@@ -0,0 +1,135 @@
+/*
+ * utils.js
+ *
+ * Shameless copy of ext.js by TJ Holowaychuk <tj@vision-media.ca>
+ * Instead of extending Object,
+ * we just attach this methods to a utils object
+ *
+ */
+
+module.exports = (function () {
+ return {
+ /**
+ * Check if an object is empty
+ *
+ * @param {object} obj
+ * @return {boolean}
+ * @api public
+ */
+ isEmpty: function (obj) {
+ if (obj === null || obj === undefined || obj === false) {
+ return true;
+ }
+ return typeof obj === "object" && obj && Object.keys(obj).length === 0;
+ },
+
+ /**
+ * Clones a object
+ *
+ * @param {object} obj
+ * @return {object}
+ * @api public
+ */
+ clone: function (obj) {
+ if (obj === null || typeof obj !== "object") {
+ return obj;
+ }
+
+ if (obj.constructor !== Object && obj.constructor !== Array) {
+ return obj;
+ }
+
+ if ([Date, RegExp, Function, String, Number, Boolean].indexOf(obj.constructor) !== -1) {
+ return new obj.constructor(obj);
+ }
+
+ var to = new obj.constructor(),
+ name = '';
+
+ for (name in obj) {
+ to[name] = typeof to[name] === "undefined" ? this.clone(obj[name], null) : to[name];
+ }
+
+ return to;
+ },
+
+ /**
+ * Merges all values from object _b_ to _a_.
+ *
+ * @param {object} a
+ * @param {object} b
+ * @return {object}
+ * @api public
+ */
+
+ merge: function (a, b) {
+ if (!b) {
+ return a;
+ }
+ var keys = Object.keys(b),
+ i = 0,
+ len = keys.length;
+
+ for (; i < len; i += 1) {
+ a[keys[i]] = b[keys[i]];
+ }
+
+ return a;
+ },
+
+ /**
+ * Perform a deep merge with object _a_ and _b_.
+ *
+ * @param {object} a
+ * @param {object} b
+ * @return {object}
+ * @api public
+ */
+
+ mergeDeep: function (a, b) {
+ if (!b) {
+ return a;
+ }
+
+ var target = a,
+ keys = Object.keys(b),
+ key = null,
+ i = 0,
+ len = keys.length;
+
+ for (; i < len; i += 1) {
+ key = keys[i];
+ if (typeof b[key] === 'object') {
+ target = this.mergeDeep((target[key] = target[key] || {}), b[key]);
+ } else {
+ target[key] = b[key];
+ }
+ }
+
+ return a;
+ },
+
+ /**
+ * Return object values as an array.
+ *
+ * @param {object} object
+ * @return {array}
+ * @api public
+ */
+ values: function (obj) {
+ if (!obj || typeof obj !== 'object') {
+ return [];
+ }
+ var keys = Object.keys(obj),
+ vals = [],
+ i = 0,
+ len = keys.length;
+
+ for (; i < len; i += 1) {
+ vals.push(obj[keys[i]]);
+ }
+
+ return vals;
+ }
+ };
+}());
View
@@ -1,14 +1,15 @@
-module.exports = function (args) {
+module.exports = function (model, data) {
var validator = {},
+ utils = require('./utils'),
// private methods
getKeys = function (object) {
return Object.keys(object).intersect(Object.keys(validator.data));
};
- validator.model = args[0];
- validator.data = args[1];
- validator.updated_model = Object.merge(Object.clone(this.model), this.data);
+ validator.model = model;
+ validator.data = data;
+ validator.updated_model = utils.merge(utils.clone(validator.model), validator.data);
validator.errors = {};
validator.regex = {
@@ -21,23 +22,46 @@ module.exports = function (args) {
};
validator.isUpdating = function () {
- return Object.isEmpty(this.model);
+ return utils.isEmpty(this.model);
};
validator.isInserting = function () {
- return !Object.isEmpty(this.model);
+ return !utils.isEmpty(this.model);
};
validator.attrChanged = function (attr) {
return this.model[attr] !== this.data[attr];
};
- validator.hasError = function (field) {
- return this.hasErrors() && this.errors[field];
+ validator.getErrorBucket = function (name) {
+ var error_bucket = {},
+ key = null,
+ name_parts = name.split('.');
+
+ if (name_parts.length === 1) {
+ this.errors[name] = this.errors[name] || [];
+ error_bucket = this.errors[name];
+ } else {
+ error_bucket = this.errors;
+ for (;name_parts.length > 0;) {
+ key = name_parts.shift();
+ if (!error_bucket[key]) {
+ error_bucket[key] = name_parts.length === 0 ? [] : {};
+ }
+ error_bucket = error_bucket[key];
+ }
+ }
+ return error_bucket;
+ };
+
+ validator.hasError = function (name) {
+ var error_bucket = this.getErrorBucket(name);
+
+ return !utils.isEmpty(this.hasErrors() && error_bucket);
};
validator.hasErrors = function () {
- return !Object.isEmpty(this.errors);
+ return !utils.isEmpty(this.errors);
};
validator.validateExistence = function (validations) {
@@ -80,11 +104,8 @@ module.exports = function (args) {
};
validator.addError = function (name, value) {
- if (!this.errors[name]) {
- this.errors[name] = [];
- }
-
- this.errors[name].push(value);
+ var error_bucket = this.getErrorBucket(name);
+ error_bucket.push(value);
};
return validator;
View
@@ -0,0 +1,80 @@
+/**
+ * Module dependencies.
+ */
+
+var validator = require('./../lib/validator');
+
+module.exports['test initial status'] = function (assert) {
+ var val = validator({}, {foo: 'bar'}),
+ val2 = validator({foo: 'zemba', hey: 'joe'}, {foo: 'bar'});
+
+ assert.eql(val.errors, {});
+ assert.eql(val.model, {});
+ assert.eql(val.data, {foo: 'bar'});
+ assert.eql(val.updated_model, {foo: 'bar'});
+
+ assert.eql(val2.errors, {});
+ assert.eql(val2.model, {foo: 'zemba', hey: 'joe'});
+ assert.eql(val2.data, {foo: 'bar'});
+ assert.eql(val2.updated_model, {foo: 'bar', hey: 'joe'});
+};
+
+// errors
+module.exports['test addError'] = function (assert) {
+ var val = validator({}, {foo: 'bar'});
+ val.addError('foo', 'foo error');
+
+ assert.includes(val.errors.foo, 'foo error');
+};
+
+module.exports['test hasErrors'] = function (assert) {
+ var val = validator({}, {foo: 'bar'});
+ val.addError('foo', 'foo error');
+
+ assert.equal(val.hasErrors(), true);
+};
+
+module.exports['test hasError'] = function (assert) {
+ var val = validator({}, {foo: 'bar'});
+ val.addError('foo', 'foo error');
+
+ assert.equal(val.hasError('foo'), true);
+};
+
+// nested errors
+module.exports['test nested addError'] = function (assert) {
+ var val = validator({}, {foo: 'bar'});
+ val.addError('foo.zemba', 'foo error');
+
+ assert.includes(val.errors.foo.zemba, 'foo error');
+};
+
+module.exports['test hasErrors'] = function (assert) {
+ var val = validator({}, {foo: 'bar'});
+ val.addError('foo.zemba', 'foo error');
+
+ assert.equal(val.hasErrors(), true);
+};
+
+module.exports['test hasError'] = function (assert) {
+ var val = validator({}, {foo: 'bar'});
+ val.addError('foo.zemba', 'foo error');
+
+ assert.equal(val.hasError('foo.zemba'), true);
+};
+
+// multiple errors per field
+module.exports['test multiple errors per field'] = function (assert) {
+ var val = validator({}, {foo: 'bar'});
+ val.addError('foo.zemba', 'error1');
+
+ assert.equal(val.hasError('foo.zemba'), true);
+
+ val.addError('foo.zemba', 'error2');
+ assert.eql(val.errors.foo.zemba, ['error1', 'error2']);
+ assert.equal(val.hasError('foo.zemba'), true);
+
+ val.addError('foo.bla', 'error3');
+ assert.eql(val.errors.foo.zemba, ['error1', 'error2']);
+ assert.eql(val.errors.foo.bla, ['error3']);
+};

0 comments on commit 60bc533

Please sign in to comment.