Clean up registry implementation, so that it doesn't taint the entire codebase and stores a pointer to the registry in the type reference itself

master
Sven Slootweg 6 years ago
parent e15fe4504b
commit 6957b2216e

@ -12,14 +12,13 @@ const nullMissingFields = require("./util/null-missing-fields");
module.exports = function createTrait(name, schema, options = {}) { module.exports = function createTrait(name, schema, options = {}) {
let schemaDescriptors = mapObj(schema, (key, rule) => { let schemaDescriptors = mapObj(schema, (key, rule) => {
return generateDescriptor(rule, key, true, options._registry); return generateDescriptor(rule, key, true);
}); });
let schemaKeys = getSchemaKeys(schema); let schemaKeys = getSchemaKeys(schema);
return typeRules._createTypeRule({ return typeRules._createTypeRule({
_isTrait: true, _isTrait: true,
_registry: options._registry,
_name: name, _name: name,
applyImplementation: function (implementation) { applyImplementation: function (implementation) {
/* FIXME: Verify that there are no extraneous keys or unspecified values */ /* FIXME: Verify that there are no extraneous keys or unspecified values */
@ -44,9 +43,9 @@ module.exports = function createTrait(name, schema, options = {}) {
let implementationDescriptors = mapObj(implementation, (key, value) => { let implementationDescriptors = mapObj(implementation, (key, value) => {
if (value != null && value._isSlotRule === true) { if (value != null && value._isSlotRule === true) {
return generateDescriptor(schema[key], key, true, options._registry); return generateDescriptor(schema[key], key, true);
} else { } else {
return generateDescriptor(value, key, false, options._registry); return generateDescriptor(value, key, false);
} }
}); });

@ -9,17 +9,16 @@ const generateValidator = require("./generate-validator");
const getSchemaKeys = require("./util/get-schema-keys"); const getSchemaKeys = require("./util/get-schema-keys");
const nullMissingFields = require("./util/null-missing-fields"); const nullMissingFields = require("./util/null-missing-fields");
module.exports = function createType(name, schema, options = {}) { module.exports = function createType(name, schema) {
if (schema._isTypeRule === true) { if (schema._isTypeRule === true) {
return typeRules._createTypeRule({ return typeRules._createTypeRule({
_typeName: name, _typeName: name,
_isTypeAlias: true, _isTypeAlias: true,
_alias: schema, _alias: schema
_registry: options._registry
}); });
} else { } else {
let propertyDescriptors = mapObj(schema, (key, rule) => { let propertyDescriptors = mapObj(schema, (key, rule) => {
return generateDescriptor(rule, key, false, options._registry); return generateDescriptor(rule, key, false);
}); });
let protoProperties = { let protoProperties = {
@ -56,7 +55,6 @@ module.exports = function createType(name, schema, options = {}) {
proto._type = factory; proto._type = factory;
factory._registry = options._registry;
factory._schemaKeys = schemaKeys; factory._schemaKeys = schemaKeys;
factory._isCustomType = true; factory._isCustomType = true;
factory._name = name; factory._name = name;

@ -4,9 +4,9 @@ const generateValidator = require("./generate-validator");
const guardedSet = require("./guarded-collections/set"); const guardedSet = require("./guarded-collections/set");
const guardedMap = require("./guarded-collections/map"); const guardedMap = require("./guarded-collections/map");
module.exports = function generateDescriptor(rule, key, allowSlot = false, registry) { module.exports = function generateDescriptor(rule, key, allowSlot = false) {
if (rule._isTypeRule === true) { if (rule._isTypeRule === true) {
let validator = generateValidator(rule, key, registry); let validator = generateValidator(rule, key);
if (rule._collectionType != null) { if (rule._collectionType != null) {
let guardedCollectionFactory; let guardedCollectionFactory;
@ -19,8 +19,8 @@ module.exports = function generateDescriptor(rule, key, allowSlot = false, regis
throw new Error(`Unknown collection type: ${rule._collectionType}`); throw new Error(`Unknown collection type: ${rule._collectionType}`);
} }
let valueGuard = generateValidator(rule._itemType, undefined, registry); let valueGuard = generateValidator(rule._itemType);
let keyGuard = (rule._keyType != null) ? generateValidator(rule._keyType, undefined, registry) : null; let keyGuard = (rule._keyType != null) ? generateValidator(rule._keyType) : null;
return [key, { return [key, {
enumerable: true, enumerable: true,

@ -12,7 +12,7 @@ const createUndefinedValidator = require("./validator-functions/undefined");
const getValueType = require("./util/get-value-type"); const getValueType = require("./util/get-value-type");
const errors = require("./errors"); const errors = require("./errors");
module.exports = function generateValidator(rule, name = "<unknown>", registry) { module.exports = function generateValidator(rule, name = "<unknown>") {
let baseRule; let baseRule;
if (rule._baseType != null) { if (rule._baseType != null) {
@ -37,7 +37,7 @@ module.exports = function generateValidator(rule, name = "<unknown>", registry)
baseRule = () => true; /* FIXME */ baseRule = () => true; /* FIXME */
} else if (rule._modifierType != null) { } else if (rule._modifierType != null) {
if (rule._modifierType === "either") { if (rule._modifierType === "either") {
let validators = rule._types.map((type) => generateValidator(type, undefined, registry)); let validators = rule._types.map((type) => generateValidator(type));
baseRule = function (value) { baseRule = function (value) {
let matched = false; let matched = false;
@ -60,7 +60,7 @@ module.exports = function generateValidator(rule, name = "<unknown>", registry)
throw new Error(`Unrecognized modifier: ${rule._modifierType}`); throw new Error(`Unrecognized modifier: ${rule._modifierType}`);
} }
} else if (rule._isTypeAlias === true) { } else if (rule._isTypeAlias === true) {
baseRule = generateValidator(rule._alias, name, registry); baseRule = generateValidator(rule._alias, name);
} else if (rule._isCustomType === true) { } else if (rule._isCustomType === true) {
let validator = rule._validator; let validator = rule._validator;
@ -77,7 +77,7 @@ module.exports = function generateValidator(rule, name = "<unknown>", registry)
baseRule = function (value) { baseRule = function (value) {
/* FIXME: Better error when the type is unknown */ /* FIXME: Better error when the type is unknown */
let actualType = registry._types.get(rule._name); let actualType = rule._registry._types.get(rule._name);
if (actualType._isCustomType) { if (actualType._isCustomType) {
if (actualType._validator.call(this, value) === true) { if (actualType._validator.call(this, value) === true) {
@ -87,7 +87,7 @@ module.exports = function generateValidator(rule, name = "<unknown>", registry)
} }
} else { } else {
if (aliasValidator == null) { if (aliasValidator == null) {
aliasValidator = generateValidator(actualType._alias, name, registry); aliasValidator = generateValidator(actualType._alias, name);
} }
let validatorResult = aliasValidator.call(this, value); let validatorResult = aliasValidator.call(this, value);
@ -110,21 +110,17 @@ module.exports = function generateValidator(rule, name = "<unknown>", registry)
} }
}; };
} else if (rule._isRegistryTrait === true) { } else if (rule._isRegistryTrait === true) {
if (registry == null) { baseRule = function (value) {
throw new Error("Registry-based type rules can only be used within the context of a type registry"); /* TODO: The below approach requires that traits be defined before the types that use them, and disallows trait registry references in `.implements` calls, due to _implementedTraits always needing to contain actual trait definitions; in the future, a better approach needs to be found for this such that trait registry references can be used everywhere. */
} else { let actualRule = rule._registry._traits.get(rule._name);
baseRule = function (value) {
/* TODO: The below approach requires that traits be defined before the types that use them, and disallows trait registry references in `.implements` calls, due to _implementedTraits always needing to contain actual trait definitions; in the future, a better approach needs to be found for this such that trait registry references can be used everywhere. */
let actualRule = registry._traits.get(rule._name);
/* FIXME: Better error when the trait is unknown */ /* FIXME: Better error when the trait is unknown */
if (value._type != null && value._type._implementedTraits != null && value._type._implementedTraits.has(actualRule)) { if (value._type != null && value._type._implementedTraits != null && value._type._implementedTraits.has(actualRule)) {
return true; return true;
} else { } else {
return new errors.ValidationError(`Expected object of a type with the ${rule._name} trait, got ${getValueType(value)} instead`); return new errors.ValidationError(`Expected object of a type with the ${rule._name} trait, got ${getValueType(value)} instead`);
} }
}; };
}
} else if (rule._isSelfRule === true) { } else if (rule._isSelfRule === true) {
baseRule = function (value) { baseRule = function (value) {
if (value instanceof this._type) { if (value instanceof this._type) {
@ -195,7 +191,7 @@ module.exports = function generateValidator(rule, name = "<unknown>", registry)
}); });
} }
} else { } else {
/* FIXME: Possibly special-case (for better performance) if the only extra rule is a 'default value' rule? This would avoid a `for` loop in the case where a value is explicitly specified. */ /* TODO: Possibly special-case (for better performance) if the only extra rule is a 'default value' rule? This would avoid a `for` loop in the case where a value is explicitly specified. */
for (let rule of rules) { for (let rule of rules) {
let result = rule.call(this, value); let result = rule.call(this, value);

@ -2,12 +2,12 @@
const generateValidator = require("./generate-validator"); const generateValidator = require("./generate-validator");
module.exports = function guardFunction(args, returnType, func, _registry) { module.exports = function guardFunction(args, returnType, func) {
let rules = args.map((arg) => { let rules = args.map((arg) => {
return generateValidator(arg, undefined, _registry); return generateValidator(arg);
}); });
let returnValueValidator = generateValidator(returnType, undefined, _registry); let returnValueValidator = generateValidator(returnType);
let guardedFunction = function (...params) { let guardedFunction = function (...params) {
let paramsWithDefaults = new Array(params.length); let paramsWithDefaults = new Array(params.length);

@ -11,11 +11,7 @@ module.exports = function createRegistry() {
_traits: new Map(), _traits: new Map(),
createType: function (name, schema, options = {}) { createType: function (name, schema, options = {}) {
if (!this._types.has(name)) { if (!this._types.has(name)) {
let combinedOptions = Object.assign({ let type = moduleAPI.createType(name, schema, options);
_registry: this
}, options);
let type = moduleAPI.createType(name, schema, combinedOptions);
this._types.set(name, type); this._types.set(name, type);
return type; return type;
} else { } else {
@ -24,30 +20,25 @@ module.exports = function createRegistry() {
}, },
createTrait: function (name, schema, options = {}) { createTrait: function (name, schema, options = {}) {
if (!this._traits.has(name)) { if (!this._traits.has(name)) {
let combinedOptions = Object.assign({ let trait = moduleAPI.createTrait(name, schema, options);
_registry: this
}, options);
let trait = moduleAPI.createTrait(name, schema, combinedOptions);
this._traits.set(name, trait); this._traits.set(name, trait);
return trait; return trait;
} else { } else {
throw new Error(`A trait named ${name} already exists in this registry`); throw new Error(`A trait named ${name} already exists in this registry`);
} }
}, },
guardFunction: function (args, returnType, func) {
return moduleAPI.guardFunction(args, returnType, func, this);
},
type: function (typeName) { type: function (typeName) {
return typeRules._createTypeRule({ return typeRules._createTypeRule({
_isCustomRegistryType: true, _isCustomRegistryType: true,
_name: typeName _name: typeName,
_registry: this
}); });
}, },
trait: function (traitName) { trait: function (traitName) {
return typeRules._createTypeRule({ return typeRules._createTypeRule({
_isRegistryTrait: true, _isRegistryTrait: true,
_name: traitName _name: traitName,
_registry: this
}); });
}, },
createRegistry: undefined createRegistry: undefined

@ -5,7 +5,6 @@ const expect = require("chai").expect;
const dm = require("../src"); const dm = require("../src");
/* FIXME: Disallow null/nothing/undefined in model definitions, as they make no semantic sense? But allow them for eg. function guards. */ /* FIXME: Disallow null/nothing/undefined in model definitions, as they make no semantic sense? But allow them for eg. function guards. */
/* FIXME: Registry */
/* FIXME: Test passing an already-guarded collection into a guarded collection factory - either stand-alone or in the context of a model? */ /* FIXME: Test passing an already-guarded collection into a guarded collection factory - either stand-alone or in the context of a model? */
let Identity = dm.createType("Identity", { let Identity = dm.createType("Identity", {
label: dm.string(), label: dm.string(),

Loading…
Cancel
Save