Release 10.1A: OpenEdge Development:
ADM and SmartObjects

> ## Writing super procedures

> Another task you perform when you extend the ADM, whether by creating a new class or by customizing an existing class, is writing super procedures; that is, adding to the super procedure file custom functions and/or internal procedures that support the special behavior of your class. If you are creating a new class, you do this for the standard super procedure file. If you are customizing an existing class, you do it for the custom super procedure file.

> Two important points to keep in mind when writing any functions or internal procedures that go into an ADM super procedure are:

> - The functions and internal procedures in an ADM super procedure must always refer to the procedure on whose behalf they run.

> - An ADM super procedure is designed to be shared among all running instances of the SmartObjects that use that super procedure.

> ##### References in super procedures to other procedures

> When you write the functions or internal procedures that go into an ADM super procedure, keep in mind there is nothing about a super procedure that identifies it as a super procedure. It is simply a Progress procedure file that is run as a persistent procedure and then added as a super procedure to some other running procedure. The source code or compiled r-code for a super procedure contains nothing special that identifies it as a super procedure.

> What is special about a super procedure is the style of the code within its functions and internal procedures, which must account for the fact that these entry points are run on behalf of some other procedure. ADM super procedures, therefore, always must refer to the procedure on whose behalf they run. The basic mechanism is the `TARGET-PROCEDURE` built-in function, which evaluates to the procedure handle of the procedure from which the entry point originally was invoked. The `{get}` and `{set}` include files, which provide a pseudo-syntax for getting and setting ADM properties, support this mechanism by always referring back to the `TARGET-PROCEDURE`, so you always should use `{get}` and `{set}` when writing property references in super procedures. In addition, these include files identify whether a given property can be accessed directly out of the ADMProps temp-table record in the `TARGET-PROCEDURE` or instead requires the execution of the actual `get` `propname` or `set` `propname` function. For detailed information on these property mechanisms, see the "{get} and {set} pseudo-syntax for object properties" section, and the "Get and set functions for object properties" section.

> For example, an internal procedure that operates on the `RowObject` buffer handle of its `TARGET` begins by retrieving that property value:

>     DEFINE VARIABLE hRowObject AS HANDLE NO-UNDO.
>     ...
>     {get Rowobject hRowObject}.

> To retrieve the value of a property from an object other than the `TARGET-PROCEDURE`, you can specify an optional final argument for the `{get}` and `{set}` include files that is the object’s procedure handle. For example, the following code gets the value of the `EnabledTables` property from the Data-Source of the `TARGET-PROCEDURE`:

>     {get DataSource hSource}.
>     {get EnabledTables cTables hSource}.

> You also can use the include file `{fn}` as a convenient way to invoke a function in `TARGET-PROCEDURE`; for example:

>     {fn openQuery}  /* invoke the openQuery function in my TARGET-PROCEDURE */

> Similarly, the variant `{fnarg}` invokes a function that takes a single argument; for example:

>     {fnarg colValues pcViewColList}.  /* invoke the colValues function in my
>                                          TARGET-PROCEDURE, and pass
>                                          pcViewColList as the one input parameter. */

> ##### Shared super procedures

> ADM super procedures are designed to be shared among all running instances of the SmartObjects that use that procedure.

> Note: It is possible to create a super procedure intended to serve exactly one running procedure at a time, but this is not the correct design for super procedures that are part of the ADM class hierarchy and that are started in the standard way (that is, by running `start-super-proc` in the ADM include files).

> Because ADM super procedures are shared, you must ensure their code never assumes continuity between calls. If a particular operation involves executing two entry points in a super procedure, the code should not share property values between calls (whether by using local variables defined in the `Definitions` section or by using another technique); instead, it should retrieve any property values needed at the top of each entry point. Although this might seem slightly inefficient, using `{get}` to retrieve a property value from the property temp-table compiles into a single in-line Progress 4GL `ASSIGN` statement and is quite fast. Using this technique assures your super procedures can be shared among all running instances of your new class.

> Likewise, because super procedures operate on behalf of other procedures, you always must `PUBLISH` events `FROM` `TARGET-PROCEDURE`. If you do not do this, your event normally will not have any effect, because the SmartObjects will not have subscribed to that event in the super procedure itself.

> Also, when you invoke one entry point in a super procedure from another, you must preserve the value of `TARGET-PROCEDURE` in the called entry point. For example, suppose your super procedure contains two procedures, `ProcA` and `ProcB`, and you want to run `ProcB` from `ProcA` in that super procedure. You have basically two choices, depending on whether `ProcB` is a `PRIVATE` internal procedure or function that should never be run from any other procedure, or an entry point you might want to run or to override from its `TARGET-PROCEDURE` or another procedure:

> - If `ProcB` is a `PRIVATE` internal procedure or function, one that should never be run from any other procedure, `ProcA` can run `ProcB` directly. In this case, if `ProcB` must refer to the target procedure (to get a property value, for example):

> - `TARGET-PROCEDURE` must be passed as an input parameter to `ProcB`.
> - `ProcB` must not use `{get}` or `{set}` to access the property.

> This is because the value of `TARGET-PROCEDURE` inside `ProcB` will not be `TARGET-PROCEDURE`—it will be the super procedure’s procedure handle, since it was executed directly from the super procedure. In such a case, it is normally better to pass any property values to `ProcB` from `ProcA`. The following two examples, which illustrate code in `ProcA`, show two ways to do this:

>     º
>     RUN procB (INPUT TARGET-PROCEDURE).
>     º

>     procB:
>     DEFINE INPUT PARAMETER hTarget AS HANDLE NO-UNDO.
>     DEFINE VARIABLE cDisplayedFields AS CHARACTER NO-UNDO.
>     º
>     cDisplayedFields = dynamic-function(‘getDisplayedFields’ in hTarget).

>     DEFINE VARIABLE cDisplayedFields AS CHARACTER NO-UNDO.
>     {get DisplayedFields cDisplayedFields}.
>     º
>     RUN procB (INPUT cDisplayedFields).

> - If `ProcB` is an entry point that you might want to run or override from its `TARGET-PROCEDURE` or another procedure, `ProcA` should invoke `ProcB` `IN` `TARGET-PROCEDURE` even though they are located in the same super procedure. In this way, references to `TARGET-PROCEDUR`E inside `ProcB` correctly evaluate to the handle of the SmartObject that caused `ProcA` to be invoked. (This also makes it possible to override `ProcB` in a SmartObject, if this is necessary and appropriate.) The following example, which illustrates code in `ProcA`, shows how to do this:

>     º
>     RUN procB IN TARGET-PROCEDURE.
>     º

>     procB:
>     DEFINE VARIABLE cDisplayedFields AS CHARACTER NO-UNDO.
>     {get DisplayedFields cDisplayedFields}.   /* This works because procB was
>     invoked in TARGET-PROCEDURE. */

Copyright © 2005 Progress Software Corporation www.progress.com Voice: (781) 280-4000 Fax: (781) 280-4095