AJS_Validator User Guide

Applying ValidationDefs

ValidationDefs can be applied to objects in two ways. The immediate approach is to use the applyValidationDef method of AJS_Validator, which accepts three arguments, the nature of which are as follows:

  1. MethodOwner – a mandatory reference to the object to which a given ValidationDef should be applied.
  2. ValidationDef – a mandatory reference to the ValidationDef that should be applied to the object referred to by the first argument.
  3. MethodOwnerName – an optional string that holds a user-defined description of the object referred to by the first argument. This is used solely in the exception notifications that AJS_Validator generates.

applyValidationDef performs various checks on the contents of the ValidationDef before using the AJS object to attach internal prefix- and suffix-functions to the methods indicated in the MethodNames property of that ValidationDef.

Following application of the ValidationDef, all calls to the methods in question cause the prefix function to execute automatically and transparently first, during which the arguments passed by the caller are compared against any constraints defined within the CallDef preperty of the ValidationDef.

Contents Applying ValidationDefs
ValidationDef Queues
ValidationDef Immutability
Multiple ValidationDefs
Transitive ValidationDefs
Allowing Method-Owner Changes
Suspension and Resumption
ValidationDefCtrls

Similarly, on return from a given method, the suffix executes (with equal transparency, from the application's point of view), and compares the value or reference returned (if any) from the method against any constraints defined in the RtnDef property of the ValidationDef. If any argument or return-value contravenes the restrictions laid down in the ValidationDef, AJS_Validator will invoke its exception handler, and Example 6 illustrates these essential principles.

Note that any preValidator and postValidator that the ValidationDef carries are also executed as part of the validation process – see the Pre- and Post-Validators sections for precise details on the calling order and its rationale. AJS_Validator can also apply the preventExtensions, seal and freeze methods of the Object constructor to objects that are returned from methods. It can also 'tag' such objects with a user-defined string, which, as mentioned previously, is of value in debugging. See the relevant sections of this guide for more information on these features.

Note also that AJS_Validator provides no way of removing a ValidationDef from a method. The underlying functionality of the AJS object upon which AJS_Validator depends permits this idea, but no compelling reason exists for such a feature. This is because it is designed to be an aid during development, not something that forms an integral part of a given application's deployed-functionality, where the the ability to add and remove ValidationDefs arbitrarily could be necessary.

However, do remember here that the support that AJS_Validator provides for Pre- and Post-Validator functions allows the dynamic modification of the effects of a given ValidationDef. You can also suspend (and resume subsequently) the effect of a given ValidationDef by use of the ValidationDefCtrl object (see below) that is returned when that ValidationDef is applied using applyValidationDef (see below).


 // -- Example 6 --------------------------------------------------------------

 var MyObj =
     {
     method_A : function (StringArgument)
        {

        }

     };

 var method_A_ValidationDef =
    {
    MethodNames     : ["method_A"],                             // The MethodNames property is redundant - without it,
    CallDef         :                                           // method_A will still be subject to the ValidationDef's
       [                                                        // strictures - but it is shown here in order to clarify
       {                                                        // the example.
       AllowClasses : ["String"]                                // Ignore for the moment the syntax here; in essence, it
       }                                                        // means that the first argument to method_A must be a
       ]                                                        // string object (not a string-literal).

    };

 AJS_Validator.applyValidationDef (MyObj, method_A_ValidationDef);

 MyObj        .method_A           (42);                         // Error, callers of method_A should pass only strings.

 -- Output --------------------------------------------------------------------

 AJS_Validator_Exception: argument 0 (StringArgument) in call to method_A is of
 class NumberLiteral (value: '42'), which is not included in the corresponding
 ArgDef's AllowClasses property in the corresponding ValidationDef. Permissible
 classes are: String. (Object's AJS_Validator_Tag : undefined, Method-Owner's
 Class: Object, Method-Owner's AJS_Validator_Tag: undefined).
            

Note too that you do not have to instantiate your ValidationDefs and then pass them to AJS_Validator.applyValidationDef (or pushValidationDef – explored below) in two steps. One of JavaScript's great strengths is the ability to define functions and object-literals anonymously, which allows one to define and pass them to a function within the parentheses that invoke a call to that function. It follows that you can simplify your code, and de-clutter the namespace to boot by defining ValidationDefs 'inline' in your calls to applyValidationDef and pushValidationDef.

Most of the examples in this guide follow this style, and Example 6 is re-presented in this fashion in Example 7 (with the elision of other whitespace too).

Finally in this section, note that you can apply a ValidationDef to any method in an application, other than constructors. The AJS-object user guide goes into this in depth, but to summarise here: applying a ValidationDef to a constructor will work in terms of validating the arguments it is passed, but the reference returned to the constructor's caller will not point to the new object but will be a reference to the proxy-function that the AJS object emplaces when the ValidationDef is applied.

This means that using AJS_Validator to validate constructor calls directly will cause working code to fail. Given this, and as the AJS-object user guide states, the (somewhat awkward) solution in this case is to wrap the call to a given constructor in a forwarding-method and to apply the ValidationDef to that.


 // -- Example 7 --------------------------------------------------------------

 var MyObj =
     {
     method_A : function (StringArgument) { }
     };

 AJS_Validator.applyValidationDef (MyObj,                       // Here we pass the method owner, and create and
     {                                                          // pass the ValidationDef in a single statement.
     MethodNames : ["method_A"],
     CallDef     : [ { AllowClasses : ["String"] } ]
     });

 MyObj .method_A (42);                                          // Error as before, callers of method_A should pass
                                                                // only string objects.

 -- Output --------------------------------------------------------------------

 AJS_Validator_Exception: argument 0 (StringArgument) in call to method_A is of
 class NumberLiteral (value: '42'), which is not included in the corresponding
 ArgDef's AllowClasses property in the corresponding ValidationDef. Permissible
 classes are: String. (Object's AJS_Validator_Tag : undefined, Method-Owner's
 Class: Object, Method-Owner's AJS_Validator_Tag: undefined).
            

ValidationDef Queues

The second way of applying ValidationDefs involves the use of the pushValidationDef method. This also accepts two arguments, which are the MethodOwner, and the ValidationDef that should be applied to that object's methods; and it adds the ValidationDef to an internal queue, where it remains until a subsequent call to the applyValidationDefQueue method. A call to that method then applies each ValidationDef to its respective object in the order in which the ValidationDefs were pushed onto the queue, emptying the queue as it does.

Example 8 demonstrates the use of pushValidationDef and applyValidationDefQueue.

Note that the issues that apply to ValidationDef queues are identical to those that apply to LogDef- and LoadDef-queues. This is because they each use an internal definition-manager object that they created by invoking the createAJS_DefMgr function-reference that you pass when you instantiate the AJS_Validator object. Given this, you should apprise yourself of the common points that apply to definition-object queues, which are covered in the overview on AJS_DefMgr.


 // -- Example 8 --------------------------------------------------------------

 var MyObj   =
    {
    method_A : function ()
       {

       }

    };

 var YourObj =
    {
    method_B : function ()
       {

       }

    };

 // -------------------------------------

 var method_A_Def =
    {
    MethodNames   : ["method_A"]                                // Again, redundant, but given here to explicate.
    };                                                          // ValidationDef has no other properties in order to clarify
                                                                // this demonstration.
 var method_B_Def =
    {
    MethodNames   : ["method_B"]                                // Redundant also, but given here too for the purpose of
    };                                                          // explication. The ValidationDef has no other properties
                                                                // in order to clarify this demonstration.

 // -------------------------------------

 AJS_Validator.pushValidationDef       (MyObj,   method_A_Def);
 AJS_Validator.pushValidationDef       (YourObj, method_B_Def);

 MyObj  .method_A                      ();                      // These calls will operate normally with no validation.
 YourObj.method_B                      ();

 AJS_Validator.applyValidationDefQueue ();                      // Queued ValidationDefs are now applied to their respective
                                                                // methods.
 MyObj  .method_A                      ();                      // These calls now operate as before, but with the benefit of
 YourObj.method_B                      ();                      // validation scrutiny (assuming the ValidationDefs concerned
                                                                // have some constraining properties).
            

ValidationDef Immutability

AJS_Validator 'freezes' all AJS_Validator-related properties of a ValidationDefs as soon as it is pushed to the pending-queue or applied, except for the MethodNames property, and any user-defined properties that have no meaning to AJS_Validator. This means that you cannot modify the properties concerned (nor their properties in turn), nor can you add or remove properties. Example 9 demonstrates this.

Without this feature, it would be possible for errant debugging-code, or out-and-out stupidity, to cause logical errors during call-validation, and to cause confusion on the part of you and your fellow developers. This would negate the benefits of using AJS_Validator in the first place. Note, however, that this does not limit you in your use of AJS_Validator, because Pre- and Post-Validators give you all the dynamic latitude you could ever need, meaning that there is no need to update ValidationDefs after they have been pushed or applied.

Note also that AJS_Validator has no need to freeze the MethodNames property because it does not refer to it after the ValidationDef in question has been applied to the methods concerned. This allows you to re-use a given ValidationDef on an entirely different method-set on other objects (see the next section).


 // -- Example 9 --------------------------------------------------------------

 var MyObject  =
    {
    someMethod : function (SomeArg) { }
    };

 var MyValidationDef  =
    {
    CallDef    : [ { NeverUndefined : false } ]
    };

 AJS_Validator.applyValidationDef (MyObject, MyValidationDef);

 MyValidationDef.CallDef[0].NeverUndefined = true;              // No can do - ValidationDef was
                                                                // frozen by applyValidationDef.

 -- Output --------------------------------------------------------------------

 TypeError: "NeverUndefined" is read-only
            

Multiple ValidationDefs

Note that, were it permitted, it would be redundant for an application to apply the same ValidationDef to a given method-owner more than once, because you can express every stricture you need in regard of a given method within a single ValidationDef. Moreover, applying different ValidationDefs to the same method(s) could result in logical errors. For example, one ValidationDef could disallow Number objects, whereas another ValidationDef that was applied to the same method might disallow everything except Numbers.

Given this, AJS_Validator does not allow the application of multiple ValidationDefs to the same method and, in order to prevent this, the AJS_DefMgr it uses maintains an internal list of those ValidationDefs that have been applied so far in a given execution of the application. It follows that attempts to apply a ValidationDef to a method that already possesses a ValidationDef will cause AJS_Validator to call the exception handler, and Example 10 demonstrates this.

Similarly, where a given ValidationDef has been pushed to the queue in association with a given method-owner, you cannot push that ValidationDef a second or third time etc. for that method owner. Nor is it possible to push a given ValidationDef to the queue (in association with a given method owner), and to then apply that ValidationDef directly to the same method-owner using applyValidationDef. Were this allowed, a subsequent call to applyValidationDefQueue would cause AJS_Validator to invoke the exception handler because of the existing ValidationDef for the method(s) in question.

It follows, however, that you may push/apply a given ValidationDef multiple times, as long as each push/application concerns different methods of the same method-owner, or occurs in the context of a different method-owner. Under optimum conditions, this means that a single ValidationDef may be applied to a great many different objects in the application, perhaps using different MethodNames properties for each application. This can yield great economy, which bears out an observation made in the AJS_Validator Motivation page.

Finally in this section, note that even if a ValidationDef is 'empty' (i.e. specifies no constraints etc.) its application to a given method will preclude the application of other ValidationDefs to that method, even though the empty ValidationDef will have no validating-effect on the method concerned.


 // -- Example 10 -------------------------------------------------------------

 var MyObj =
    {
    method_A    : function () { }
    };

 AJS_Validator.applyValidationDef (MyObj,
    {
    MethodNames : ["method_A"]                                  // Again, redundant, but given explicitly here
    });                                                         // in order to clarify the example.

 AJS_Validator.applyValidationDef (MyObj,
    {
    MethodNames : ["method_A"]                                  // Also redundant, but present here in order to
    });                                                         // clarify matters.

 -- Output --------------------------------------------------------------------

 AJS_Validator_Exception: cannot accept the ValidationDef passed to
 AJS_Validator.applyValidationDef, because the method 'method_A' of the
 method-owner in question is subject to a ValidationDef that was applied
 previously.
            

Transitive ValidationDefs

JavaScript allows one to to assign a method to which one object refers to another object, or to replace the function-reference that constitutes a method in a given object with another function-reference. Indeed, this is the fundamental principle upon which the entire AspectJS library pivots. Consider Example 11. Here, method_A starts life as a propert of MyObj, but then gains dual ownership because its reference is copied to MyOtherObj.

However, copying function reference in this way, and then applying a ValidationDef in the context of one of the method-owners, will not confer the effect of the ValidationDef to other objects that possess a reference to the function in question. I.e. calling the method in the context of another object will not invoke AJS_Validator's validation mechanism.

Instead, if you want to make a given ValidationDef 'transitive', you should apply it to the method in question before you copy the function-reference. This will copy not the reference to the function itself, but the reference to the proxy function that the AJS object instantiated when AJS_Validator applied the ValidationDef.

Similarly, you may apply a ValidationDef to a method, but then call the function in question simply by applying the parentheses operator to the raw function-reference. To wit:

   var MyObj =
      {
      a : function () { }
      };

   AJS_Validator.applyValidationDef (MyObj, { ... });

   var b = MyObj.a;

   b (); // b is now subject to validation.

Now every call to b will be validated without it being a property of an object.


 // -- Example 11 -------------------------------------------------------------

 var MyObj      =
    {
    method_A    : function () { console.log ("Method executed"); }
    };

 var MyOtherObj =
    {

    };

 MyObj     .method_A ();                                        // Straightforward call.

 MyOtherObj.method_A = MyObj.method_A;                          // Assign the method to MyOtherObj (MyObj
                                                                // still holds the same reference).
 MyOtherObj.method_A ();                                        // Execute the method on MyOtherObj.

 -- Output --------------------------------------------------------------------

 Method Executed
 Method Executed
            

Allowing Method-Owner Changes

Note that if you do execute a function in the context of a different object, or in no context at all (i.e. this === undefined), AJS_Validator will detect the change-in/absence-of method-owner when the function is invoked, by comparing the this-reference with the MethodOwner reference that was provided when the ValidationDef was pushed or applied. It follows that it will also detect the use of call and apply when you use those methods to point the this reference at some other object.

In the strictest application of design-by-contract principles, this amounts to a 'breach of contract', and so AJS_Validator's default behaviour is to call the exception handler on detecting such a change, as Example 12 demonstrates.

However, there are times when you may wish to copy a method to another object, or to execute a method in the context of another object. Alternatively, your design may involve passing a function reference to another function (i.e. passing a call-back function – a common technique), where you wish to validate calls to the first function using AJS_Validator. In this case you are forced to make the function a method of some object, even if you do nothing with that object later, otherwise you will be unable to apply a ValidationDef to the function.


 // -- Example 12 -------------------------------------------------------------

 var MyObj      =
     {
     method_A   : function () { console.log ("Method executed"); }
     };

 var MyOtherObj = { };

 AJS_Validator.applyValidationDef (MyObj, { });                 // An empty ValidationDef is all that is required for
                                                                // this demonstration.
 MyObj        .method_A ();                                     // No problem, the call does not contravene the
                                                                // ValidationDef (it contains no properties).
 MyOtherObj   .method_A = MyObj.method_A;                       // Copy the function-reference.

 MyOtherObj   .method_A ();                                     // Now AJS_Validator will detect the change in the
                                                                // 'this' reference.

 -- Output --------------------------------------------------------------------

 Method executed

 AJS_Validator_Exception: the 'this' reference in call to method_A does not
 refer to the MethodOwner stipulated in that method's ValidationDef
 (Method-Owner's Class: Object, Method-Owner's AJS_Validator_Tag: undefined).
            

Given this, and when you wish AJS_Validator to treat your reference juggling as as non-exceptional, you can suppress its default behaviour by using the AllowDiffThis (ALLOW DIFFerent THIS) property of the ValidationDef concerned, giving it a value of true. Example 13 shows this.

On a related track, note that AJS_Validator cannot detect the loss of an object or method from the execution environment (i.e. if it goes out of all scope), meaning that if you call delete on a method within a given object, or if the method is garbage-collected, the underlying AJS_DefMgr's list of methods that possess ValidationDefs will still possess a reference to that method.

The implication here is that, in scenarios where methods are born and die frequently (and where these methods have ValidationDefs), the applied-ValidationDefs list will accrue ever-more redundant entries at a significant rate. In principle, and in truly-extreme cases, this may incur troublesome storage-issues. However, AJS_Validator is a development tool, and is not something that, normally, will form part of a deployed application, so this issue should never impinge on end-users.


 // -- Example 13 -------------------------------------------------------------

 var MyObj        =
     {
     method_A     : function () { console.log ("Method executed"); }
     };

 var MyOtherObj   = { };

 AJS_Validator    .applyValidationDef (MyObj,
    {
    AllowDiffThis : true                                        // Tell AJS_Validator that a different 'this' is OK.
    });

 MyObj            .method_A ();                                 // Straight-forward call, as before - no problem.

 MyOtherObj       .method_A = MyObj.method_A;                   // Copy the function-reference.

 MyOtherObj       .method_A ();                                 // OK, AJS_Validator will ignore the change in method-owner.

 -- Output --------------------------------------------------------------------

 Method executed
 Method executed
            

Suspension and Resumption

Note that you can suspend the behaviour of AJS_Validator by means of a call to that object's suspend method. That is to say, a call to AJS_Validator.suspend, will cause subsequent calls to applyValidationDef, pushValidationDef and applyValidationDefQueue to have no effect. This extends to any ValidationDefs that have already been applied to methods in the application, which is to say that, following a call to suspend, methods that are subject normally to validation are free of such policing, including the execution of any Pre- and Post-Validators.

The complementary method, resume, reverses the action of suspend, such that calling AJS_Validator.resume after a call to suspend, will reinstate validation. Example 14 demonstrates the suspend and resume methods in action.

Suspending ValidationDefs does not 'detach' them from the methods to which they have been applied, and so they continue to incur some latency, although their effect in this respect is much smaller than when they are in a resumed state; and do note here that no performance degradation is apparent even in an application where active ValidationDefs are applied throughout.

Note also that you can suspend and resume the action of AJS_Validator as many times as you want during a run of the application concerned. Note too that calling suspend a second or third time etc. before calling resume has no effect, nor do similarly-repeated calls to resume. That is to say that suspend and resume do not exhibit 'stack-like' (or 'push-down') behaviour where, say, three calls to suspend require three complementary calls to resume to cause AJS_Validator to resume normal operation.


 // -- Example 14 -------------------------------------------------------------

 var MyObj   =
    {
    method_A : function (NumberArg) { console.log ("Method executed NumberArg-value is: " + NumberArg); }
    };

 AJS_Validator.applyValidationDef (MyObj,
    {
    CallDef  : [ { AllowClasses : ["NumberLiteral"] } ]         // Ignore for the moment the syntax here. As with
                                                                // Example 6, it means only that the first argument
    });                                                         // to method_A must be a number-literal.

 AJS_Validator.suspend  ();                                     // suspend validation operations.
 MyObj        .method_A ("I am not a number");                  // Erroneous call gets through.
 AJS_Validator.resume   ();                                     // Resume validation operations.
 MyObj        .method_A ("Nor am I");                           // Now we cannot get away with the erroneous call.

 -- Output --------------------------------------------------------------------

 Method executed NumberArg-value is: I am not a number

 AJS_Validator_Exception: argument 0 (NumberArg) in call to method_A is of class
 StringLiteral (value: 'Nor am I'), which is not included in the corresponding
 ArgDef's AllowClasses property in the corresponding ValidationDef. Permissible
 classes are: NumberLiteral. (Object's AJS_Validator_Tag : undefined,
 Method-Owner's Class: Object, Method-Owner's AJS_Validator_Tag: undefined).
            

ValidationDefCtrls

In order to give fine-grained support for suspension and resumption of method-call scrutiny, AJS_Validator also defines the concept of a 'ValidationDefCtrl' (VALIDATION DEFinition ConTRoL). A ValidationDefCtrl is a small, two-method object that is returned from a call to AJS_Validator.applyValidationDef, and Example 15 shows the composition of such an object.

A ValidationDefCtrl may be used to suppress and reinstate specifically the validation functionality that AJS_Validator.applyValidationDef applies to the application-methods covered by the corresponding ValidationDef. That is to say: if a given ValidationDef covers methods A, B and C in a given object, the suspend and resume methods of the ValidationDefCtrl object that AJS_Validator.applyValidationDef returns when applying that ValidationDef will suspend/resume respectively the validation on all three of those methods. (Note that it is not possible to acquire the relevant ValidationDefCtrl object when an application uses AJS_Validator.pushValidationDef, nor can you acquire the ValidationDefCtrl for a given ValidationDef, when you call applyValidationDefQueue.)

It is unlikely that you will avail yourself of this feature of AJS_Validator very often, as the role of ValidationDefs is to sit quietly in the background as unobtrusive but ever-watchful sentinels. Indeed, support for ValidationDefCtrls was implemented with considerable reluctance, due to the desire to keep AJS_Validator as simple and easy to understand as possible.

Nevertheless, there is a rather-abstruse situation where management of the action of certain Pre- and Post-Validators is necessary (specifically: in the use of ValidationDefs to validate the use of the AJS object itself, the code for which can be found within AJS.val.js), in which case ValidationDefCtrls are invaluable (see the appropriate section on Pre-/Post-Validators for more information). Given this, there is a chance that you may also encounter cases where you find ValidationDefCtrls to be of value in your own work, and the fact is that they add very little overhead, hence their support within AJS_Validator.

Note that AJS_Validator renders ValidationDefCtrls immutable in the same way that it freezes the AJS_Validator-related properties (other than MethodNames) of ValidationDefs. Note also that the applyValidationDef method returns undefined rather than a reference to a ValidationDefCtrl object if AJS_Validator has been suspended.


 // -- Example 15: ValidationDefCtrl Composition (pseudo code) ----------------

 var ValidationDefCtrl =
    {
    suspend : function () { ... },
    resume  : function () { ... }
    };