AJS Object User-Guide

AffixCtrl Objects

addPrefix and addSuffix return a reference to an 'AffixCtrl' object that corresponds to the prefix or suffix function that has been added to the interceptee. It is through this that you may manipulate the prefix or suffix in question, and through which you may apply additional prefixes and suffixes (as an alternative to re-calling an AJS method on an existing intereptee, or calling the equivalent methods that the interceptee function-object gains when it acquires an initial affix).

Example 22 shows, in pseudo code, the composition of an AffixCtrl object. See the API documentation for full details.

Note here that, should client code lose all references to a given AffixCtrl object, it will be impossible to remove the affix function from the interceptee.

Contents AffixCtrl Objects
WrapperCtrl Objects
Affix Suspension and Resumption
Removing Affixes
AffixCtrl Life-Cycle
Getting/Setting Affix Functions

 // -- Example 22 - AffixCtrl-Object Snapshot (pseudocode) --------------------

 var MyAffixCtrl   =
    {
    addBefore      : function (affixFunc)    { ... },
    addAfter       : function (affixFunc)    { ... },

    remove         : function ()             { ... },

    isAttached     : function ()             { ... },
    getNumAffixes  : function ()             { ... },

    getFirstSib    : function ()             { ... },
    getLastSib     : function ()             { ... },

    getNext        : function ()             { ... },
    getPrev        : function ()             { ... },

    getFunc        : function ()             { ... },
    setFunc        : function (newAffixFunc) { ... },

    getSuspensions : function ()             { ... },

    suspend        : function ()             { ... },
    resume         : function ()             { ... },

    suspendAll     : function ()             { ... },
    resumeAll      : function ()             { ... },

    promote        : function ()             { ... },
    demote         : function ()             { ... }

    };
      

WrapperCtrl Objects

In line with this scheme, addWrapper returns a 'WrapperCtrl' object, which subsumes a reference to two AffixCtrl objects (one for the prefix and one for the suffix),

Example 23 shows (again, in pseudocode) the structure of a WrapperCtrl object. The interface that WrapperCtrls present is, essentially, a sub-set of that presented by AffixCtrl objects, in that both types support the same methods, except for:

  • add-Before/-After
  • get-FirstSib/-LastSib
  • get-Next/-Prev
  • getNumAffixes
  • getSuspensions

...Which are absent from WrapperCtrls. This is because those methods are meaningful only in the context of a single AffixCtrl object, and so WrapperCtrl objects support the getPrefix and getSuffix methods, which give client code access to the underlying AffixCtrl objects.

Note that, as with individual AffixCtrl objects, should client code lose all references to a given WrapperCtrl object (where there are no copies of its AffixCtrl-object references), it will be impossible to remove the corresponding affix-functions from the method in question.


 // -- Example 23 WrapperCtrl-Object Snapshot (pseudocode) --------------------

 var MyWrapperCtrl =
    {
    remove         : function ()             { ... },

    isAttached     : function ()             { ... },

    getPrefixCtrl  : function ()             { ... },
    getSuffixCtrl  : function ()             { ... },

    setFunc        : function (newAffixFunc) { ... },

    suspend        : function ()             { ... },
    resume         : function ()             { ... },

    suspendAll     : function ()             { ... },
    resumeAll      : function ()             { ... },

    promote        : function ()             { ... },
    demote         : function ()             { ... }

    };
      

Affix Suspension and Resumption

Affix-functions can be 'suspended' and 'resumed'. suspending an affix means that it remains attached to the interceptee but does not execute when that method is invoked, where resuming a suspended affix is to reinstate its operation.

Example 24 illustrates this.

Note that suspend and resume have 'stack-like' behaviour (in both Affix- and Wrapper-Ctrl objects). This means that, for example, calling suspend twice on an affix requires two calls to resume to reinstate it. Similarly, three calls to suspend require three calls to resume to reinstate the affix, and so on. To determine an affix's degree of suspension, call the getSuspensions method of the AffixCtrl object concerned.

In line with this, calling suspend on a WrapperCtrl object suspends both the prefix and suffix, and calling resume on a WrapperCtrl object reinstates those affixes. Furthermore, a call to the suspend method of a WrapperCtrl object, where only one of its affixes is active, causes suspension of that affix, while its counterpart's suspension count increases by one. Similarly, a call to the resume method of a WrapperCtrl object, where only one of the affixes is suspended, decrements its suspension count by one (which may mean it is reinstated), while its counterpart remains active.

Note that an affix function can suspend itself during its execution, and a prefix can suspend/resume any corresponding suffixes and vice versa. Note also that suspension/resumption has no effect on the methods that the Affix-/Wrapper-Ctrl object in question supports. It is still possible to use, say, the addBefore method when an affix is suspended.


 // -- Example 24 -------------------------------------------------------------

 function prefixFunc () { console.log ("prefixFunc executed"); }
 function suffixFunc () { console.log ("suffixFunc executed"); }

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

 AJS.addPrefix (MyObj, "method_A", prefixFunc);

 var SuffixObj = AJS.addSuffix (MyObj, "method_A", suffixFunc);

 SuffixObj.suspend  ();
 MyObj    .method_A ();

 SuffixObj.resume   ();
 MyObj    .method_A ();

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

 prefixFunc executed
 method_A executed
 prefixFunc executed
 method_A executed
 suffixFunc executed
      

Removing Affixes

To remove a given prefix or suffix, simply call the remove method of the corresponding AffixCtrl object. In the case of wrappers, calling the remove method of a given WrapperCtrl object will remove both of its affixes simultaneously. These methods return the AffixCtrl/WrapperCtrl object in question.

In Example 25, the code applies a single prefix before calling the interceptee. The prefix is then removed, meaning that a subsequent call to the interceptee results in normal non-prefixed execution.

Note that it is entirely possible for an affix to remove one of its siblings or even itself as it executes (and for an interceptee to remove one of its affixes), as long as a reference to the corresponding AffixCtrl object is in scope. Indeed, AJS_ODL exploits this to remove loading prefixes from interceptees after it has issued a given loading-request.

Moreover, a prefix can remove its interceptee's suffixes, as can a suffix remove its interceptee's prefixes. Note that when an affix removes itself, and it returns a value, that value is still passed on to any following affix-function (see the sections on multiple prefix and multiple suffix function signatures for clarification).


 // -- Example 25 -------------------------------------------------------------

 function prefixFunc () { console.log ("prefixFunc executed"); }

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

 var PrefixObj = AJS.addPrefix (MyObj, "method_A", prefixFunc);

 MyObj    .method_A ();

 PrefixObj.remove   ();

 MyObj    .method_A ();

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

 prefixFunc executed
 method_A executed
 method_A executed
      

AffixCtrl Life-Cycle

As mentioned elsewhere in this guide, when an interceptee loses all of its affixes, the interception mechanism for that interceptee disappears, meaning that the relationship between the interceptee and its owner-object returns to normal. Moreover, once its corresponding affix function has been removed, an AffixCtrl or WrapperCtrl object cannot be 're-attached' to the original (or any other) interceptee. Removing an affix-function or a prefix/suffix pair that are governed by a corresponding WrapperCtrl object also puts the object in question into an emasculated state, such that calling its methods has no effect, where those methods return only 'safe' or null values such as zero or null.

This renders such objects useless thereafter in relation to the AJS object, and so, as long as the application retains no references to a given Affix-/Wrapper-Ctrl object post-removal, and the interceptee has no other affixes, all storage that was consumed in maintaining the interception mechanism is recovered by the run-time's garbage collector.

Note that AffixCtrl objects have a 'single use' nature because implementing a mechanism that allowed detached AffixCtrls to be re-attached to a given interceptee would complicate the AJS object's implementation. Moreover, such a mechanism would be redundant, as the same effect can be achieved simply by calling the addBefore or addAfter methods of another AffixCtrl object to re-attach the affix function concerned (the AJS object supports multiple affixes for a given interceptee), or by calling AJS.add-Prefix/-Suffix/-Wrapper again.

Note also that use of the ValidationDef in AJS.val.js will cause AJS_Validator to alert you should your code make a call to a 'dead' Affix-/Wrapper-Ctrl object. This enables you to ensure that you application runs as efficiently as possible when using the the AJS object, by guarding against 'space leaks' (which are distinct from memory leaks).

Getting/Setting Affix Functions

It is also possible to access and change a given affix function. This is accomplished using the getFunc, setFunc, methods of the AffixCtrl type, which operate as you would expect.

Note that the setFunc method returns nothing (rather than the old function-reference it has replaced). This is because the AffixCtrl and WrapperCtrl types are designed to be as interchangeable as possible, and so, were the setFunc method of AffixCtrl objects to return the old value, this the WrapperCtrl implementation of setFunc would also have to return something. However, given that a WrapperCtrl object subsumes a prefix and suffix, it would have to return a object containing two values.

Clearly this object would differ in type from the single scalar-value returned by the AffixCtrl implementation, which would erode the substitutability of AffixCtrls and WrapperCtrls. It is for the same reason that WrapperCtrl objects do not support a getFunc method.

Example 26 shows the setFunc method in action.


 // -- Example 26 -------------------------------------------------------------

 function prefixfunc_0 () { console.log ("prefixfunc_0 executed - affix func-ref changed to prefixfunc_1"); }
 function prefixfunc_1 () { console.log ("prefixfunc_1 executed"); }

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

 var AffixCtrl = AJS.addPrefix (MyObj, "method_A", prefixfunc_0);

 MyObj.method_A ();

 AffixCtrl.setFunc (prefixfunc_1);

 MyObj.method_A ();

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

 prefixfunc_0 executed - affix func-ref changed to prefixfunc_1
 method_A executed
 prefixfunc_1 executed
 method_A executed