Enhancing BASIC+ Part 4: Services with a Smile
In this series of articles, we’ve been exploring how a precompiler can be used to enhance the BASIC+ language. Last week we took things to a new level with making commuter modules easier to develop and maintain. In this article, we show how SRP PreCompiler is making services a first class citizen, and hopefully a normal part of your development technique.
Services
In BASIC+, a service is a stored procedure that can do more than one thing. This is accomplished via a parameter that indicates what functionality, or service, you want done. Services have been around for a long time, as evidenced by built-in routines such as Utility or Repository. Services reduce clutter in your repository and make it easier to find the functionality you need. This convenience is significantly increased for SRP Editor users since the tool uses service metadata to provide useful prompts and parameter information.
For today’s article, let’s modify the following service using the SRP PreCompiler. Note how the service uses metadata in the comments:
Compile function INVOICE_SERVICES(Service, Param1, Param2, Param3, Param4, Param5) // @@DEFINE_SERVICES_SIGNATURE(@QUOTED_SERVICE, @PARAMS) // Branch to the service Begin Case Case Service _EQC "GetCustomerAddress" ; GoSub GetCustomerAddress Case Service _EQC "AddSalesTax" ; GoSub AddSalesTax Case 1 // Service not handled End Case // Return "" by default If Assigned(Response) else Response = "" Return Response // @@DEFINE_QUOTED_OPTIONS ADDRESSTYPES(Mailing, Shipping, Plant) // @@DEFINE_SERVICE(GetCustomerAddress, AddressType=ADDRESSTYPES) GetCustomerAddress: AddressType = Param1 Begin Case Case AddressType _EQC "Mailing" ; Response = CustRec< MAILING_ADDRESS$ > Case AddressType _EQC "Shipping" ; Response = CustRec< SHIPPING_ADDRESS$ > Case AddressType _EQC "Plant" ; Response = CustRec< PLANT_ADDRESS$ > End Case return // @@DEFINE_SERVICE(AddSalesTax, Amount) AddSalesTax: Amount = Param1 Amount += (Amount * CustRec<TAX_RATEgt;)
Param1 = Amount
returnDefining a Service
The Enhanced BASIC+ syntax for a service is similar to that of an event, only we use the Service keyword:
Service GetCustomerAddress(AddressType) Begin Case Case AddressType _EQC "Mailing" ; Response = CustRec< MAILING_ADDRESS$ > Case AddressType _EQC "Shipping" ; Response = CustRec< SHIPPING_ADDRESS$ > Case AddressType _EQC "Plant" ; Response = CustRec< PLANT_ADDRESS$ > End Case End Service Service AddSalesTax(ref Amount) Amount += (Amount * CustRec< TAX_RATE$ >) End ServiceAs it did with events, the SRP PreCompiler recognizes the Service keyword followed by a name and parameter list, which will get converted into a standard GoSub followed by assignments of the generic parameters (Param1 – ParamN) to the ones named in the parameter list. However, there is one additional feature: the Ref keyword.
Normally, service parameters (like event parameters) are passed by value, not by reference. This makes sense because behind the scenes, Param1 gets copied into the first named variable, such as AddressType. Since the service only has a copy, any changes made to it will not pass back up to the caller. If you need a parameter’s changes to persist back to the caller, then precede it with the Ref keyword as seen in the AddSalesTax service above. Hopefully you noticed that our code no longer copies the Param1 variable to the Amount variable and then back. The Ref keyword does the work for you! You can now be assured that the referenced parameter is copied back to its original Param when the service is done.
Not only have we improved the readability of our code, but we’ve made it easy for the SRP PreCompiler to produce metadata for the service too, though it’s only partially complete.
Service Metadata
In our original example we used a kind of service metadata called options. Options tell the SRP Editor to show a dropdown list of options for a parameter. This was accomplished using the @@DEFINE_QUOTED_OPTIONS directive, but now we can do it with a bona fide Options keyword:
Options ADDRESSTYPES = "Mailing", "Shipping", "Plant" Service GetCustomerAddress(AddressType=ADDRESSTYPES) Begin Case Case AddressType _EQC "Mailing" ; Response = CustRec< MAILING_ADDRESS$ > Case AddressType _EQC "Shipping" ; Response = CustRec< SHIPPING_ADDRESS$ > Case AddressType _EQC "Plant" ; Response = CustRec< PLANT_ADDRESS$ > End Case End ServiceThe Options keyword can appear anywhere in your code, and its sole purpose is to let you add more metadata to your service. In our example, the developer has three options for the AddressType parameter. So first we defined the list of options and gave it a name: ADDRESSTYPES. Then we assigned it to our service’s AddressType parameter using the equal operator. Now when the user presses a quote character, the list of address types will appear.
Jumping to the Service
To wrap it up, we need to update our service module to jump to the service being requested without having to manually keep a case statement up to date:
GoToService else // Service not handled endAnd if you don’t want the else keyword, you can just do this:
GoToServiceA single keyword? Yes indeed. However, in order for the correct service to be called, there must be a parameter at the top of your code dedicated to passing in the service name. The SRP PreCompiler provides a keyword for that:
Compile function INVOICE_SERVICES(@SERVICE, Param1, Param2, Param3, Param4, Param5)Now the SRP PreCompiler knows which parameter will have the service name and can use it to branch to the appropriate service gosub.
As a bonus, you can forego defining the generic parameters and do this:
Compile function INVOICE_SERVICES(@SERVICE, @PARAMS)The SRP PreCompiler will replace @PARAMS with Param1 – ParamN, with N being the exact number of parameters needed to cover all your services.
Final Result
Our original example can now be re-written like so:
Compile function INVOICE_SERVICES(@SERVICE, @PARAMS) // Branch to the service GoToService else // Service not handled end // Return "" by default Return Response or "" Options ADDRESSTYPES = "Mailing", "Shipping", "Plant" Service GetCustomerAddress(AddressType=ADDRESSTYPES) Begin Case Case AddressType _EQC "Mailing" ; Response = CustRec< MAILING_ADDRESS$ > Case AddressType _EQC "Shipping" ; Response = CustRec< SHIPPING_ADDRESS$ > Case AddressType _EQC "Plant" ; Response = CustRec< PLANT_ADDRESS$ > End Case End Service Service AddSalesTax(ref Amount) Amount += (Amount * CustRec< TAX_RATE$ >) End ServiceNot only did we simplify the process of making a service, we simplified the process of producing metadata with it. It’s important to emphasize that the SRP PreCompiler works outside of the SRP Editor, but only the SRP Editor will take advantage of metadata.
Hopefully this gets you excited about using services in your own applications. Next week we’ll show one more cool feature of the SRP Editor. It’s not more Enhanced BASIC+ syntax, but it’s a feature made possible by Enhanced BASIC+. Stay tuned.
Leave a Reply