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:

  1. 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.
  2. 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.
  3. 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

Event Handling

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.

GoSub 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.

Final Result

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.

 

Leave a Reply