Using Commuter Modules
The OpenInsight Form Designer’s event handlers are convenient for rapid development, but become difficult to maintain in a large project. Arev programmers traditionally use a single program for each window, calling that program from every window or prompt process, passing a parameter to indicate which process to run. In Arev, we called these programs “commuter modules”. In today’s buzzwords, you would say that the Arev coders were implementing a model-view-controller design pattern. This same pattern works well in OpenInsight.
OpenInsight improves upon the Arev event calling method in several ways: The event model is much richer, so it is easier to detect how and exactly when to respond to events. You can pass any number of parameters to your event module, so you don’t need to write as much code to guess the current situation. You have Get_Property and Set_Property to affect the window directly, so it is easier to get exactly the result you want. Finally, the 64k limit on program size is gone, so you will not need to worry about program size.
OpenInsight now includes a QuickEvent, which will call your commuter module. This lesson will talk about when to use the QuickEvent, when you cannot use it, and how to write a commuter module, which is compatible with the QuickEvent interface.
The QuickEvent Window
Notice how the parameters are filled in with default values.
Parameter | Meaning |
---|---|
@SELF | Current Control |
@EVENT | Current Event |
@PARAM1…@PARAM6 | Default parameters passed by the event. |
Some events, for example Click, pass no parameters, others, like Poschanged, pass values. The OpenInsight event handler will pass the values through to the commuter module in the @param1 parameter, @param2 parameter, etc., etc., etc.
The Commuter Module
The Obj_Call_Event function calls a stored procedure executable with the same name as the OpenInsight Window Executable. For example, those events sent within the JOB window would call the JOB commuter module.
Function JOB(CtrlEntId,Event,Param1,Param2,Param3,Param4,Param5) Declare Function Unassigned * Initialize Unassigned Variables If Unassigned(Param1) Then Param1 = "" If Unassigned(Param2) Then Param2 = "" If Unassigned(Param3) Then Param3 = "" If Unassigned(Param4) Then Param4 = "" If Unassigned(Param5) Then Param5 = "" If Unassigned(Param6) Then Param6 = "" * Set the Return Value ReturnValue = 0 * The Body of the Commuter Module Begin Case Case CtrlEntId _eqc @Window:".JOBS" ; Gosub Search_Jobs Case CtrlEntId _eqc @Window:".CITY"; Gosub Search_City End Case * Return to the Calling Event Return ReturnValue
Syntax
Function JOB(CtrlEntId,Event,param1,param2,param3,param4,param5,param6)
The Obj_Call_Event function will pass the following arguments:
CtrlEntID – The fully qualified control name.
Event – The current event.
Param1 … Param6 – The parameters passed by the event.
Experience shows that 6 parameters are sufficient for most situations.
It is good practice to assign the arguments that have not been passed. This will avoid Variable Not Assigned Value errors.
If Unassigned(param1) then param1 = “”
The function returns a value. For a QuickEvent called commuter module the return value should be a 0 since there are no other steps within the event chain.
ReturnValue = 0
The body of the commuter module is the branch processing based on the control and event. A Case Statement is useful for this process with Gosub calls to internal subroutines. The code in the internal subroutines will handle all the events for the control.
Begin Case Case CtrlEntID eqc @window:” JOBS”; Gosub Search_Jobs Case CtrlEntID eqc @window:”.CITY”; Gosub Search_City End Case
A function requires a Return statement and Return Value.
Return ReturnValue
Using the Commuter Module for System Events
The example code outlined above works for those events that are the last in the event chain. If code needs to occur elsewhere in the event chain then the QuickEvent cannot be used, the control’s event handler must be used. The following is sample code within the event handler for the Read Event of the JOB form
declare function Obj_Call_Event returnValue = Obj_Call_Event(CtrlEntId,"READ") return ReturnValue
This code will call the commuter module and pass through the event chain based on the return value of the commuter module function.
In conclusion, the commuter module functionality can add to the ease of maintenance of applications by separating the event logic from the OpenInsight window and creating a central location for source code behind a window.
QuickEvents, Commuter Modules and Passing-By-Reference
Variables in Basic+ are passed to stored procedures "by reference", meaning that if you change a passed variable in the called procedure, that change is reflected in the calling routine. This behavior is critical to the way many system functions work (such as iconv and oconv) and is very useful in returning more than one item of data from the called procedure.
However, one place where this rule breaks down is when calling stored procedures via a QuickEvent, such as routing an event to a commuter module as seen in the image at top.
When you call a stored procedure in this way the system eventually passes your request to a routine called ExecNpHandler() which dispatches the event and its arguments to the desired target, however at this point the pass-by-reference chain gets broken because a copy of the argument is passed to the stored procedure rather than the original.
For example, if we used Send_Event() to trigger the OMNIEVENT in the above example, and the commuter module changed Param2, the Send_Event() caller would not see the change. (Note that if you use an Event Script handler then passing the event arguments by reference works as you would expect, and you will see changes made to your arguments by the Event Script handler.)
During development of the OpenInsight 10 IDE this proved to be a problem because the framework relies on a series of OMNIEVENT calls to pass messages between various entities, and sometimes arguments are updated to let the caller know of things like state changes. With something as complex as the IDE, writing the events as a series of Event Scripts was impractical so we took the opportunity to update ExecNpHandler() to respect the pass-by-reference paradigm instead. Now you get to see your changes in a consistent manner regardless of whether you prefer Event Scripts or QuickEvents.
So, is this change likely to impact you? In most cases the answer is no, as ExecNpHandler() is normally the last handler to be called in the event chain, and any changes to passed arguments are usually ignored. Where this might be an issue is when:
- You call forward_Event() from an Event Script, or fire an event via Send_Event(), and
- You rely on the arguments you passed to either of the above functions remaining unchanged, and
- You changed the arguments passed to you in the commuter module, either by carelessness or design.
In this set of circumstances you will probably see an effect, and you may have to modify your code accordingly.