123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292 |
- "use strict";
- /**
- * @fileOverview Enables a schema and validation feature set to your document or other object.
- * @module documents/schema
- * @requires base
- * @requires jjv
- * @require lodash
- */
- var sys = require( "lodash" );
- var Validator = require( "jjv" );
- var Base = require( "../base" );
- /**
- * The validator mixin provides access to the features of the JSON validation system
- * @exports documents/schema
- * @mixin
- */
- var Schema = Base.compose( [Base], /** @lends documents/schema# */{
- constructor : function () {
- /**
- * The schema that defines the validation rules. This should probably be defined at the prototype for each
- * object or model classification. It can be an anonymous schema defined right here, or this can be
- * registered schema names to use, or just a single name
- *
- * @type {object}
- * @memberOf documents/schema#
- * @name schema
- */
- /**
- * If you want to register multiple schemas, use this property instead
- *
- * @type {object}
- * @memberOf documents/schema#
- * @name schemas
- */
- /**
- * The validation environment
- * @private
- * @type {jjv}
- */
- var env = new Validator();
- /**
- * The default name of the scheman when you use anonymous schemas. You can define this at the prototype for classified
- * schemas. The can also
- *
- * @type {string|function():{string}}
- * @memberOf documents/schema#
- * @name _defaultSchemaName
- */
- this._defaultSchemaName = sys.result( this, "_defaultSchemaName" ) || sys.uniqueId( "schema" );
- /**
- * The options to pass to the validator when it runs
- * @type {object|function():{object}}
- * @name validationOptions
- * @memberOf documents/schema#
- */
- this.validationOptions = sys.defaults( {}, sys.result( this, 'validationOptions' ), {checkRequired : true} );
- /**
- * Validate an object against the schema
- * @returns {object?}
- * @method
- * @name validate
- * @memberOf documents/schema#
- * @param {object=} record The record to validate
- * @param {string|object=} schemaName The name of a previously registered schema
- * @param {object=} options Options to pass to the validator
- * @example
- * // This supports these signatures:
- *
- * instance.validate(record, schemaName, options);
- *
- *
- * instance.validate(); // this, this._defaultSchemaName, this.validationOptions
- * instance.validate(record); // record, this._defaultSchemaName, this.validationOptions
- * instance.validate(schemaName); //this, schemaName, this.validationOptions
- * instance.validate(record, schemaName); //record, schemaName, this.validationOptions
- * instance.validate(schemaName, options); //this, schemaName, this.validationOptions
- */
- this.validate = function ( record, schemaName, options ) {
- if ( arguments.length === 0 ) {
- record = this;
- schemaName = this._defaultSchemaName;
- options = this.validationOptions;
- } else {
- if ( sys.isString( record ) ) {
- schemaName = record;
- record = this;
- }
- if ( sys.isEmpty( options ) ) {
- options = this.validationOptions;
- }
- }
- return env.validate( schemaName, record, options );
- };
- /**
- * Initialize the schema collection by registering the with the handler. You can call this at any time and as often as you like. It will be called once
- * by the constructor on any instance schemas
- * @method
- * @name registerSchemas
- * @memberOf documents/schema#
- * @param {hash} schemas A hash of schemas where the key is the name of the schema
- */
- this.registerSchemas = function ( schemas ) {
- var schema = sys.result( this, "schema" );
- var schemas = schemas || sys.result( this, "schemas" );
- if ( !sys.isEmpty( schema ) ) {
- env.addSchema( this._defaultSchemaName, schema );
- }
- if ( !sys.isEmpty( schemas ) ) {
- sys.each( schemas, function ( val, key ) {
- env.addSchema( val, key );
- } );
- }
- };
- /**
- * Extracts only the elements of the object that are defined in the schema
- * @memberOf documents/schema#
- * @name extract
- * @param {object=} record The record to extract from
- * @param {string=} schema The name of the schema to attach
- * @method
- */
- this.extract = function ( record, schema ) {
- if ( arguments.length === 0 ) {
- record = this;
- schema = this._defaultSchemaName;
- }
- if ( sys.isString( record ) ) {
- schema = record;
- record = this;
- }
- };
- /**
- * Create a type to be used in your schemas to define new validators
- * @memberOf documents/schema#
- * @name addType
- * @method
- * @param {string} name The name of the type
- * @param {function(object)} operation What to do with the type.
- * @param {object} operation.value The value to validation
- * @returns {boolean}
- */
- this.addType = env.addType;
- /**
- * It is also possible to add support for additional string formats through the addFormat function.
- * @memberOf documents/schema#
- * @name addFormat
- * @method
- * @param {string} name The name of the formatter
- * @param {function(object)} formatter How to format it
- * @param {object} formatter.value The value to format
- * @returns {boolean}
- */
- this.addFormat = env.addFormat;
- /**
- * It is possible to add support for custom checks (i.e., minItems, maxItems, minLength, maxLength, etc.) through the addCheck function
- * @memberOf documents/schema#
- * @name addCheck
- * @method
- * @param {string} name The name of the check
- * @param {function(...object)} formatter Perform the check
- * @param {object} formatter.value The value to check followed by any parameters from the schema
- * @returns {boolean}
- */
- this.addCheck = env.addCheck;
- /**
- * Custom coercion rules
- *
- * @memberOf documents/schema#
- * @name addTypeCoercion
- * @method
- * @param {string} name The name of the coercion
- * @param {function(object)} coercer Perform the coercion
- * @param {object} coercer.value The value to coerce
- * @returns {boolean}
- */
- this.addTypeCoercion = env.addTypeCoercion;
- /**
- * Get a registered schema by name
- * @param {string=} schemaName
- * @returns {object?}
- * @memberOf documents/schema#
- * @name getSchema
- * @method
- */
- this.getSchema = function ( schemaName ) {
- if ( sys.isEmpty( schemaName ) || !sys.isString() ) {
- schemaName = this._defaultSchemaName;
- }
- return env.schema[schemaName];
- }
- },
- /**
- * This method will create a new object that contains only the fields and no methods or other artifacts. This is useful
- * for creating objects to pass over the wire or save in a table. This is not deeply copied, so changes made to the
- * extracted object will be represented in this class for reference objects.
- *
- * @param {string=} schema The schema name to use
- * @param {object=} src The object to extract fields from
- * @return {object} Data-only version of the class instance.
- */
- extract : function ( schemaName, src ) {
- if ( sys.isObject( schemaName ) ) {
- src = schema;
- schemaName = this._defaultSchemaName;
- }
- if ( sys.isEmpty( src ) ) {
- src = this;
- }
- if ( sys.isFunction( src.toJSON ) ) {
- src = src.toJSON();
- }
- var schema = this.getSchema( schemaName ) || {};
- var newobj = {};
- sys.each( schema.properties, function ( prop, propname ) {
- if ( prop.properties && !sys.isUndefined( src[ propname ] ) ) {
- newobj[ propname ] = this.extract( prop, src[propname] );
- } else if ( !sys.isUndefined( src[ propname ] ) ) {
- newobj[ propname ] = src[ propname ];
- }
- }, this );
- return newobj;
- },
- /**
- * Builds a default document based on the schema. What this does is create a document from schema and for each property
- * that has a default value or is required, the resultant object will contain that property. It is useful for extending
- * values from some source that may be incomplete, like options or some such.
- * @param {json-schema} schema A schema to use to create the default document
- * @returns {object?}
- * @name defaultDoc
- * @memberOf documents/schema#
- * @method
- */
- defaultDoc : function ( schemaName ) {
- if ( sys.isEmpty( schemaName ) ) {
- schemaName = this._defaultSchemaName;
- }
- var newdoc = {};
- var schema;
- if ( sys.isObject( schemaName ) ) {
- schema = schemaName;
- } else {
- schema = this.getSchema( schemaName ) || {};
- }
- sys.each( schema.properties, function ( val, key ) {
- var def = val[ "default" ]; // keyword and all that
- if ( val.type === "object" && !sys.isEmpty( val.properties ) ) {
- newdoc[ key ] = this.defaultDoc( val );
- } else {
- if ( sys.isFunction( def ) || sys.isBoolean( def ) || sys.isNumber( def ) || !sys.isEmpty( def ) ) {
- if ( sys.isFunction( def ) ) {
- newdoc[ key ] = def( schema );
- } else {
- newdoc[ key ] = def;
- }
- } else if ( val.required ) {
- if ( val.type === 'string' ) {
- newdoc[ key ] = null;
- } else if ( val.type === 'object' ) {
- newdoc[ key ] = {};
- } else if ( val.type === 'array' ) {
- newdoc[ key ] = [];
- } else {
- newdoc[ key ] = null;
- }
- }
- }
- }, this );
- return newdoc;
- }
- } );
- module.exports = Schema;
|