At this point in this guide, it is important to summarise and explain the validation model that AJS_Validator implements. It is also important to consider the possibility of collison between different Arg- and Rtn-Def properties.
Contents |
Overview Validation Model Property Concurrence and Collisions |
That we should strive for cohesion is a fundamental design-principle in programming, and thus one might argue that the AllowClasses property should be a single class-name string only, not an array of such strings. I.e. that AllowClasses should only ever permit one class of object per argument or return-value for a given application-method, rather than allow multiple classes.
However, imposing this restriction would preclude using AJS_Validator on methods that support polymorphic arguments and/or polymorphic returns, where the class of the object in question may differ from call to call (which gives rise to duck-typing). Indeed, in an early alpha-version of AJS_Validator, the ancillary method isIn would accept objects of class Object or Array (the current version of isIn does not operate in this way).
It is for this reason that AJS_Validator allows you to specify more than one class in an AllowClasses property, as Example 42 shows. This demands in turn that AJS_Validator treat the numerically-oriented Never... properties of Arg- and Rtn-Defs permissively, as it must also with the Max... and Min... properties; tedious conflicts would arise were the tool to operate restrictively instead.
That is to say: a NeverNegative constraint (for example) is applied only if the object in question is of class Number – the presence of a NeverNegative property does not demand implicitly that the object in question be a number. If it did, this would conflict with any corresponding AllowClasses property that allowed numbers and other classes of object. Example 43 demonstrates this.
This permissive model applies also to the other Arg- and Rtn-Def properties (aside from NeverUndefined and NeverNull – see below), for which the defaults are false, zero, infinity and -infinity, depending on the property in question. For example, AJS_Validator will allow a method's caller to pass an empty array as an argument unless the corresponding ArgDef carries a MinLen property with a non-zero value.
Notably, however, the defaults for the NeverUndefined and NeverNull properties are restrictive rather than permissive by default. This is because the fact that you are using AJS_Validator to validate the values of arguments means that it is highly likely that you are seeking defined, non-null values for these objects. In other words, argument values are not undefined or null usually, unless the argument in question is optional (in which case you pass nothing when calling a method with such an argument, or you pass undefined/null as a placeholder where a subsequent argument in the method's calling signature must carry a defined non-null value).
In the light of this, were NeverUndefined and NeverNull to operate in the same way as the other 'Never...' properties, where their defaults were false, you would be forced to provide true values for these properties in the majority of ArgDefs that you construct. As Example 44 shows, this would be tedious and time-consuming, and would serve also to bloat your ValidationDefs. Hence their defaults are true, which makes it possible to demand a defined, non-null value for a given argument simply by creating an empty ArgDef (that is: just a pair of braces, as noted in the section on ArgDefs).
Validation-Model Summary |
A given simple (non-class/-tag) constraint does not require objects of a congruent class. |
Simple constraints are applied only when congruent with the class of object under consideration. |
AllowClasses must, for a given simple constraint, cite at least one class that is congruent with that constraint. |
AllowClasses permits you to name multiple classes in order to permit polymorphic method-arguments. |
Other than NeverUndefined and NeverNull, the defaults for all boolean constraints are false. |
NeverUndefined and NeverNull default to true. |
// -- Example 42 ------------------------------------------------------------- var MyObj = { method_A : function (A, B) { console.log (A + B); } }; AJS_Validator.applyValidationDef (MyObj, { CallDef : [ { AllowClasses : ["StringLiteral", "NumberLiteral"] } ] }); method_A ("Hello World"); // One of these calls would be impossible method_A ( 42); // were multiple classes disallowed in an // AllowClasses property.
// -- Example 43 ------------------------------------------------------------- var MyValidationDef = { CallDef : [ { AllowClasses : ["StringLiteral", "NumberLiteral"], // A restrictive model would disallow MinLen : 42, // multiple class-names here, which MaxVal : 42 // would preclude either MinLen or MaxVal. } ] };
// -- Example 44 ------------------------------------------------------------- // Without default values of true for NeverUndefined and NeverNull, every // Arg- and Rtn-Def would require the presence of those properties, which // would be tedious, time consuming, and would contribute to code-bloat. var MyValidationDef = { CallDef : [ { MinVal : 42, MaxVal : 84, NeverUndefined : true, NeverNull : true }, { NeverZero : true, NeverUndefined : true, NeverNull : true }, { NeverNotSealed : true, NeverUndefined : true, NeverNull : true } ] };
As earlier sections of this guide mention, the value of certain properties must concur with each other if they are present within a given ArgDef or RtnDef; and some must never appear together at all. Given this, AJS_Validator will call its exception handler whenever property collision occurs within an ArgDef or RtnDef.
For example, you cannot use NeverNegative, NeverZero or NeverPositive together, where they all have a value of true. Similarly, if a given Arg- or Rtn-Def precludes positive numbers then any corresponding MaxVal property must have a zero or negative value, and Example 45 illustrates this.
This principle extends to include the use the Disallow-/Allow-Classes properties. For example, if you use MaxVal and/or MinVal, any corresponding AllowClasses property must include 'Number'; similarly, it must contain a 'String' or 'StringLiteral' member if a RegExp property exists, as Example 46 shows.
It follows too that an AllowClasses property must not appear alongside a DisallowClasses property. Principally, this is because such practice would be redundant were it permitted – if you allow only a sub-set of something then, by definition, you are disallowing everything else. However, it also serves to prevent the same class being both allowed and disallowed, where trapping such contradictions would require additional logic within AJS_Validator.
Example 47 highlights this argument.
The equivalent principle holds for AllowTags and DisallowTags, which is to say that they cannot appear together; moreover, conflict can arise also when stipulating permitted class-names alongside tags. An Arg-/Rtn-Def cannot possess an AllowClasses property that includes a class-name that cannot possess user-defined properties ('BooleanLiteral', for example), where there is a corresponding AllowTags properties. This extends to NeverUndefined and NeverNull; these cannot appear with values of false alongside an AllowTags property because the concept of adding new properties to undefined and null doesn't apply.
Equivalently, a RtnDef cannot carry an ApplyTag property and an AllowClasses property that includes a class that cannot possess properties, as shown in Example 48. The NeverUndefined/NeverNull restriction applies here also.
The full set of possible property-collision permutations is presented in a table in the AJS_Validator-API section of this site.
Concurrence and Collision Summary |
All numerical-Never... properties must concur with any MaxVal/MinVal. |
The value of any Max... constraint must be equal to or greater than any corresponding Min... constraint. |
An Allow... property cannot appear alongside its corresponding Disallow... property. |
An Allow-/Disallow-Classes property must not preclude the class implied by a given simple-constraint. |
An AllowClasses property must not include inextensible classes when it appears alongside ApplyTag or AllowTags. |
// -- Example 45 (pseudo code) ----------------------------------------------- { NeverPositive : true, // No can do: if a number must be zero or negative, MinVal : 42 // it cannot have a MinVal with a positive value. }
// -- Example 46 (pseudo code) ----------------------------------------------- { AllowClasses : ["Object"], // No way Jose: the presence of the RegExp property RegExp : /^c[aou]t$/ // requires that AllowClasses include String and/or } // StringLiteral.
// -- Example 47 (pseudo code) ----------------------------------------------- { AllowClasses : ["Number" ], // One property here is redundant; if you allow DisallowClasses : ["BooleanLiteral"] // numbers then, by definition you disallow } // everything else (and vice versa).
// -- Example 48 (pseudo code) ----------------------------------------------- { AllowClasses : ["Object", "StringLiteral"], ApplyTag : "MyGloriousObject" // Impossible, AllowClasses permits string-literals. }