Enhancing BASIC+ Part 3: Shortening the Commute
In this series of articles, we’ve been exploring how a precompiler can be used to enhance the BASIC+ language. So far we’ve discussed how to implement a precompiler and introduced the SRP PreCompiler’s For…Each loop. Now we step it up a notch by showing you how the SRP PreCompiler will drastically improve your quality of life when it comes to maintaining a common type of stored procedure: the commuter module.
The Commuter Module
“Commuter Module” is a term in the OpenInsight community describing a single stored procedure that handles all the events for a given window. It is a single module that travels, or commutes, with the form. There are multiple ways to route a Form’s events to its commuter module, such as explicitly calling it from a Quick Event or by calling it automatically via Promoted Events. How one calls a commuter module is not the scope of this post, but rather, how do we make our lives better when it comes to writing the commuter module itself.
There are three essential components to a commuter module:
- Generic parameters. A commuter module needs enough generic parameters to handle the event with the most parameters. Usually, programmers declare around ten parameters just to avoid adding more later.
- GoSub branching. A commuter module needs to translate the current event and its control into some logic that will jump to a GoSub containing the event handler.
- Event handling. A commuter module has one GoSub for each event it handles. Within this handler the generic parameters are used to make important decisions.
Here is a skeleton commuter module (limited to 5 generic parameters for space consideration) for an imaginary window called MY_WINDOW:
Compile function MY_WINDOW_EVENTS(CtrlEntId, Event, Param1, Param2, Param3, Param4, Param5) Window = @Window[1, "F*"] Control = Field(CtrlEntId, ",", 2, 3) // Branch to event handler Begin Case Case Control EQ Window Begin Case Case Event EQ "CREATE" ; GoSub WINDOW.CREATE End Case Case Control EQ "MY_EDITFIELD" Begin Case Case Event EQ "CHAR" ; GoSub MY_EDITFIELD.CHAR End Case Case Event EQ "CLICK" Begin Case Case Control EQ "OK_BUTTON" ; GoSub PUB_OK.CLICK Case Control EQ "CANCEL_BUTTON" ; GoSub PUB_CANCEL.CLICK End Case Case 1 // Event not handled End Case // Return 1 by default so the event chain continues If Assigned(EventFlow) else EventFlow = 1 Return EventFlow WINDOW.CREATE: CreateParam = Param1 return MY_EDITFIELD.CHAR: VirtCode = Param1 ScanCode = Param2 CtrlKey = Param3 ShiftKey = Param4 AltKey = Param5 return OK_BUTTON.CLICK: EventFlow = 0 End_Dialog(@Window, Data) return CANCEL_BUTTON.CLICK: Post_Event(@Window, "CLOSE") return
Let’s take a look at SRP PreCompiler’s improvement on this process by starting with the event handlers themselves. Each event handler has to work with generic parameters, in our case, Param1 through ParamN. As a practice, at SRP we tend to assign these parameters immediately to more meaningful variables to make our code more readable. With the SRP PreCompiler, we can rewrite our handler like so:
Event WINDOW.CREATE(CreateParam) End Event Event MY_EDITFIELD.CHAR(VirtCode, ScanCode, CtrlKey, ShiftKey, AltKey) End Event Event OK_BUTTON.CLICK() EventFlow = 0 End_Dialog(@Window, Data) End Event Event CANCEL_BUTTON.CLICK() Post_Event(@Window, "CLOSE") End Event
The SRP PreCompiler recognizes the Event keyword followed by a name and parameter list, which will get converted into a standard GoSub followed by assignments of the generic parameters to the ones named in the parameter list. Thus, Param1 will be assigned to CreateParam for the WINDOW.CREATE event.
There is an important caveat. First, you must use our naming convention for your generic parameters. They must be named Param1 through ParamN. You cannot use P1 though PN, or Parm1 through ParmN. Otherwise, your code will compile with VNAV errors. Sometimes, to gain efficiency, you have to adhere to guidelines. Hopefully, this isn’t an unreasonable demand.
So far, things are looking cleaner, and we’ve added one small convenience. There is, however, an even more important reason we use the Event keyword: automating the branching.
Let’s face it, the tedious thing about commuter modules is the requirement to update the logic that branches to your event handler. There are, of course, multiple ways of branching. In our example above we used Case statements, but many programmers also use On…Pos…GoSub to branch. Either way, you have to make sure you update the branching logic manually each time you create a new event handler. With the SRP PreCompiler, you’ll never have to do that again. We can replace our branching logic with this:
// Branch to event handler GoToEvent Event for CtrlEntId else // Event not handled end
The syntax is straight forward, but note that Event should contain only the name of the event. If your application passes other information in that variable, then add some code to extract only the event name. CtrlEntId is the fully qualified name of the control associated with the event, which means the window name (i.e., @Window) is included.
Invisibly, the SRP PreCompiler will build a list of all event handlers as identified by the Event keyword. It then replaces the GoToEvent statement with an On…Pos…GoSub, but you’ll never see it. You will never have to touch it again. Simply add a new Event, compile, and your branching logic will be up to date.
There is a caveat for this level of convenience. The GoToEvent statement will always assume the events are named in the format CONTROL.EVENT. Make sure you name your events accordingly. For window events (CREATE, CLOSE, READ, WRITE, etc.), prefix the event name with the word WINDOW, i.e., WINDOW.CREATE or WINDOW.WRITE.
Our original example can now be re-written like so:
Compile function MY_WINDOW_EVENTS(CtrlEntId, Event, Param1, Param2, Param3, Param4, Param5) #pragma precomp SRP_PreCompiler // Branch to event handler GoToEvent Event for CtrlEntId else // Event not handled end // Return 1 by default so the event chain continues Return EventFlow or 1 Event WINDOW.CREATE(CreateParam) End Event Event MY_EDITFIELD.CHAR(VirtCode, ScanCode, CtrlKey, ShiftKey, AltKey) End Event Event OK_BUTTON.CLICK() EventFlow = 0 End_Dialog(@Window, Data) End Event Event CANCEL_BUTTON.CLICK() Post_Event(@Window, "CLOSE") End Event
This represents a significant reduction in code and an increase in productivity. Of course, the SRP Editor will recognize the Enhanced BASIC+ and highlight it accordingly. In fact, it will even make you more productive with regards to events, but we’ll reveal those specifics later.
In the meantime, we hope you are excited for what we are doing. In the next article, we’ll show you how Enhanced BASIC+ can make it much easier to employ Service Oriented Architecture to your application logic.