Preliminary Notes About the Generic Commandr Application By M. Asherman, 1/13/2000 Intro: The Commander (aka Commandr) application is a generic table-driven facility for defining functions. It supports optional user-defined arguments and return values, nested and recursive functions. There is a convenient wrapper function interface, commandr(), plus a pure object-oriented class, Commander, in commandr.vcx. For a quick look at some examples, type the following command from VFP command level: ? commandr() This will automatically initialize the default meta-file and evaluate the default sample function, which just displays a WAIT WINDOW message. Creation and maintenance of the Commander meta-file can be done via VFP's BROWSE command, and this is made easier by taking advantage of VFP's Browse preferences, e.g. via the BROWSE LAST option. Of course you can also populate the Commander meta-file programmatically, which is a major benefit of the meta-file based design. See the Saver application for an example of totally programmatic meta-file creation and use of the Commander facility. To clean up after testing commandr() without closing out the current VFP session, use the this command: _screen.removeobject('commander') This will release the default Commander object and close the meta-file. This is mainly useful during development and testing of the Commandr facility itself, to avoid "class is in use" errors. Currently available documentation: - annotated examples are in the sample meta-file, commandf.dbf (see the Notes field) - run commandr() to see the first sample table-driven function - brief descriptions have been attached to all files in commandr.pjx - there are DBC-based field comments for all meta-file fields (in commandf.dbf) - all programs are liberally commented - see additional usage notes in comments in commandr.prg and method Commander.DoFunc - see the commandr.h include file for error codes and hard-wired constants - note that certain generic mdacomm functions are employed in the sample meta-file - also see cmdrclon.prg for handy utility to clone entire function definitions Related outstanding issues and tasks (* indicates it can wait): - provide an error-trapping mechanism that can work with command-line representation Use the Commander.Error event method to catch errors during the &cmd expansion in Commander.DoFunc logic, and allow these errors to fail cleanly with a non-zero Commander.ErrorCode. Use the new generic ErrHandler class to support this, along the same lines as changes made to Querier. - use VFP's newobject() function and method where appropriate To avoid annoying hassles with older SET CLASSLIB + add/createobject() approach. - introduce a PRIVATE mvar for convenient reference to the Commander object itself So that a table-driven function can invoke a nested function in the same meta-file, using the same Commander.DoFunc() directly (as opposed to the commandr() UDF interface). - Commander.AddFuncStep should clear the Notes field when re-using a deleted record *- provide additional user/developer documentation of the Commandr facility *- support a more flexible way to assign default alias for the meta-file Currently this is a macro-defined constant, CMDR_ALIAS, defined in the commandr.h include file. A more flexible way to define this would be via a property in the Commander class, which would be initialized by that macro, but should be settable to an alternate value. This would avoid conflicts when multiple Commander objects coexist within a common data session. *- allow for functional expression representation, with success flag I.e. instead of expressing the step as a command line, let it be a functional expression that returns a success flag for the individual step, which would be tested by the Commander.DoFunc loop logic. This should be more efficient than the present reliance on ¯o expanded command lines. However, for most purposes the performance of the present implementation should be entirely satisfactory. *- a "goto" mechanism would be handy (but not essential) This would be especially useful for looping and block coding constructs, which are otherwise awkward to express in individual command lines. The mechanism could be provided by introducing a new PRIVATE mvar, e.g. m.cmdr_next, whose assignment would tell Commander.DoFunc to jump directly to the step with the specified sequence #, for example. *- maybe it would be useful to have a parent-level of "procedure" record But this doesn't seem necessary, except as an annotation and organizational aid. The core Commander class logic should not depend on the existence of this parent meta-file. *- form-based meta-file maintenance facility Should not be initially needed, because Browse + handy UDFs is adequate for basic meta-file manipulations. *- conditional return expression syntax: *[expr1]*expr2 Evaluates expr1 and returns eval(expr2) if eval(expr1) = .T. This can be used to construct a concise CASE statement construct as a subfunction, for example. *- verify current &-expansion limit by careful tests and #define a macro for this Preliminary tests indicate this restriction may not be nearly as severe under VFP SP3 as I recall it to have been in the past. There may be many specific types of limits in certain contexts, but evidently not in &-macro expansion itself. *- adjust logic in Commander.DoFunc to supprt cmdprg() instead of macro expansion I.e. automatically use this approach when macro limit would otherwise be exceeded, causing an error. There's no point in messing with this unless clear instances of exceeding the limit show up again. Wait for the problem to resurface. General conventions: @- maintain consistent usage notes between wrapper function and corresponding method For this app, that means commandr.prg and corresponding Commander.DoFunc. @- maintain clean copies of selected files in shared mdacomm directory For Commander, this includes commandr.app/h/txt/vcx/vct + cmdrtmpl.DBF/FPT/CDX + cmdrclon.prg/fxp. @- rebuild dependent apps after changes to shared Commander stuff @- maintain thorough error and escape-handling provisions @- all apps should avoid potential name conflicts in case moved into mdacomm E.g. avoid file names like "notes.txt" in favor of ".txt". @- avoid using the same prefix for macro name and anything else (e.g. global mvars) Remember that VFP is case-insensitive with respect to names, so take care not to accidentally use a macro name unintentionally by sticking to a unique prefix. Unfortunately, I didn't think of this issue when I first coded the Commander application, and my choice of global mvar naming conventions violates this principle. Specifically, the macros use "CMDR_" prefix and global mvars use "cmdr_" prefix, which risks confusion. However none of the current macro names actually conflict with global mvar names, so this is not a serious problem. Change history: 990529 added property Commander.quietnofunc to suppress error msg. on functionid not found 990529 new method Commander.DeleteFunc(funcid) for deleting records 990529 new method Commander.AddFuncStep(funcid, seq, cmdline) for appending records 990529 adjust field comments on commandf.modidate: this is no longer optional 990608 revised macro CMDR_CLIB in commandr.h to avoid inclusion of "ADDITIVE" here 990608 support auto-creation of meta-file from bound template 990610 added test case demo08 to demonstrate proper cleanup for default demo mode 990610 don't assume the Commander meta-file necessarily has a "DBF" extension 990611 new property, metafilepath, for holding on to fully-qualified meta-file path/name 990629 extension to support direct string assigments without macros or eval() Introduce a special syntax for an alternative way of expressing a processing step that reduces to a string assignment statement, instead of the usual (more general) macro-expanded command line interpretation. In this new syntax, distinguished by a reserved prefix, there would be an initial portion giving a variable name expression, followed by a reserved delimiter, followed by the character string to assign to the preceding variable. There would be no surrounding quotes for this type of assignment; just take the rest of the memo field as the value to assign. This allows for a simple, efficient way of handling arbitrary strings values, which could be very long or contain special characters that would be difficult to handle otherwise. 990629 create example, demo09, illustrating new string assignment flavor of cmdline 990629 adjust field notes for cmdline to mention special string assignment syntax 990629 review/adjust Commander-based apps to take advantage of new string assigment syntax The special string assignment syntax, *=(name expression)=*string value..., is the preferred way to handle literal string assignments. This is both more efficient and more flexible than the usual &-macro expansion technique that Commander uses otherwise. The new syntax can support much longer strings, and there are no restrictions on use of special characters. 990629 test out usage in a private data session As far as I know, this will work, but it's not yet tested. We may as well wait until a suitable application comes up for this feature, which should happen shortly in the planned implementation of a generic state saving and restoration facility. 990719 create new cmdrclon.prg - stand-alone utility for cloning function definitions 991021 make adjustments for move from M: to D: This entails the following steps: - adjust icon for launching VFP to start in dir on D:... and use -cd:... option - config.fpw should be ok as-is - we already eliminated hard-wired drive/path - open .pjx and tell dialog OK to move it from M: to D: - confirm that all files in project have been relocated to D:, as expected 991021 introduce a separate method, cmdrm_open, to perform opening of meta-file Use this function in Init and other methods that require accessing the meta-file to support automatic re-opening of meta-file if unexpectedly closed. 991021 add new property, cmdrp_funcidlen, containing the functionid length Since we can't assume the Commander meta-file is directly accessible outside of Commander (caller may be in another data session). Initialize this in method cmdrm_open. 991022 **** UPLOADED CHANGES TO BT SERVER'S D:\REDP\APPL\COMMANDR\ & ...MDACOMM\ **** 991116 add new method Commander.cmdrm_seekfunc, which does a lookup for a given functionid This is helpful for support of Saver's Delete button enablement. 991130 extend cmdrclon.prg to support a separate target meta-file Also entailed changes to generic clonerec.prg, and I threw in a some cleanups to posrest.prg. 991203 **** UPLOADED CHANGES TO BT SERVER'S D:\REDP\APPL\COMMANDR\ & ...MDACOMM\ **** 000111 create new stand-alone UDF, cmdprg.prg, to generate/compile/run/cleanup a PRG This should be able to serve as a replacement for macro-expansion of command lines, for both greater efficiency and to avoid restrictive macro length limits. Let cmdprg.prg/fxp reside in mdacomm only, since this really is a generic stand-alone utility, not a piece of commander (although it's related).