mongoose
Version:
Mongoose MongoDB ORM
351 lines (288 loc) • 6.59 kB
JavaScript
/**
* Module dependencies.
*/
var MongooseError = require('./error');
var utils = require('./utils');
/**
* SchemaType constructor
*
* @param {String} path
* @api public
*/
function SchemaType (path, options) {
this.path = path;
this.validators = [];
this.setters = [];
this.getters = [];
this.options = options;
this._index = null;
for (var i in options) if (this[i] && 'function' == typeof this[i]) {
var opts = Array.isArray(options[i])
? options[i]
: [options[i]];
this[i].apply(this, opts);
}
};
/**
* Base schema. Set by Schema when instantiated.
*
* @api private
*/
SchemaType.prototype.base;
/**
* Sets a default
*
* @param {Object} default value
* @api public
*/
SchemaType.prototype.default = function (val) {
if (1 === arguments.length) {
this.defaultValue = typeof val === 'function'
? val
: this.cast(val);
return this;
} else if (arguments.length > 1) {
this.defaultValue = utils.args(arguments);
}
return this.defaultValue;
};
/**
* Sets index. It can be a boolean or a hash of options
* Example:
* Schema.path('my.path').index(true);
* Schema.path('my.path').index({ unique: true });
*
* "Direction doesn't matter for single key indexes"
* http://www.mongodb.org/display/DOCS/Indexes#Indexes-CompoundKeysIndexes
*
* @param {Object} true/
* @api public
*/
SchemaType.prototype.index = function (index) {
this._index = index;
return this;
};
/**
* Adds an unique index
*
* @param {Boolean}
* @api private
*/
SchemaType.prototype.unique = function (bool) {
if (!this._index || Object !== this._index.constructor) {
this._index = {};
}
this._index.unique = bool;
return this;
};
/**
* Adds an unique index
*
* @param {Boolean}
* @api private
*/
SchemaType.prototype.sparse = function (bool) {
if (!this._index || Object !== this._index.constructor) {
this._index = {};
}
this._index.sparse = bool;
return this;
};
/**
* Adds a setter
*
* @param {Function} setter
* @api public
*/
SchemaType.prototype.set = function (fn) {
this.setters.push(fn);
return this;
};
/**
* Adds a getter
*
* @param {Function} getter
* @api public
*/
SchemaType.prototype.get = function (fn) {
this.getters.push(fn);
return this;
};
/**
* Adds a validator
*
* @param {Object} validator
* @param {String} optional error message
* @api public
*/
SchemaType.prototype.validate = function (obj, error) {
this.validators.push([obj, error]);
return this;
};
/**
* Adds a required validator
*
* @param {Boolean} enable/disable the validator
* @api public
*/
SchemaType.prototype.required = function (required) {
var self = this;
function __checkRequired (v) {
return self.checkRequired(v);
}
if (false === required) {
this.isRequired = false;
this.validators = this.validators.filter(function (v) {
return v[0].name !== '__checkRequired';
});
} else {
this.isRequired = true;
this.validators.push([__checkRequired, 'required']);
}
return this;
};
/**
* Gets the default value
*
* @param {Object} scope for callback defaults
* @api private
*/
SchemaType.prototype.getDefault = function (scope) {
var ret = 'function' === typeof this.defaultValue
? this.defaultValue.call(scope)
: this.defaultValue;
if (null !== ret && undefined !== ret) {
ret = this.cast(ret, scope);
}
return ret;
};
/**
* Applies setters
*
* @param {Object} value
* @param {Object} scope
* @api private
*/
SchemaType.prototype.applySetters = function (value, scope, init) {
var v = value
, setters = this.setters
, len = setters.length;
for (var k = len - 1; k >= 0; k--) {
v = setters[k].call(scope, v);
if (null === v || undefined === v) return v;
v = this.cast(v, scope);
}
if (!len) {
if (null === v || undefined === v) return v;
if (!init) {
// if we just initialized we dont recast
v = this.cast(v, scope, init);
}
}
return v;
};
/**
* Applies getters to a value
*
* @param {Object} value
* @param {Object} scope
* @api private
*/
SchemaType.prototype.applyGetters = function (value, scope) {
var v = value
, getters = this.getters
, len = getters.length;
for (var k = len - 1; k >= 0; k--){
v = this.getters[k].call(scope, v);
if (null === v || undefined === v) return v;
v = this.cast(v, scope);
}
if (!len) {
if (null === v || undefined === v) return v;
v = this.cast(v, scope);
}
return v;
};
/**
* Performs a validation
*
* @param {Function} callback
* @param {Object} scope
* @api private
*/
SchemaType.prototype.doValidate = function (value, fn, scope) {
var err = false
, path = this.path
, count = this.validators.length;
if (!count) return fn(null);
function validate (val, msg) {
if (err) return;
if (val === undefined || val) {
--count || fn(null);
} else {
fn(new ValidatorError(path, msg));
err = true;
}
}
this.validators.forEach(function (v) {
var validator = v[0]
, message = v[1];
if (validator instanceof RegExp) {
validate(validator.test(value), message);
} else if ('function' === typeof validator) {
if (2 === validator.length) {
validator.call(scope, value, function (val) {
validate(val, message);
});
} else {
validate(validator.call(scope, value), message);
}
}
});
};
/**
* Schema validator error
*
* @param {String} path
* @param {String} msg
* @api private
*/
function ValidatorError (path, type) {
var msg = type
? '"' + type + '" '
: '';
MongooseError.call(this, 'Validator ' + msg + 'failed for path ' + path);
Error.captureStackTrace(this, arguments.callee);
this.name = 'ValidatorError';
this.path = path;
this.type = type;
};
ValidatorError.prototype.toString = function() {
return this.message;
}
/**
* Inherits from MongooseError
*/
ValidatorError.prototype.__proto__ = MongooseError.prototype;
/**
* Cast error
*
* @api private
*/
function CastError (type, value) {
MongooseError.call(this, 'Cast to ' + type + ' failed for value "' + value + '"');
Error.captureStackTrace(this, arguments.callee);
this.name = 'CastError';
this.type = type;
this.value = value;
};
/**
* Inherits from MongooseError.
*/
CastError.prototype.__proto__ = MongooseError.prototype;
/**
* Module exports.
*/
module.exports = exports = SchemaType;
exports.CastError = CastError;
exports.ValidatorError = ValidatorError;