SRP Editor – Auto-Generating Metadata
In our most recent service module metadata article, we documented how the metadata record is structured and how the Key ID should be named in the SYSENV table for proper functionality. We were pleased to see that some of you have already taken the initiative to generate your own metadata. In this article, we hope to make the task of generating service module metadata a whole lot easier by providing you with relatively quick and easy ways to comment your stored procedures in a way that asks the SRP Editor to do the work for you.
The SRP Editor’s post-compiler is designed to look for special metadata directives. Each directive instructs the post-compiler how the metadata record should be constructed. Directives are always enclosed in comments. In most cases it will not matter what kind of comment you use. Also, it does not matter where the comment(s) are located in the code. Thus, some developers might develop a habit of storing all of the directives at the top of the code, whereas others at the bottom. Alternatively, you might choose to distribute these directives in multiple locations. This is all a matter of preference.
There are only a few directives that you need to work with, and once you have become familiar with them we think you’ll discover that adding them to your own code will become an easy and automatic process. Let’s start with the first one.
This is the most critical directive. It does two things. First, it identifies your stored procedure as a service module and thus prepares the post-compiler to create a metadata record. This is used to populate attribute 1 of the metadata structure. The Key ID to the SYSENV table is automatically created based on the procedure name and the application this procedure belongs to. And, yes, for those who like to use the SRP Editor’s special ability to handle inherited stored procedures, you can rest assured that the post-compiler will correctly identify the parent application.
The second purpose for this directive is to identify the service signature (i.e., parameters) for this service module. This is used to populate attributes 2-5 of the metadata structure. However, in order to do this, the directive will need to include a little more information to properly create the signature metadata: actual parameter names and one or two special predefined placeholders. These predefined placeholders are documented in our metadata structures article. In this case, examples will explain far better than words:
// @@DEFINE_SERVICES_SIGNATURE(@QUOTED_SERVICE, @PARAMS)
The above directive format is probably one of the most common that will be used. It defines a simple signature wherein the first parameter identifies the location of the internal services (@QUOTED_SERVICE). The second parameter (@PARAMS) identifies where the remaining generic parameters start. This format would be suitable for SSPs like Set_Printer, Rev_Utility, and RTI_BRWSupport.
The Get_Property function does not have a standard service signature, so it needs to be modified to look like this:
// @@DEFINE_SERVICES_SIGNATURE(CtrlEntID, @QUOTED_SERVICE, AuxiliaryArg)
Likewise, the Repository function is also different and would need to look like this:
// @@DEFINE_SERVICES_SIGNATURE(@QUOTED_SERVICE, EntID, @PARAMS)
The above examples probably cover nearly every signature that you are likely to use. So, let’s move on to the next directive.
Each unique service within the stored procedure will need its own directive. This informs the post-compiler to add the name of the service to attribute 7 of the metadata structure. Additionally, each service will need to have its own parameter signature, which is stored in attribute 8 of the metadata structure. This allows the SRP Editor to properly adjust the call tip once the specific service is identified. Here are some examples that would be used for the aforementioned Repository function:
// @@DEFINE_SERVICE(DEFAULT, EntID) // @@DEFINE_SERVICE(ADDMODULE, EntID, ModuleName) // @@DEFINE_SERVICE(CLEARFLAG, EntID, Type, Tree)
Note that the names of each service are not enclosed in quotes. This is because the @QUOTED_SERVICES placeholder in the @@DEFINE_SERVICES_SIGNATURE directive makes this clear. In fact, no directive parameters should ever be surrounded in quotes.
As already mentioned, the developer may wish to include all directives in one area of the code for convenience. SRP has opted to place each specific @@DEFINE_SERVICE directive right above the relevant code (which is usually contained with a GoSub label and matching return statement). For example, we have a service module procedure called Form_Services. This routine includes various useful internal services that help manage common OpenInsight form taks. One of these services is CheckRequiredControls. For this service we created the following comment block:
//======================================================================== // CheckRequiredControls // // Param1 - Form whose required controls need to be checked. Default is // @Window // // @@DEFINE_SERVICE(CheckRequiredControls, Window) // // Checks to see if the current form has all required controls populated. // If not, then the appropriate message display is used. If the Apply // Special Colors flag from the Preferences Manager is set then this will // use the custom colors property and SRP Popup control. Otherwise a // message box will appear. This service replaces the Win_Check_Req_Fields // function, which is called by the Promoted_Write_Event function. //======================================================================== CheckRequiredControls: * Service logic goes here. return
@@DEFINE_UNQUOTED_OPTIONS and @@DEFINE_QUOTED_OPTIONS
Our last two metadata directives are optional, but they can be very useful in providing the programmer a list of predefined options for specific service parameters. This is a two-step process. First, one or more sets of options need to be defined with one the above directives, whichever is appropriate. Second, the relevant @@DEFINE_SERVICE directives need to be modified to use these options. Let’s begin with a simple example.
A rather common, and easy to create, set of options are Boolean flags. Our previous article included the complete metadata record for the Rev_Utility function. This structure included a set of BOOLEAN options, and these were linked to the SETREADONLY method (aka service). The directive used to create this set of options looks like this:
// @@DEFINE_UNQUOTED_OPTIONS BOOLEAN(True$, False$)
In this case we specified that the options are unquoted, This is because we also include (usually through an insert) the True$ and False$ equates. Therefore, we want these to be listed as unquoted so they will insert properly into our code as variables rather than literals.
However, if you need to provide a list of literals, then the option directive might look like this:
/* @@DEFINE_QUOTED_OPTIONS RESPONSEHEADERNAMES(Allow, Access-Control-Allow-Headers, Access-Control-Allow-Methods, Access-Control-Allow-Origin, Allow, Content-Encoding, Content-Language, Content-Length, Content-Location, Content-Disposition, Content-Type, Date, Expires, Last-Modified, Link, Location, Retry-After, Server, Set-Cookie, Transfer-Encoding, Vary, WWW-Authenticate) */
The above was taken from our HTTP_Services routine, a module that enables developers to consume HTTP requests and build HTTP responses. We included a list of common HTTP response headers for convenience but they are defined as quoted. Thus, when selected from the list, the result will be a quoted string. Oh, did you notice that the directive can be split across multiple lines? Quite handy when the list of options is relatively long. However, this only works when using the comment block notation (/*..*/).
We are now ready to move onto step 2: linking these option directives to @@DEFINE_SERVICE directive parameters. Simply append the name of the option to the parameter and separate with an equal sign. Now we can see how this was done for the SETREADONLY method:
// @@DEFINE_SERVICE(SETREADONLY, ControlList, ReadOnly=BOOLEAN)
This two-step process completes the generation of values for attributes 9-12 in the metadata structure.
The Wrap Up
There is a lot of documentation above and this might seem overwhelming to absorb. Have no fear. The metadata directives are relatively easy to understand once you have a good example to follow. So, with that in mind I have included a couple of complete sets of metadata directives. For convenience I am putting all of the directives in one comment block. The first is what the RTI_BRWSupport directives would have looked like:
/* @@DEFINE_SERVICES_SIGNATURE(@QUOTED_SERVICE, @PARAMS) @@DEFINE_UNQUOTED_OPTIONS BOOLEAN(True$, False$) @@DEFINE_SERVICE(DEFAULT, reportGroupName) @@DEFINE_SERVICE(DELETE, reportGroupName) @@DEFINE_SERVICE(DISPLAY, reportGroupName, reportName, overrideListID, rptDetails) @@DEFINE_SERVICE(KILL) @@DEFINE_SERVICE(LAUNCH) @@DEFINE_SERVICE(LIST, reportGroupName) @@DEFINE_SERVICE(READ, reportGroupName, bLockFlag=BOOLEAN) @@DEFINE_SERVICE(REPORTS) @@DEFINE_SERVICE(WRITE, reportGroupName, reportGroupDefinition, defaultReport) */
The second example is based on the Rev_Utility function documentation:
/* @@DEFINE_SERVICES_SIGNATURE(@QUOTED_SERVICE, @PARAMS) @@DEFINE_UNQUOTED_OPTIONS BOOLEAN(True$, False$) @@DEFINE_UNQUOTED_OPTIONS WINDOWS(@Window) @@DEFINE_QUOTED_OPTIONS FSCODES(FS10, FS11, FS12, FS13, FS14, FS15, FS16, FS17, FS18, FS30, FS50, FS51, FS52, FS53, FS54, FS55, FS56, FS57, FS100, FS101, FS102, FS103, FS104, FS105, FS106, FS107, FS109, FS110, FS111, FS112, FS113, FS114, FS115, FS116, FS124, FS125, FS126, FS127, FS128, FS129, FS130, FS133, FS135, FS140, FS141, FS142, FS143, FS144, FS145, FS146, FS147, FS148, FS149, FS150, FS151, FS152, FS160, FS161, FS162, FS171, FS172, FS173, FS174, FS175, FS176, FS185, FS186, FS187, FS188, FS189, FS190, FS191, FS192, FS193, FS194, FS195, FS196, FS200, FS201, FS202, FS210, FS211, FS212, FS213, FS214, FS215, FS216, FS217, FS231, FS250, FS251, FS252, FS253, FS254, FS255, FS256, FS257, FS258, FS259, FS260, FS261, FS262, FS263, FS264, FS265, FS266, FS267, FS270, FS271, FS272, FS273, FS274, FS275, FS276, FS277, FS278, FS279, FS280, FS281, FS290, FS291, FS292, FS293, FS294, FS300, FS301, FS302, FS303, FS304, FS305, FS306, FS307, FS308, FS309, FS310, FS311, FS312, FS313, FS314, FS315, FS316, FS330, FS350, FS351, FS352, FS353, FS354, FS355, FS360, FS361, FS362, FS363, FS364, FS365, FS400, FS401, FS402, FS403, FS404, FS405, FS410, FS411, FS412, FS413, FS414, FS415, FS420, FS421, FS430, FS431, FS432, FS434, FS435, FS436, FS437, FS438, FS439, FS440, FS441, FS442, FS443, FS444, FS450, FS451, FS452, FS453, FS454, FS455, FS456, FS457, FS458, FS459, FS460, FS461, FS462, FS463, FS464, FS465, FS466, FS467, FS468, FS469, FS470, FS471, FS472, FS473, FS474, FS477, FS478, FS479, FS480, FS481, FS482, FS483, FS484, FS485, FS486, FS487, FS488, FS489, FS490, FS491, FS492, FS493, FS494, FS495, FS496, FS497, FS499, FS1000, FS1001, FS1002, FS1003, FS1004, FS1005, FS1006, FS1007, FS1008, FS1009, FS1010, FS1011, FS1012, FS1013, FS1014, FS1015, FS1016, FS1017, FS1018, FS1019, FS1020, FS1021, FS1022, FS1023, FS1024, FS1025, FS1026, FS1027, FS1028, FS1029, FS1030, FS1100, FS1101, FS1102, FS1103, FS1104, FS1105, FS1106, FS1107, FS1108, FS1109) @@DEFINE_SERVICE(BORDERWIDTHS, WindowName=WINDOWS) @@DEFINE_SERVICE(ERRORTEXT, ErrorCode=FSCODES) @@DEFINE_SERVICE(FSERROR) @@DEFINE_SERVICE(GETSYSTEMFONTS) @@DEFINE_SERVICE(SETREADONLY, ControlList, ReadOnly=BOOLEAN) @@DEFINE_SERVICE(SETWINDOWSIZE, WindowName=WINDOWS, SizeArray) @@DEFINE_SERVICE(SYSTEMSIZE) @@DEFINE_SERVICE(TOKENIZE, String) @@DEFINE_SERVICE(VERIFYPROC, StoredProcName) */
Once the required metadata directives are in place, it is a simple matter to add new directives as the list of service options grows. Just compile your code and the SRP Editor takes care of the rest!