alamid-schema
Advanced tools
Comparing version 0.0.6 to 0.1.0
@@ -52,3 +52,3 @@ "use strict"; | ||
if (supportedTypes.indexOf(name) === -1) { | ||
throw new TypeError("(alamid) Type '" + name + "' is not supported"); | ||
throw new TypeError("(alamid-schema) Type '" + name + "' is not supported"); | ||
} | ||
@@ -55,0 +55,0 @@ |
"use strict"; | ||
var processSchema = require("./processSchema"); | ||
var processDefinition = require("./processDefinition.js"), | ||
merge = require("./merge.js"); | ||
@@ -14,7 +15,7 @@ var slice = Array.prototype.slice; | ||
* | ||
* @param {String|Object} name | ||
* @param {String|Object=} schemaDefinition | ||
* @param {String=Anonymous} name | ||
* @param {String|Object} definition | ||
* @constructor | ||
*/ | ||
Schema.prototype.constructor = function (name, schemaDefinition) { | ||
Schema.prototype.constructor = function (name, definition) { | ||
var schema; | ||
@@ -24,9 +25,9 @@ | ||
name = "Anonymous"; | ||
schemaDefinition = arguments[0]; | ||
definition = arguments[0]; | ||
} | ||
this.name = name; | ||
this.definition = definition; | ||
//data we always extract | ||
schema = processSchema(schemaDefinition); | ||
schema = processDefinition(definition); | ||
@@ -61,2 +62,31 @@ this.keys = schema.keys; | ||
/** | ||
* Creates a new schema that inherits from the current schema. Field definitions are merged | ||
* where appropriate. If a definition conflicts with the parent definition, the child's definition supersedes. | ||
* | ||
* @param {String=Anonymous} name | ||
* @param {Object} definition | ||
* @returns {Schema} | ||
*/ | ||
Schema.prototype.extend = function (name, definition) { | ||
if (arguments.length === 1) { | ||
name = "Anonymous"; | ||
definition = arguments[0]; | ||
} | ||
merge(this.definition, definition); | ||
return new Schema(name, definition); | ||
}; | ||
/** | ||
* Calls the given function with the Schema as first argument and the given config (optionally). Plugins can be used | ||
* to hook into class methods by overriding them. | ||
* | ||
* You may call this function multiple times with the same plugin, the plugin will only be applied once. | ||
* | ||
* @param {Function} plugin | ||
* @param {Object=} config | ||
* @returns {Function} | ||
*/ | ||
Schema.use = function (plugin, config) { | ||
@@ -63,0 +93,0 @@ this._plugins = this._plugins || []; |
{ | ||
"name": "alamid-schema", | ||
"version": "0.0.6", | ||
"version": "0.1.0", | ||
"description": "an extendable mongoose-like like schemas for node.js and the browser", | ||
@@ -27,3 +27,3 @@ "main": "lib/Schema.js", | ||
"dependencies": { | ||
"value": "~0.3.0" | ||
"value": "0.3.x" | ||
}, | ||
@@ -33,4 +33,4 @@ "devDependencies": { | ||
"mocha": "1.x", | ||
"chai-spies": "~0.5.1" | ||
"chai-spies": "0.5.x" | ||
} | ||
} |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ ## alamid-schema |
@@ -15,7 +15,67 @@ "use strict"; | ||
name: String, | ||
age: Number, | ||
friends: Array | ||
age: 3, | ||
friends: { | ||
type: Array | ||
} | ||
}); | ||
}); | ||
describe(".constructor(definition)", function () { | ||
it("should extract the keys from the definition", function () { | ||
expect(schema.keys).to.eql(["name", "age", "friends"]); | ||
}); | ||
it("should extract the types from the definition", function () { | ||
expect(schema.types).to.eql({ | ||
name: "String", | ||
age: "Number", | ||
friends: "Array" | ||
}); | ||
}); | ||
it("should normalize the definition", function () { | ||
expect(schema.definition).to.eql({ | ||
name: { | ||
type: "String" | ||
}, | ||
age: { | ||
type: "Number" | ||
}, | ||
friends: { | ||
type: "Array" | ||
} | ||
}); | ||
}); | ||
it("should apply 'Anonymous' as schema name", function () { | ||
expect(schema.name).to.equal("Anonymous"); | ||
}); | ||
}); | ||
describe(".constructor(name, definition)", function () { | ||
var namedSchema; | ||
beforeEach(function () { | ||
namedSchema = new Schema("User", { | ||
name: String, | ||
age: 3, | ||
friends: { | ||
type: Array | ||
} | ||
}); | ||
}); | ||
it("should apply the given name", function () { | ||
expect(namedSchema.name).to.equal("User"); | ||
}); | ||
it("should work like .constructor(definition)", function () { | ||
namedSchema.name = schema.name; | ||
expect(schema).to.eql(namedSchema); | ||
}); | ||
}); | ||
describe(".fields(key1, key2, key3)", function () { | ||
@@ -46,4 +106,68 @@ var subset; | ||
describe(".extend(definition)", function () { | ||
var extended; | ||
it("should create an independent schema", function () { | ||
extended = schema.extend({}); | ||
expect(extended).to.not.equal(schema); | ||
expect(extended.keys).to.not.equal(schema.keys); | ||
expect(extended.types).to.not.equal(schema.types); | ||
expect(extended).to.eql(schema); | ||
}); | ||
it("should extend the schema by the specified properties", function () { | ||
extended = schema.extend({ | ||
password: String, | ||
token: { | ||
type: "String" | ||
} | ||
}); | ||
expect(extended.keys).to.contain("password", "token"); | ||
expect(extended.types.password).to.equal("String"); | ||
expect(extended.types.token).to.equal("String"); | ||
}); | ||
it("should merge the definition", function () { | ||
extended = schema.extend({ | ||
age: { | ||
validate: function () {} | ||
} | ||
}); | ||
expect(extended.types.age).to.equal("Number"); | ||
expect(extended.definition.age.validate).to.be.a("function"); | ||
}); | ||
}); | ||
describe(".extend(name, definition)", function () { | ||
var extended; | ||
beforeEach(function () { | ||
extended = schema.extend("SuperUser", { | ||
password: String, | ||
token: { | ||
type: "String" | ||
} | ||
}); | ||
}); | ||
it("should apply the given name", function () { | ||
expect(extended.name).to.equal("SuperUser"); | ||
}); | ||
it("should work like .extend(definition)", function () { | ||
extended.name = schema.name; | ||
expect(extended.keys).to.contain("password", "token"); | ||
expect(extended.types.password).to.equal("String"); | ||
expect(extended.types.token).to.equal("String"); | ||
}); | ||
}); | ||
}); | ||
}); |
@@ -24,135 +24,141 @@ "use strict"; | ||
describe("validationPlugin", function () { | ||
describe("plugins/validation", function () { | ||
describe("#constructor", function () { | ||
describe(".prototype", function () { | ||
describe("#default validators", function () { | ||
describe(".constructor()", function () { | ||
it("should accept required validators", function () { | ||
describe("default validators", function () { | ||
var schema = new Schema({ | ||
name: { | ||
type: String, | ||
required: true | ||
} | ||
it("should add a required validator", function () { | ||
var schema = new Schema({ | ||
name: { | ||
type: String, | ||
required: true | ||
} | ||
}); | ||
expect(schema.validators.name).to.be.an("array"); | ||
expect(schema.validators.name).to.have.length(1); | ||
expect(schema.validators.name[0].name).to.eql(validators.required().name); | ||
}); | ||
expect(schema.validators.name).to.be.an("array"); | ||
expect(schema.validators.name).to.have.length(1); | ||
expect(schema.validators.name[0].name).to.eql(validators.required().name); | ||
}); | ||
it("should add a enum validator", function () { | ||
it("should accept enum validators", function () { | ||
var schema = new Schema({ | ||
tags: { | ||
type: String, | ||
"enum": ["a", "b"] | ||
} | ||
}); | ||
var schema = new Schema({ | ||
tags: { | ||
type: String, | ||
"enum": ["a", "b"] | ||
} | ||
expect(schema.validators.tags).to.be.an("array"); | ||
expect(schema.validators.tags).to.have.length(1); | ||
expect(schema.validators.tags[0].name).to.eql(validators.enum().name); | ||
}); | ||
expect(schema.validators.tags).to.be.an("array"); | ||
expect(schema.validators.tags).to.have.length(1); | ||
expect(schema.validators.tags[0].name).to.eql(validators.enum().name); | ||
}); | ||
it("should add a min validator", function () { | ||
it("should accept min validators", function () { | ||
var schema = new Schema({ | ||
age: { | ||
type: Number, | ||
min: 0 | ||
} | ||
}); | ||
var schema = new Schema({ | ||
age: { | ||
type: Number, | ||
min: 0 | ||
} | ||
expect(schema.validators.age).to.be.an("array"); | ||
expect(schema.validators.age).to.have.length(1); | ||
expect(schema.validators.age[0].name).to.eql(validators.min().name); | ||
}); | ||
expect(schema.validators.age).to.be.an("array"); | ||
expect(schema.validators.age).to.have.length(1); | ||
expect(schema.validators.age[0].name).to.eql(validators.min().name); | ||
}); | ||
it("should add a max validator", function () { | ||
it("should accept max validators", function () { | ||
var schema = new Schema({ | ||
age: { | ||
type: Number, | ||
max: 200 | ||
} | ||
}); | ||
var schema = new Schema({ | ||
age: { | ||
type: Number, | ||
max: 200 | ||
} | ||
expect(schema.validators.age).to.be.an("array"); | ||
expect(schema.validators.age).to.have.length(1); | ||
expect(schema.validators.age[0].name).to.eql(validators.max().name); | ||
}); | ||
expect(schema.validators.age).to.be.an("array"); | ||
expect(schema.validators.age).to.have.length(1); | ||
expect(schema.validators.age[0].name).to.eql(validators.max().name); | ||
}); | ||
}); | ||
describe("#custom validators", function () { | ||
describe("custom validators", function () { | ||
it("should accept a single validation function", function () { | ||
it("should accept a single validation function", function () { | ||
var schema = new Schema({ | ||
age: { | ||
type: Number, | ||
validate: oldEnough | ||
} | ||
var schema = new Schema({ | ||
age: { | ||
type: Number, | ||
validate: oldEnough | ||
} | ||
}); | ||
expect(schema.validators.age).to.be.an("array"); | ||
expect(schema.validators.age).to.have.length(1); | ||
expect(schema.validators.age[0]).to.eql(oldEnough); | ||
}); | ||
expect(schema.validators.age).to.be.an("array"); | ||
expect(schema.validators.age).to.have.length(1); | ||
expect(schema.validators.age[0]).to.eql(oldEnough); | ||
}); | ||
it("should accept an array of validation functions", function () { | ||
it("should accept an array of validation functions", function () { | ||
var schema = new Schema({ | ||
age: { | ||
type: Number, | ||
validate: [oldEnough, notTooOld] | ||
} | ||
}); | ||
var schema = new Schema({ | ||
age: { | ||
type: Number, | ||
validate: [oldEnough, notTooOld] | ||
} | ||
expect(schema.validators.age).to.be.an("array"); | ||
expect(schema.validators.age).to.have.length(2); | ||
expect(schema.validators.age[0]).to.eql(oldEnough); | ||
expect(schema.validators.age[1]).to.eql(notTooOld); | ||
}); | ||
expect(schema.validators.age).to.be.an("array"); | ||
expect(schema.validators.age).to.have.length(2); | ||
expect(schema.validators.age[0]).to.eql(oldEnough); | ||
expect(schema.validators.age[1]).to.eql(notTooOld); | ||
}); | ||
}); | ||
describe("mixed validators", function () { | ||
describe("mixed validators", function () { | ||
it("should register default validators always before custom validators", function () { | ||
it("should register default validators always before custom validators", function () { | ||
var schema = new Schema({ | ||
age: { | ||
type: Number, | ||
min: 1, | ||
validate: [oldEnough, notTooOld] | ||
} | ||
}); | ||
var schema = new Schema({ | ||
age: { | ||
type: Number, | ||
min: 1, | ||
validate: [oldEnough, notTooOld] | ||
} | ||
expect(schema.validators.age).to.be.an("array"); | ||
expect(schema.validators.age).to.have.length(3); | ||
expect(schema.validators.age[0].name).to.eql("validateMin"); | ||
expect(schema.validators.age[1]).to.eql(oldEnough); | ||
expect(schema.validators.age[2]).to.eql(notTooOld); | ||
}); | ||
expect(schema.validators.age).to.be.an("array"); | ||
expect(schema.validators.age).to.have.length(3); | ||
expect(schema.validators.age[0].name).to.eql("validateMin"); | ||
expect(schema.validators.age[1]).to.eql(oldEnough); | ||
expect(schema.validators.age[2]).to.eql(notTooOld); | ||
}); | ||
}); | ||
}); | ||
describe(".validate", function () { | ||
describe("extended schemas", function () { | ||
describe("sync validators", function (done) { | ||
it("should merge all specified validators", function () { | ||
var schema = new Schema({ | ||
age: { | ||
required: true, | ||
max: 99, | ||
validate: oldEnough | ||
} | ||
}), | ||
extended = schema.extend({ | ||
age: { | ||
required: false, | ||
validate: notTooOld | ||
} | ||
}), | ||
ageValidators = extended.validators.age.toString(); | ||
var schema = new Schema({ | ||
age: { | ||
type: Number, | ||
validate: function (age) { | ||
return age > 8; | ||
} | ||
} | ||
}); | ||
expect(ageValidators).to.contain(validators.max(), oldEnough, notTooOld); | ||
expect(ageValidators).to.not.contain(validators.required()); | ||
}); | ||
schema.validate({ age: 18 }, function (validation) { | ||
expect(validation.result).to.eql(true); | ||
expect(validation.failedFields).to.eql([]); | ||
done(); | ||
}); | ||
@@ -162,42 +168,11 @@ | ||
describe("async validators", function (done) { | ||
describe(".validate(model, callback)", function () { | ||
var schema = new Schema({ | ||
age: { | ||
type: Number, | ||
validate: function (age, callback) { | ||
setTimeout(function () { | ||
callback(age > 8); | ||
}, 0); | ||
} | ||
} | ||
}); | ||
schema.validate({ age: 18 }, function (validation) { | ||
expect(validation.result).to.eql(true); | ||
expect(validation.failedFields).to.eql([]); | ||
done(); | ||
}); | ||
}); | ||
describe("mixed validators", function () { | ||
function validateAgeAsync(age, callback) { | ||
setTimeout(function () { | ||
callback(age > 8 || "fail-async"); | ||
}, 0); | ||
} | ||
function validateAgeSync(age) { | ||
return age > 8 || "fail-sync"; | ||
} | ||
it("should pass if async & sync validators pass", function (done) { | ||
var asyncSpy = chai.spy(validateAgeAsync), | ||
syncSpy = chai.spy(validateAgeSync); | ||
it("should work with sync validators", function (done) { | ||
var schema = new Schema({ | ||
age: { | ||
type: Number, | ||
validate: [asyncSpy, syncSpy] | ||
validate: function (age) { | ||
return age > 8; | ||
} | ||
} | ||
@@ -207,4 +182,2 @@ }); | ||
schema.validate({ age: 18 }, function (validation) { | ||
expect(asyncSpy).to.have.been.called.once(); | ||
expect(syncSpy).to.have.been.called.once(); | ||
expect(validation.result).to.eql(true); | ||
@@ -216,18 +189,17 @@ expect(validation.failedFields).to.eql({}); | ||
it("should fail if a async and sync validator fail", function (done) { | ||
var asyncSpy = chai.spy(validateAgeAsync), | ||
syncSpy = chai.spy(validateAgeSync); | ||
it("should work with async validators", function (done) { | ||
var schema = new Schema({ | ||
age: { | ||
type: Number, | ||
validate: [asyncSpy, syncSpy] | ||
validate: function (age, callback) { | ||
setTimeout(function () { | ||
callback(age > 8); | ||
}, 0); | ||
} | ||
} | ||
}); | ||
schema.validate({ age: 6 }, function (validation) { | ||
expect(asyncSpy).to.have.been.called.once(); | ||
expect(syncSpy).to.have.been.called.once(); | ||
expect(validation.result).to.eql(false); | ||
expect(validation.failedFields.age).to.contain("fail-async", "fail-sync"); | ||
schema.validate({ age: 18 }, function (validation) { | ||
expect(validation.result).to.eql(true); | ||
expect(validation.failedFields).to.eql({}); | ||
done(); | ||
@@ -237,51 +209,102 @@ }); | ||
it("should fail if the async validator fails & sync passes", function (done) { | ||
var asyncSpy = chai.spy(function (age, callback) { | ||
setTimeout(function () { | ||
callback("fail-async"); | ||
}, 0); | ||
}), | ||
syncSpy = chai.spy(validateAgeSync); | ||
describe("mixed validators", function () { | ||
var schema = new Schema({ | ||
age: { | ||
type: Number, | ||
validate: [asyncSpy, syncSpy] | ||
} | ||
function validateAgeAsync(age, callback) { | ||
setTimeout(function () { | ||
callback(age > 8 || "fail-async"); | ||
}, 0); | ||
} | ||
function validateAgeSync(age) { | ||
return age > 8 || "fail-sync"; | ||
} | ||
it("should pass if async & sync validators pass", function (done) { | ||
var asyncSpy = chai.spy(validateAgeAsync), | ||
syncSpy = chai.spy(validateAgeSync), | ||
schema = new Schema({ | ||
age: { | ||
type: Number, | ||
validate: [asyncSpy, syncSpy] | ||
} | ||
}); | ||
schema.validate({ age: 18 }, function (validation) { | ||
expect(asyncSpy).to.have.been.called.once(); | ||
expect(syncSpy).to.have.been.called.once(); | ||
expect(validation.result).to.eql(true); | ||
expect(validation.failedFields).to.eql({}); | ||
done(); | ||
}); | ||
}); | ||
schema.validate({ age: 6 }, function (validation) { | ||
expect(asyncSpy).to.have.been.called.once(); | ||
expect(syncSpy).to.have.been.called.once(); | ||
expect(validation.result).to.eql(false); | ||
expect(validation.failedFields.age).to.contain("fail-async"); | ||
done(); | ||
it("should fail if a async and sync validator fail", function (done) { | ||
var asyncSpy = chai.spy(validateAgeAsync), | ||
syncSpy = chai.spy(validateAgeSync), | ||
schema = new Schema({ | ||
age: { | ||
type: Number, | ||
validate: [asyncSpy, syncSpy] | ||
} | ||
}); | ||
schema.validate({ age: 6 }, function (validation) { | ||
expect(asyncSpy).to.have.been.called.once(); | ||
expect(syncSpy).to.have.been.called.once(); | ||
expect(validation.result).to.eql(false); | ||
expect(validation.failedFields.age).to.contain("fail-async", "fail-sync"); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
it("should fail if the sync validator fails & async passes", function (done) { | ||
var asyncSpy = chai.spy(function(age, callback) { | ||
callback(true); | ||
}), | ||
syncSpy = chai.spy(function(age) { | ||
return "fail-sync"; | ||
it("should fail if the async validator fails & sync passes", function (done) { | ||
var asyncSpy = chai.spy(function (age, callback) { | ||
setTimeout(function () { | ||
callback("fail-async"); | ||
}, 0); | ||
}), | ||
syncSpy = chai.spy(validateAgeSync), | ||
schema = new Schema({ | ||
age: { | ||
type: Number, | ||
validate: [asyncSpy, syncSpy] | ||
} | ||
}); | ||
schema.validate({ age: 6 }, function (validation) { | ||
expect(asyncSpy).to.have.been.called.once(); | ||
expect(syncSpy).to.have.been.called.once(); | ||
expect(validation.result).to.eql(false); | ||
expect(validation.failedFields.age).to.contain("fail-async"); | ||
done(); | ||
}); | ||
var schema = new Schema({ | ||
age: { | ||
type: Number, | ||
validate: [asyncSpy, syncSpy] | ||
} | ||
}); | ||
schema.validate({ age: 8 }, function (validation) { | ||
expect(asyncSpy).to.have.been.called.once(); | ||
expect(syncSpy).to.have.been.called.once(); | ||
expect(validation.result).to.eql(false); | ||
expect(validation.failedFields.age).to.contain("fail-sync"); | ||
done(); | ||
it("should fail if the sync validator fails & async passes", function (done) { | ||
var asyncSpy = chai.spy(function(age, callback) { | ||
callback(true); | ||
}), | ||
syncSpy = chai.spy(function(age) { | ||
return "fail-sync"; | ||
}), | ||
schema = new Schema({ | ||
age: { | ||
type: Number, | ||
validate: [asyncSpy, syncSpy] | ||
} | ||
}); | ||
schema.validate({ age: 8 }, function (validation) { | ||
expect(asyncSpy).to.have.been.called.once(); | ||
expect(syncSpy).to.have.been.called.once(); | ||
expect(validation.result).to.eql(false); | ||
expect(validation.failedFields.age).to.contain("fail-sync"); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
33188
15
849
Updatedvalue@0.3.x