Comparing version 0.0.1 to 0.0.5
{ | ||
"glob" : "src/**/*.coffee", | ||
"glob" : ["src/**/*.coffee", "README.md"], | ||
"index-page-title" : "Scheming", | ||
"out": "doc" | ||
} |
(function() { | ||
var NESTED_TYPES, RESERVED_PROPERTIES, Scheming, TYPES, getPrimitiveTypeOf, isNode, register, registry, root, uuid, _, | ||
var DEFAULT_OPTIONS, NESTED_TYPES, Scheming, TYPES, addToRegistry, getPrimitiveTypeOf, instanceFactory, isNode, registry, root, schemaFactory, uuid, _, | ||
__slice = [].slice; | ||
@@ -24,6 +24,17 @@ | ||
RESERVED_PROPERTIES = { | ||
validate: 'validate' | ||
DEFAULT_OPTIONS = { | ||
seal: false, | ||
strict: false | ||
}; | ||
/* | ||
Scheming exports the default types that it uses for parsing schemas. You can extend with custom types, or | ||
override the identifier / parser functions of the default types. A custom type should provide: | ||
- ctor (optional) - Used in schema definitions to declare a type. `Scheming.create name : String` | ||
- string - Used in schema definitions to declare a type. `Scheming.create name : 'string'` | ||
- identifier - Function, returns true or false. Determines whether a value needs to be parsed. | ||
- parser - Function, parses a value into the type. | ||
*/ | ||
TYPES = { | ||
@@ -51,7 +62,2 @@ String: { | ||
}, | ||
Float: { | ||
string: 'float', | ||
identifier: _.isNumber, | ||
parser: parseFloat | ||
}, | ||
Date: { | ||
@@ -85,2 +91,8 @@ ctor: Date, | ||
/* | ||
Special type definitions for nested types. Used to identify and parse nested Arrays and Schemas. | ||
Should not be extended or overridden. | ||
*/ | ||
NESTED_TYPES = { | ||
@@ -91,5 +103,5 @@ Array: { | ||
identifier: _.isArray, | ||
parser: _.toArray, | ||
childType: null, | ||
parser: _.toArray, | ||
childParser: _.toArray | ||
childParser: null | ||
}, | ||
@@ -100,4 +112,4 @@ Schema: { | ||
identifier: null, | ||
childType: null, | ||
parser: null | ||
parser: null, | ||
childType: null | ||
} | ||
@@ -120,3 +132,3 @@ }; | ||
NESTED_TYPES: NESTED_TYPES, | ||
RESERVED_PROPERTIES: RESERVED_PROPERTIES | ||
DEFAULT_OPTIONS: DEFAULT_OPTIONS | ||
}; | ||
@@ -130,6 +142,8 @@ | ||
type = _.cloneDeep(NESTED_TYPES.Array); | ||
childType = TYPES.Mixed; | ||
if (typeDef.length) { | ||
childType = Scheming.resolveType(typeDef[0]); | ||
} | ||
if (!childType) { | ||
throw new Error("Error resolving type of array value " + typeDef); | ||
} | ||
type.childType = childType; | ||
@@ -156,2 +170,9 @@ type.childParser = function(val) { | ||
}; | ||
/* | ||
- If the type definition is an object `{}` | ||
- Create a new Schema from the object | ||
- Treat the field as a nested Schema | ||
- Set identifier and parser functions immediately | ||
*/ | ||
if (_.isPlainObject(typeDef)) { | ||
@@ -162,3 +183,9 @@ type = _.cloneDeep(NESTED_TYPES.Schema); | ||
} | ||
if (_.isFunction(typeDef) && typeDef.__skemaId) { | ||
/* | ||
- If the type definition is a reference to a Schema constructor | ||
- Treat the field as a nested Schema | ||
- Set identifier and parser functions immediately | ||
*/ | ||
if (_.isFunction(typeDef) && typeDef.__schemaId) { | ||
type = _.cloneDeep(NESTED_TYPES.Schema); | ||
@@ -168,2 +195,12 @@ childType = typeDef; | ||
} | ||
/* | ||
- If the type definition is a string that begins with Schema:, such as `'Schema:Car'` | ||
- It is assumed that the field is a reference to a nested Schema that will be registered with the name Car, | ||
but may not be registered yet | ||
- The Schema is not resolved immediately | ||
- The parser and identifier functions are written as wrappers, so that the first time they are invoked the Schema | ||
will be looked up at that time via `Scheming.get`, and real identifier and parser are set at that time. | ||
- If the registered Schema cannot be resolved, throw an error. | ||
*/ | ||
if (_.isString(typeDef) && typeDef.slice(0, 7) === 'Schema:') { | ||
@@ -192,4 +229,13 @@ type = _.cloneDeep(NESTED_TYPES.Schema); | ||
Scheming.normalizeProperty = function(config, fieldName) { | ||
/* | ||
Normalizes a field declaration on a schema to capture type, default value, setter, getter, and validation. | ||
Used internally when a schema is created to build a normalized schema definition. | ||
*/ | ||
Scheming.normalizePropertyConfig = function(propConfig, propName) { | ||
var definition, fn, getter, required, setter, type, validate, _i, _len; | ||
if (propName == null) { | ||
propName = 'field'; | ||
} | ||
definition = { | ||
@@ -203,16 +249,16 @@ type: null, | ||
}; | ||
if (!(_.isPlainObject(config) && (config.type != null))) { | ||
config = { | ||
type: config | ||
if (!(_.isPlainObject(propConfig) && (propConfig.type != null))) { | ||
propConfig = { | ||
type: propConfig | ||
}; | ||
} | ||
type = config.type, getter = config.getter, setter = config.setter, validate = config.validate, required = config.required; | ||
type = propConfig.type, getter = propConfig.getter, setter = propConfig.setter, validate = propConfig.validate, required = propConfig.required; | ||
if (type == null) { | ||
throw new Error("Error resolving " + fieldName + ". Schema type must be defined."); | ||
throw new Error("Error resolving " + propName + ". Schema type must be defined."); | ||
} | ||
if ((getter != null) && !_.isFunction(getter)) { | ||
throw new Error("Error resolving " + fieldName + ". Schema getter must be a function."); | ||
throw new Error("Error resolving " + propName + ". Schema getter must be a function."); | ||
} | ||
if ((setter != null) && !_.isFunction(setter)) { | ||
throw new Error("Error resolving " + fieldName + ". Schema setter must be a function."); | ||
throw new Error("Error resolving " + propName + ". Schema setter must be a function."); | ||
} | ||
@@ -228,3 +274,3 @@ if (validate == null) { | ||
if (!_.isFunction(fn)) { | ||
throw new Error("Error resolving " + fieldName + ". Schema validate must be a function or array of functions."); | ||
throw new Error("Error resolving " + propName + ". Schema validate must be a function or array of functions."); | ||
} | ||
@@ -234,5 +280,5 @@ } | ||
if (definition.type == null) { | ||
throw new Error("Error resolving " + fieldName + ". Unrecognized type " + type); | ||
throw new Error("Error resolving " + propName + ". Unrecognized type " + type); | ||
} | ||
definition["default"] = config["default"]; | ||
definition["default"] = propConfig["default"]; | ||
definition.getter = getter; | ||
@@ -245,17 +291,5 @@ definition.setter = setter; | ||
/* | ||
opts: | ||
strict - if false, allows attachment of arbitrary properties to object | ||
*/ | ||
/* | ||
Doc notes - | ||
- parsers are applied before setters; setters can assume they are receiving correct type | ||
*/ | ||
registry = {}; | ||
register = function(key, value) { | ||
addToRegistry = function(key, value) { | ||
if (registry[key]) { | ||
@@ -267,4 +301,12 @@ throw new Error("Naming conflict encountered. Schema " + key + " already exists"); | ||
Scheming.get = function(name) { | ||
return registry[name]; | ||
}; | ||
Scheming.reset = function() { | ||
return registry = {}; | ||
}; | ||
Scheming.create = function() { | ||
var Schema, args, name, normalizedSchema, opts, schemaConfig; | ||
var Schema, args, name, opts, schemaConfig; | ||
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | ||
@@ -275,6 +317,19 @@ if (!_.isString(args[0])) { | ||
name = args[0], schemaConfig = args[1], opts = args[2]; | ||
opts = _.defaults(opts || {}, DEFAULT_OPTIONS); | ||
Schema = schemaFactory(name, opts); | ||
Schema.defineProperties(schemaConfig); | ||
addToRegistry(name, Schema); | ||
return Schema; | ||
}; | ||
schemaFactory = function(name, opts) { | ||
var Schema, normalizedSchema; | ||
normalizedSchema = {}; | ||
Schema = (function() { | ||
Schema.__skemaId = name; | ||
return Schema = (function() { | ||
Schema.__schemaId = name; | ||
Schema.defineProperty = function(propName, propConfig) { | ||
return normalizedSchema[propName] = Scheming.normalizePropertyConfig(propConfig, propName); | ||
}; | ||
Schema.defineProperties = function(config) { | ||
@@ -290,55 +345,5 @@ var k, v, _results; | ||
Schema.defineProperty = function(fieldName, config) { | ||
return normalizedSchema[fieldName] = Scheming.normalizeProperty(config, fieldName); | ||
}; | ||
function Schema(model) { | ||
var data, fieldName, key, typeDefinition, value, _fn; | ||
data = {}; | ||
Object.defineProperty(this, '__skemaId', { | ||
enumerable: false, | ||
configurable: false, | ||
writable: false, | ||
value: Schema.__skemaId | ||
}); | ||
_fn = (function(_this) { | ||
return function(fieldName, typeDefinition) { | ||
var getter, setter, type; | ||
type = typeDefinition.type, getter = typeDefinition.getter, setter = typeDefinition.setter; | ||
Object.defineProperty(_this, fieldName, { | ||
configurable: true, | ||
enumerable: true, | ||
get: function() { | ||
var val; | ||
val = data[fieldName]; | ||
if (val === void 0) { | ||
return val; | ||
} | ||
if (type.string === NESTED_TYPES.Array.string) { | ||
val = type.childParser(val); | ||
} | ||
if (getter) { | ||
val = getter(val); | ||
} | ||
return val; | ||
}, | ||
set: function(val) { | ||
if (!type.identifier(val)) { | ||
val = type.parser(val); | ||
} | ||
if (setter) { | ||
val = setter(val); | ||
} | ||
return data[fieldName] = val; | ||
} | ||
}); | ||
if (typeDefinition["default"] === !void 0) { | ||
return _this[fieldName] = typeDefinition["default"]; | ||
} | ||
}; | ||
})(this); | ||
for (fieldName in normalizedSchema) { | ||
typeDefinition = normalizedSchema[fieldName]; | ||
_fn(fieldName, typeDefinition); | ||
} | ||
var key, value; | ||
instanceFactory(this, normalizedSchema, opts); | ||
for (key in model) { | ||
@@ -348,93 +353,137 @@ value = model[key]; | ||
} | ||
this.validate = function() { | ||
var childErrors, e, err, errors, i, k, member, pushError, required, type, v, val, validator, validators, _i, _j, _len, _len1; | ||
errors = {}; | ||
if (this._validating) { | ||
return null; | ||
} | ||
this._validating = true; | ||
pushError = function(key, err) { | ||
var e, _i, _len; | ||
if (_.isArray(err)) { | ||
for (_i = 0, _len = err.length; _i < _len; _i++) { | ||
e = err[_i]; | ||
return pushError(key, e); | ||
} | ||
return Schema; | ||
})(); | ||
}; | ||
instanceFactory = function(instance, normalizedSchema, opts) { | ||
var data, propConfig, propName, seal, strict, _fn; | ||
data = {}; | ||
strict = opts.strict, seal = opts.seal; | ||
_fn = (function(_this) { | ||
return function(propName, propConfig) { | ||
var getter, setter, type; | ||
type = propConfig.type, getter = propConfig.getter, setter = propConfig.setter; | ||
Object.defineProperty(instance, propName, { | ||
configurable: false, | ||
enumerable: true, | ||
set: function(val) { | ||
if (val === void 0) { | ||
return data[propName] = val; | ||
} | ||
if (!type.identifier(val)) { | ||
if (strict) { | ||
throw new Error("Error assigning " + val + " to " + propName + ". Value is not of type " + type.string); | ||
} | ||
val = type.parser(val); | ||
} | ||
if (!_.isString(err)) { | ||
err = 'Validation error occurred.'; | ||
if (setter) { | ||
val = setter(val); | ||
} | ||
if (errors[key] == null) { | ||
errors[key] = []; | ||
return data[propName] = val; | ||
}, | ||
get: function() { | ||
var val; | ||
val = data[propName]; | ||
if (val === void 0) { | ||
return val; | ||
} | ||
return errors[key].push(err); | ||
}; | ||
for (key in normalizedSchema) { | ||
value = normalizedSchema[key]; | ||
validators = value.validators, required = value.required; | ||
val = this[key]; | ||
if (required && (val == null)) { | ||
pushError(key, "Field is required."); | ||
if (getter) { | ||
val = getter(val); | ||
} | ||
if (val != null) { | ||
type = normalizedSchema[key].type; | ||
for (_i = 0, _len = validators.length; _i < _len; _i++) { | ||
validator = validators[_i]; | ||
err = true; | ||
try { | ||
err = validator(val); | ||
} catch (_error) { | ||
e = _error; | ||
if (e) { | ||
err = e.message; | ||
} | ||
} | ||
if (err !== true) { | ||
pushError(key, err); | ||
} | ||
if (type.string === NESTED_TYPES.Array.string) { | ||
val = type.childParser(val); | ||
} | ||
return val; | ||
} | ||
}); | ||
if (propConfig["default"] !== void 0) { | ||
return instance[propName] = (typeof propConfig["default"] === "function" ? propConfig["default"]() : void 0) || propConfig["default"]; | ||
} | ||
}; | ||
})(this); | ||
for (propName in normalizedSchema) { | ||
propConfig = normalizedSchema[propName]; | ||
_fn(propName, propConfig); | ||
} | ||
instance.validate = function() { | ||
var childErrors, e, err, errors, i, k, key, member, pushError, required, type, v, val, validator, validators, value, _i, _j, _len, _len1; | ||
errors = {}; | ||
if (this._validating) { | ||
return null; | ||
} | ||
this._validating = true; | ||
pushError = function(key, err) { | ||
var e, _i, _len; | ||
if (_.isArray(err)) { | ||
for (_i = 0, _len = err.length; _i < _len; _i++) { | ||
e = err[_i]; | ||
return pushError(key, e); | ||
} | ||
} | ||
if (!_.isString(err)) { | ||
err = 'Validation error occurred.'; | ||
} | ||
if (errors[key] == null) { | ||
errors[key] = []; | ||
} | ||
return errors[key].push(err); | ||
}; | ||
for (key in normalizedSchema) { | ||
value = normalizedSchema[key]; | ||
validators = value.validators, required = value.required; | ||
val = this[key]; | ||
if (required && (val == null)) { | ||
pushError(key, "Field is required."); | ||
} | ||
if (val != null) { | ||
type = normalizedSchema[key].type; | ||
for (_i = 0, _len = validators.length; _i < _len; _i++) { | ||
validator = validators[_i]; | ||
err = true; | ||
try { | ||
err = validator(val); | ||
} catch (_error) { | ||
e = _error; | ||
if (e) { | ||
err = e.message; | ||
} | ||
if (type.string === 'schema') { | ||
childErrors = val.validate(); | ||
for (k in childErrors) { | ||
v = childErrors[k]; | ||
pushError("" + key + "." + k, v); | ||
} | ||
} | ||
if (err !== true) { | ||
pushError(key, err); | ||
} | ||
} | ||
if (type.string === 'schema') { | ||
childErrors = val.validate(); | ||
for (k in childErrors) { | ||
v = childErrors[k]; | ||
pushError("" + key + "." + k, v); | ||
} | ||
} | ||
if (type.string === 'array' && type.childType.string === 'schema') { | ||
for (i = _j = 0, _len1 = val.length; _j < _len1; i = ++_j) { | ||
member = val[i]; | ||
childErrors = member.validate(); | ||
for (k in childErrors) { | ||
v = childErrors[k]; | ||
pushError("" + key + "[" + i + "]." + k, v); | ||
} | ||
if (type.string === 'array' && type.childType.string === 'schema') { | ||
for (i = _j = 0, _len1 = val.length; _j < _len1; i = ++_j) { | ||
member = val[i]; | ||
childErrors = member.validate(); | ||
for (k in childErrors) { | ||
v = childErrors[k]; | ||
pushError("" + key + "[" + i + "]." + k, v); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
this._validating = false; | ||
if (_.size(errors) === 0) { | ||
return null; | ||
} else { | ||
return errors; | ||
} | ||
}; | ||
} | ||
} | ||
return Schema; | ||
})(); | ||
Schema.defineProperties(schemaConfig); | ||
register(name, Schema); | ||
return Schema; | ||
this._validating = false; | ||
if (_.size(errors) === 0) { | ||
return null; | ||
} else { | ||
return errors; | ||
} | ||
}; | ||
if (seal) { | ||
return Object.seal(instance); | ||
} | ||
}; | ||
Scheming.get = function(name) { | ||
return registry[name]; | ||
}; | ||
Scheming.reset = function() { | ||
return registry = {}; | ||
}; | ||
if (isNode) { | ||
@@ -441,0 +490,0 @@ module.exports = Scheming; |
{ | ||
"name": "scheming", | ||
"version": "0.0.1", | ||
"version": "0.0.5", | ||
"main": "index.js", | ||
"repository": "https://github.com/autoric/skema", | ||
"repository": "https://github.com/autoric/scheming", | ||
"scripts": { | ||
@@ -16,4 +16,4 @@ "test": "mocha test -w --growl --reporter spec --compilers coffee:coffee-script/register", | ||
"chai": "~1.9.2", | ||
"mocha": "~1.20.1", | ||
"sinon": "~1.10.3", | ||
"mocha": "~2.0.1", | ||
"sinon": "~1.11.1", | ||
"sinon-chai": "~2.6.0", | ||
@@ -24,4 +24,11 @@ "gulp": "~3.8.9", | ||
"gulp-sourcemaps": "~1.2.4", | ||
"gulp-groc": "~0.4.1" | ||
"gulp-groc": "~0.4.1", | ||
"karma": "~0.12.24", | ||
"karma-phantomjs-launcher": "~0.1.4", | ||
"karma-mocha": "~0.1.9", | ||
"karma-coffee-preprocessor": "~0.2.1", | ||
"karma-sinon": "~1.0.3", | ||
"karma-sinon-chai": "~0.2.0", | ||
"karma-chai": "~0.1.0" | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
No README
QualityPackage does not have a README. This may indicate a failed publish or a low quality package.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
362474
22
1899
1
421
16