The NeverUndefined property is the simplest explicit assertion that an ArgDef or RtnDef can make. As noted in the section on ArgDefs the default for an ArgDef is that the argument is mandatory (i.e. it cannot be undefined). This means that a caller must supply a value for the argument in question, unless its corresponding ArgDef has an NeverUndefined property with a value of false.
Example 23 demonstrates the use of the NeverUndefined property, where method_A is defined as taking one optional argument. This means that AJS_Validator will accept happily either of the subsequent calls to method_A.
Note that failure to provide a non-boolean value for NeverUndefined will cause AJS_Validator to call its exception handler. In other words: a NeverUndefined property can be only true or false.
// -- Example 23 ------------------------------------------------------------- var MyObj = { method_A : function (StringArg) { } }; AJS_Validator.applyValidationDef (MyObj, { CallDef : [ { NeverUndefined : false } // The first (and only) argument CAN be undefined (but not null) ] }); MyObj.method_A ("Hello World"); // Both of these calls satisfy the ValidationDef for method_A MyObj.method_A ();
As with the NeverUndefined property, AJS_Validator's default behaviour for an argument represented by an ArgDef is that its value cannot be null (irrespective of whether it can be undefined or not). However, using the NeverNull property with a value of true relaxes this behaviour to allow, as one would expect, an argument to be null.
Example 24 illustrates the use of the NeverNull property.
As with the other boolean parameters that ValidationDefs can possess, failure to provide a non-boolean value for NeverNull will cause AJS_Validator to invoke the exception handler, which is to say that a NeverNull property must be either true or false.
Note that the term 'null' applies here in its strictest sense. It does not, for example, encompass empty strings, as a reference to an instance of the empty string is still a non-null value (even though conditional-logic in ECMAScript evaluates the empty string as a value of false). Given this, you should use the RegExp property (see below) with an appropriate regular-expression when you need to trap empty strings.
// -- Example 24 ------------------------------------------------------------- var MyObj = { method_A : function (Arg_0) { } }; AJS_Validator.applyValidationDef (MyObj, // The ValidationDef will be applied to all methods in MyObj. { CallDef : [ { NeverNull : false } // The first and only argument CAN be null. ] }); MyObj.method_A (null); // Fine, the ArgDef allows null... MyObj.method_A (); // ...But not undefined. -- Output -------------------------------------------------------------------- AJS_Validator_Exception: argument 0 (Arg_0) in call to method_A is undefined, which the corresponding ArgDef within the corresponding ValidationDef disallows (Method-Owner's Class: Object, Method-Owner's AJS_Validator_Tag: undefined).
When applying AJS_Validator to numeric arguments or return-values, you can constrain the value in question using the NeverNegative, NeverZero, NeverPositive and NeverFractional assertions. If an argument or return value is a number then AJS_Validator will apply these definitions accordingly, and these properties of an ArgDef or RtnDef act exactly as you would expect (NeverFractional, for example, constrains a value such that it must only ever be an integer). Example 25 demonstrates their use on the battlefield.
Note that AJS_Validator considers a positive value to be anything greater than zero, and a negative value to be anything less than zero. This is to say that AJS_Validator does not recognise the concept of minus-zero.
// -- Example 25 ------------------------------------------------------------- var MyObj = { doSomethingNumerical : function (NumericalArg) { } }; AJS_Validator.applyValidationDef (MyObj, { CallDef : [ { NeverPositive : true } // The argument must be zero or negative. ] }); MyObj.doSomethingNumerical (42); // No can do, the argument must be zero or negative. -- Output -------------------------------------------------------------------- AJS_Validator_Exception: argument 0 (NumericalArg) in call to doSomethingNumerical is a number with the value of 42, which the corresponding NeverPositive property of the corresponding ValidationDef disallows (Method-Owner's Class: Object, Method-Owner's AJS_Validator_Tag: undefined).
Note also that, while NeverNegative, NeverZero and NeverPositive can be used in conjunction with each other in an ArgDef or a RtnDef, they cannot appear together where they all have a value of true. This is because to constrain a numeric value to never be negative, zero nor positive is to require a value that cannot exist (in this universe).
Example 26 demonstrates this; and the Property Concurrence and Collisions section provides greater detail on property collisions.
// -- Example 26 ------------------------------------------------------------- var MyObj = { doSomethingNumerical : function (NumericalArg) { } }; AJS_Validator.applyValidationDef (MyObj, { CallDef : [ { NeverNegative : true, // Error, if these three appear together NeverZero : true, // then at least one must be false. NeverPositive : true } ] }); -- Output -------------------------------------------------------------------- AJS_Validator_Exception: ArgDef-member 0 of the CallDef property of a ValidationDef passed to AJS_Validator.applyValidationDef possesses NeverPositive, NeverNegative and NeverZero properties, all of which are true.
To police numerical arguments or returns more precisely than the NeverNegative/NeverZero/NeverPositive properties allow, you can use the MaxVal and MinVal (MAXimum-/MINimum-VALue) properties in a given ArgDef or RtnDef. As you would expect, MaxVal sets the upper limit, and MinVal sets the lower limit on a numerical value. Note that use of MaxVal does not require the use of MinVal as well, and vice versa.
Example 27 illustrates these points.
Note also that you can specify a particular value by setting both MaxVal and MinVal to that value. Note too that providing non-numeric values for MaxVal and MinVal will cause AJS_Validator to invoke the exception handler, as will setting MinVal to a value that is greater than any MaxVal that you provide (see the Property Collisions section for more information on this issue).
// -- Example 27 ------------------------------------------------------------- var MyObj = { doSomethingNumerical : function (NumericalArg) { } }; AJS_Validator.applyValidationDef (MyObj, { CallDef : [ { MaxVal : 42 // The argument cannot be greater than 42. } ] }); MyObj.doSomethingNumerical (43); // Problem. -- Output -------------------------------------------------------------------- AJS_Validator_Exception: argument 0 (NumericalArg) in call to doSomethingNumerical is a number with the value of 43, which is greater than the maximum of 42 that is permitted by the corresponding MaxVal property in the corresponding ValidationDef (Method-Owner's Class: Object, Method-Owner's AJS_Validator_Tag: undefined).
AJS_Validator also allows you to constrain the maximum and minimum number of elements that an array can hold, and to constrain the length of strings. This is the purpose respectively of the MaxLen and MinLen properties (MAXimum-/MINimum-LENgth), which are optional numerical-properties of an ArgDef or RtnDef.
As with MaxVal and MinVal, use of MaxLen does not require the use of MinLen, and vice versa. That is: you can state the lower bound on the number of elements in an array, without stipulating an upper bound, or you can assert an upper bound while stating no lower-bound on the member-count. Similarly, to constrain an array or string such that it is of a specific length, use the MinLen and MaxLen properties together, giving them both the value you desire.
As with MaxVal and MinVal, you cannot set a MinLen value that is greater than the value of any corresponding MaxLen (again, see the Property Collisions section for more information). Finally in this section, note that it is easy to constrain strings and arrays such that they cannot be empty: state simply a MinLen value of 1.
Example 28 demonstrates MaxLen and MinLen doing their scalar-thing like only they know how.
// -- Example 28 ------------------------------------------------------------- var MyObj = { method_A : function (ArrayArg) { console.log (ArrayArg[1]); } }; AJS_Validator.applyValidationDef (MyObj, { CallDef : [ { MinLen : 2, MaxLen : 4 } // The length of any arrays or strings ] // passed must be between 2 and 4. }); var OneMemberArray = [ "Hydrogen" ]; var ThreeMemberArray = [ "Lithium", "Sodium", "Potassium" ]; var FiveMemberArray = [ "Boron", "Carbon", "Nitrogen", "Oxygen", "Fluorine" ]; try { MyObj.method_A (OneMemberArray); // Error, array must have at least two } // members. catch (E) { console.log (E.name + ": " + E.message); } try { MyObj.method_A (ThreeMemberArray); // OK, array has just three members. } catch (E) { console.log (E.name + ": " + E.message); } try { MyObj.method_A (FiveMemberArray); // Error, array must have four members } // at most. catch (E) { console.log (E.name + ": " + E.message); } -- Output -------------------------------------------------------------------- AJS_Validator_Exception: argument 0 (ArrayArg) in call to method_A is an array of length 1, which is smaller than the minimum of 2 that is permitted by the corresponding MinLen property in the corresponding ValidationDef (Method-Owner's Class: Object, Method-Owner's AJS_Validator_Tag: undefined). Sodium AJS_Validator_Exception: argument 0 (ArrayArg) in call to method_A is an array of length 5, which is greater than the maximum of 4 that is permitted by the corresponding MaxLen property in the corresponding ValidationDef (Method-Owner's Class: Object, Method-Owner's AJS_Validator_Tag: undefined).
To constrain string objects such that their contents conform to a particular textual pattern, you should use the RegExp property. The value for this must be a regular expression, such that to provide anything else in the relevant ArgDef or RtnDef will cause AJS_Validator to call its exception handler.
Example 29 shows the RegExp property in action. Here the method called computeDistance takes two strings that represent UK postcodes, and (we assume) computes the distance between the two districts they denote. Clearly, however, a bug in the application could see a non-valid postcode passed as the first, second, or both arguments – as has happened here; the 'B' in the second argument should be a number not a letter (probably an '8', were this the result of a clerical error).
However, we can see-off such irksome problems by applying a ValidationDef to the method, stipulating that the character-patterns for each string-argument must conform to the regular expression that the respective ArgDefs hold. Duly, therefore, the ArgDef members of the CallDef shown in Example 29 give as their RegExp properties the regular expression for detecting UK postcodes.
// -- Example 29 ------------------------------------------------------------- var MyObj = { computeDistance : function (PostCode_A, PostCode_B) { // ... } }; AJS_Validator.applyValidationDef (MyObj, { CallDef : [ { RegExp : /^[A-Z]{1,2}[0-9R][0-9A-Z]? [0-9][ABD-HJLNP-UW-Z]{2}$/ }, // Both arguments must be UK postcodes. { RegExp : /^[A-Z]{1,2}[0-9R][0-9A-Z]? [0-9][ABD-HJLNP-UW-Z]{2}$/ } ] }); MyObj.computeDistance ("SW11 5XZ", "SWB 9JL"); // "SWB" is the culprit here. -- Output -------------------------------------------------------------------- AJS_Validator_Exception: argument 1 (PostCode_B) in call to computeDistance does not conform to the regular-expression specified by the RegExp-property of the corresponding ArgDef within the corresponding ValidationDef. Argument's value was: SWB 9JL (Method-Owner's Class: Object, Method-Owner's AJS_Validator_Tag: undefined).
The addition to the ECMAScript standard of the preventExtensions, seal and freeze methods of the Object constructor brought the much-needed ability to apply degrees of immutability to objects. They are congruent with design-by-contract programming entirely because they prevent inadvertent, foolish or downright-malicious changes to objects that should remain as they are, and the table shown here documents the effects of these methods.
Change Existing Properties | Delete Existing Properties | Add New Properties | |
preventExtensions | |||
seal | |||
freeze |
In the light of this, AJS_Validator supports three Arg- and Rtn-Def properties called NeverExtensible, NeverNotSealed and NeverNotFrozen, which fit hand-in-glove with the rationale behind ES5's mutability-limiting methods. When they possess a value of true (their defaults are false), they require argument and return values that refer to extensible types to refer to objects to which the preventExtensions, seal and freeze methods respectively have been applied.
For example, if an ArgDef carries a NeverNotFrozen property with a value of true, the argument to which that ArgDef relates must refer to an object that has been frozen, assuming that the object is a freezable type (as with the other simple constraints, this permissive behaviour disappears when you use the Allow-/Disallow-Classes properties.).
Example 30 demonstrates these points.
Note that it is redundant for NeverExtensible to appear in conjunction with NeverNotSealed (where they both have a value of true), as it is for either of those to appear alongside a NeverNotFrozen property (with a value of true). This is because, if an object has been frozen, it is, by implication, sealed; and if it is sealed it must be inextensible.
Moreover, it is contradictory for, say, the Seal property to appear with a value of true alongside, say, a PreventExt property with a value of false, because sealing an object renders it closed to extension. Given these points, AJS_Validator will call the exception handler if any conjunctions of NeverExtensible, NeverNotSealed and NeverNotFrozen occur in a given Arg-/Rtn-Def.
// -- Example 30 ------------------------------------------------------------- var MyObj = { }; var MyOtherObj = { someMethod : function (SealedObjRef) { } }; AJS_Validator.applyValidationDef (MyOtherObj, { MethodNames : [ "someMethod" ], CallDef : [ { NeverNotSealed : true } ] // The argument must always be sealed. }); MyOtherObj.someMethod (MyObj); // Oops! MyObj has not been sealed. -- Output -------------------------------------------------------------------- AJS_Validator_Exception: argument 0 (SealedObjRef) in call to someMethod refers to an object that is not sealed, which the corresponding NeverNotSealed property in the corresponding ArgDef disallows. (Method-Owner's Class: Object, Method-Owner's AJS_Validator_Tag: undefined).