« February 2008 | Main | September 2008 »

August 30, 2008

Employ "white space" in your code

Make your code more readable (by not jamming it all together) and, therefore, easier to maintain.

Examples:

  a) Code examples:

    Without white space, the code is not as readable:
      REPLACE CB_UnitsBilled WITH ;
        (lnBillableDaysTotal/DAY(GOMONTH(C_ClaimHeader.CH_From,1) ;
         -1))*CB_UnitsBilled

    With white space, the code is more readable:
      REPLACE CB_UnitsBilled;
        WITH ;
          (lnBillableDaysTotal / DAY(GOMONTH(C_ClaimHeader.CH_From, 1) ;
           - 1)) * CB_UnitsBilled

    Of course, with good comments, the command makes the most sense:
      * Units Billed = (Total Billable Days / Total # Days in month) x
      *                Authorized Units
      REPLACE CB_UnitsBilled WITH (m.lnBillableDaysTotal /    ;
         DAY(GOMONTH(C_ClaimHeader.CH_From, 1) - 1)) * CB_UnitsBilled

  b) Inserting a space after each semicolon (";") makes your PATH setting in configuration files and SET PATH commands in VFP code more readable:

    Without white space, the path is not as readable:
PATH = C:\App;C:\App\Data;C:\Program Files\Microsoft Visual FoxPro 9;C:\Stonefield\SfCommon;C:\Stonefield\SDT;C:\Stonefield\SDT\Source;C:\Stonefield\SDT\DBCX

    With white space, the path is more readable:
PATH = C:\App; C:\App\Data; C:\Program Files\Microsoft Visual FoxPro 9; C:\Stonefield\SfCommon; C:\Stonefield\SDT; C:\Stonefield\SDT\Source; C:\Stonefield\SDT\DBCX

The above examples demonstrate intraline (horizontal) white space (making it easier to read your code from left to right); also employ interline (vertical) white space (to make your code easier to read from the top down).

Posted by abergquist on August 30, 2008 | Permalink | Comments (1) | TrackBack

August 26, 2008

Don't Think "Tomorrow"; Think "A Decade from Now"

The always entertaining and thought provoking, Rick Schummer, had a blog recently, ( Rick's Blog - “Getting Started with Visual FoxPro") that caused me to stop and reflect upon his heartfelt advice. What I came up with is not particularly pretty for me, but it was a moment of truth for me.


Before I go any further, let me say I have been using FoxPro in one version or another since 1989. I have made a good living thanks to that fine product. My custom license plates read “FOXPRO”. Get the point?  I L-U-V the software! I will continue to use and work with the product until the day I retire. Fortunately, for me, that day is not that far away. Timing is everything. For me, the timing is that I should be able to run out my career and the last line of code I write will be... RETURN. However there are others who have 25, 30, 40 years left in the work force and my advice to them is... DON’T WASTE YOUR TIME WITH FOXPRO.


I can hear many of you shouting, “Blasphemer!”. Maybe I should even be shouting at myself, but the truth of the matter is, FoxPro will not be viable software in a few (plug in your own definition of “few”) years. Rick’s advice for anyone wanting to learn FoxPro was dead on. He did an excellent job of covering all of the bases. My question is WHY would anyone want to learn FoxPro at this point in history? The only two reasons I could come up with are because the dwindling ranks of FoxPro developers have caused companies, still using legacy FoxPro application, to get someone, anyone, to maintain and enhance their applications OR the individual is wanting to learn VFP because it is still an awesome hunk of software that is VERY cost effective; particularly in third world countries.

In the first case, the boss probably went to an employee and said, “We need to have you learn FoxPro.”  The apparent up front cost of converting a legacy application is, in many cases, too much for a small to medium business to bear in these somewhat difficult economic times. The application, basically, still does what they need it to do, but once in a while, it just needs some tweaking.  The “quick fix”, in management’s mind, is to invest a minimum amount and “throw another body” at the problem. This only delays the final decision to rewrite or replace the existing legacy application. Ignoring the thought that if the employee refuses to “waste” their time learning “old technology” they might be fired, any developer worth their weight in salt would prefer to learn current technologies, thus making themselves more valuable both to their present company and when they re-enter the job market. Learning FoxPro serves his company well (momentarily), but it does not serve the developer’s best interests. Soon, that developer will realize his stagnant position and move on causing the problem to arise again and the same solution to be more expensive than it was earlier. See a pattern here?


In scenario number two, each desire to learn VFP has many reasons. I have no problem with anyone weighing their options and deciding that Visual FoxPro is the answer to their solution.  Many times, it actually IS the best solution; even now. However, it would seem to me that it would have to be a very special set of circumstances to arrive at that conclusion. In that case, once again, Rick’s advice is the way to go.


Returning to scenario number one... What would Rick’s answer have been had the question been, “How do I get started learning COBOL? My guess is that Rick would have asked WHY they wanted to learn COBOL. Now is the time to convert or rewrite legacy applications. It will never be less expensive; aren’t labor costs always going to increase? It will never be easier; the project will only grow in scope as the legacy app grows. At the very least, a far-seeing IT manager should realize what is inevitable and begin to make plans to move forward. In the meantime, and this will sound like an advertisement, the most cost effective solution in the long run is to hire the right people who already have the experience and knowledge necessary to keep the application afloat while your company prepares to move forward and stay ahead of the competition. The people you hire to maintain the legacy app could even be involved in the conversion.


Comments?  I would love to hear them; I have thick skin.

Posted by Dave Aring on August 26, 2008 | Permalink | Comments (4) | TrackBack

August 25, 2008

When a "Watch Window" expression goes in and out of scope

It used to be extremely frustrating to me when a "Watch Window" expression went in and out of scope.  Now, I simply employ a technique I learned in Nancy Folsom's short but excellent book Debugging Visual FoxPro Applications (http://www.hentzenwerke.com/catalog/debugvfp.htm).

On p. 53, Nancy indicates that:

  "If you know the value of the expression you're looking for, you can create a 'Break when expression is true' breakpoint and enter a conditional IIF() in the Expression field.  The expression would look like the following:

  IIF(TYPE('SomeExpression') = 'C', SomeExpression = SomeValue, .F.)

  "This expression will cause the program to be suspended only when the value of the expression is what you expect."

Here's an example of an expression:

  IIF(VARTYPE(RECCOUNT('lv_WDates')) = 'N', RECCOUNT('lv_WDates') = 6, .F.)

Michael Cummings of the Los Angeles FoxPro Users Group (LA Fox) suggested an even better technique [viz., test for VARTYPE(…) = 'U']; here's a translation of the above:

  IIF(VARTYPE(RECCOUNT('lv_WDates')) = 'U', .F., RECCOUNT('lv_WDates') = 6)

Testing for an 'U'ndefined data type obviates having to get the variable's data type "right" in the breakpoint expression.

Patrick O'Hara of the Chicago FoxPro User/Developer Group (CFUDG) reminded me that [Ctrl + B] brings up the Breakpoints dialog box.

Posted by abergquist on August 25, 2008 | Permalink | Comments (2) | TrackBack

August 21, 2008

Debugging errors that occur only in production code

There are a few techniques to debug errors that occur only in production:

1. Insert the following line:

MESSAGEBOX('<n>. ' + PROGRAM() + ' ...')

at "strategic" points in the program (e.g., before calling a function, method, procedure, etc.).  I start "n" at 1 and increment by 1 in subsequent MESSAGEBOX() calls.  The last number that displays in a MESSAGEBOX() before an error occurs helps me narrow down exactly where the problem is occurring.  If necessary, I delete the original MESSAGEBOX() calls and then insert new MESSAGEBOX() calls "closer" to where the error is occurring.  (Repeat as necessary [and, yes, you can -- and should -- try this at home <g>].)

2. Another technique is to write text to a log file (.Log, .Txt, etc.) at various points of/during program execution.  (This technique, by the way, is also useful for debugging COM objects since you cannot step through code in the VFP debugger nor can you [or are supposed to] send output to the screen from a COM object.)

3. In VFP 9, you can do coverage logging not only at design time but also in .EXEs (via the SET COVERAGE command).  Your client will need to send you the coverage log file  (generated by the SET COVERAGE command) which you can then run through the Coverage Profiler yourself.  Put code like the following toward the top of your main calling routine:

ON ERROR SET COVERAGE TO
SET COVERAGE TO <filename of your choice>.Txt

Posted by abergquist on August 21, 2008 | Permalink | Comments (6) | TrackBack

August 16, 2008

Make use of methods for complex Dynamic... grid settings

In a grid's Init() event method:

  *--------------------------------------------------------------------------
  * Set the .DynamicBackColor property for this column.
  *--------------------------------------------------------------------------
  This.Column1.DynamicBackColor = [ThisForm.DynamicBackColorForThisColumn()]

In the form's custom DynamicBackColorForThisColumn() method:

  *-----------------------------------------------------------------------------------
  *  Method: DynamicBackColorForThisColumn()
  * Purpose: Handles the initialization of DynamicBackColor for the grid's Column
  *
  *   Notes:  It is *far* easier to put the logic into this method (and allows
  *     us to document it thoroughly) rather than trying to "stuff" text
  *   into the grid's .Column1.DynamicBackColor property.
  *
  * Plan Codes:
  *  01 - Bill full cost
  *  07 - Zero AtP (do not send bill)
  *  08 - Zero AtP (do send bill)
  *-----------------------------------------------------------------------------------

  LOCAL lcColor
  DO CASE
    CASE AtP_PlanCd = '01'
      lcColor = 'RGB(255, 255, 255)'

    CASE AtP_PlanCd = '07'
      lcColor = 'RGB(0, 0, 0)'

    CASE AtP_PlanCd = '08'
      lcColor = 'RGB(192, 192, 192)'

    OTHERWISE
      lcColor = '<some default color>'
  ENDCASE

  RETURN lcColor

The above could have been coded in the grid's Init() event method as follows:

  *-----------------------------------------------------
  * Set the .DynamicBackColor property for this column.
  *-----------------------------------------------------
  This.Column1.DynamicBackColor = [IIF(AtP_PlanCd = '01', 'RGB(255, 255, 255)', ;
    IIF(AtP_PlanCd = '07', 'RGB(0, 0, 0)', IIF(AtP_PlanCd = '08', ;
  'RGB(192, 192, 192)', '<some default color>')))]

Which implementation do you think is easier to read, debug and maintain?

Even with VFP 9's new ICASE() function, it's still challenging to read, debug and maintain:

  This.Column1.DynamicBackColor = [ICASE(AtP_PlanCd = '01', 'RGB(255, 255, 255)', ;
    AtP_PlanCd = '07', 'RGB(0, 0, 0)', AtP_PlanCd = '08', 'RGB(192, 192, 192)', ;
    '<some default color>')]

(By the way, the form-level method could alternatively be implemented as a [class-level] method of a grid class.)

Posted by abergquist on August 16, 2008 | Permalink | Comments (0) | TrackBack

August 12, 2008

Employ SQL-SELECTs "TO SCREEN" clause to determine if a record exists

To determine if a record exists in a table, I used to send the results of a SQL-SELECT to an array, like so:

  LOCAL ARRAY la[FieldName][1]
  _Tally = 0
  SELECT [FieldName]     ;
    FROM [Database]![TableName]    ;
    WHERE [FieldName] = [Memory Variable]  ;
    INTO ARRAY la[FieldName]
  IF _Tally = 0
    MESSAGEBOX(TRANSFORM([Memory Variable]) + ' is not a valid [Whatever] ',
              0, '[MESSAGEBOX() Caption]')
  ENDIF

Sending the results of the SQL-SELECT TO SCREEN NOCONSOLE  obviates the need to declare an array prior to doing the SQL-SELECT:

  _Tally = 0
  SELECT [FieldName]     ;
    FROM [Database]![TableName]    ;
    WHERE [FieldName] = [Memory Variable]  ;
    TO SCREEN NOCONSOLE
  IF _Tally = 0
    MESSAGEBOX(TRANSFORM([Memory Variable]) + ' is not a valid [Whatever] ',
              0, '[MESSAGEBOX() Caption]')
  ENDIF

In different versions of VFP up to and including VFP 8, _Tally was not always reliably accurate (cf. http://foxproadvisor.com/doc/12139 [Cathy Pountney's "_TALLY trap tip]); if that is your experience, simply send the output to a CURSOR and then check the RECCOUNT() of the cursor.

Enjoy!

Art Bergquist

Posted by abergquist on August 12, 2008 | Permalink | Comments (1) | TrackBack

August 09, 2008

USE IN SELECT() to close a work area

In lieu of:

  IF USED(<Work Area>)
    USE IN '<Work Area>'
  ENDIF

and:

  USE IN IIF(USED(<Work Area>) …

employ code like the following:

  USE IN SELECT('<Work Area>')

Like the first example above, this one will work regardless of whether the work area is in use or not (the second example will bomb if the specified work area is not in use).

Yours for more concise (yet still readable) code,

Art Bergquist

Posted by abergquist on August 9, 2008 | Permalink | Comments (0) | TrackBack

August 06, 2008

Copy- (or Cut)-and-paste as much as possible; avoid re-typing code!

For example:

LOCAL  lnTotalInvoice && Type only one (1) of the instances of 'lnTotalInvoice'
            lnTotalInvoice = … && and then copy-and-paste it for all other instances

This helps prevent inadvertent typos (are there any other kind?!) from creeping into your code.

It is also helpful when you communicate with other developers (e.g., via e-mail when posting to an on-line news group or message board) so that you don’t accidentally modify the code in question (again introducing that inadvertent typo <g>).

Doug Carpenter reminded me of a trick when working with memory variables that have been declared LOCAL and PUBLIC (but not PRIVATE):
typing ZLOC followed by the <Spacebar> in a .Prg file/method causes VFP to display an IntelliSense list of available LOCAL and PUBLIC memvars and parameters.

Here is an example of the list that displays after you press the <Spacebar>:
    lcString
    llCustom
    llError
    llSuccess
    loException

Posted by abergquist on August 6, 2008 | Permalink | Comments (1) | TrackBack

August 01, 2008

Best practices with Visual FoxPro

With respect to dBASE-like languages, I started developing with dBASE IV and then switched over to FoxPro for DOS in 1992; since then, I have developed with/in every single version of FoxPro and Visual FoxPro and am currently developing in VFP 9. For the next couple of weeks, I'd like to share some of my best practices with Visual FoxPro.  You might not consider some tips to be earth-shattering but it's my hope that  you will find a couple nuggets/gems that will facilitate and expedite your particular development (environment).

So here goes my first tip: Default options to facilitate VFP development

1. Via the View tab/page of the 'Tools | Options' dialog, maximize lists for less scrolling and/or typing:
  • Set the Most Recently Used list to contain the maximum of 24 items
    (The Most Recently Used list controls the number of files displayed in the most recently used [MRU] list.)
  • Set the List display count to contain the maximum of 30 items
    (The List display count specifies the maximum number of items to initially display in IntelliSense drop-down list boxes.) In VFP 8, you will find the “List display count” spinner control in the “Editor Options” section at the top of the “Tools | Options | Editor” tab.
2. Via the Editor tab/page of the 'Tools | Options' dialog, you can more quickly detect incorrectly delimited strings):
  • At the very least, set the Background color of 'Strings' to yellow (like a highlighter).
    I also like to set the Foregound color of 'Comments' to green (to distinguish comments from code).
  • In the same vein, I like to maximize windows to see as much code as possible at one time and, as a result, to not have to scroll as often.
Enjoy!
Art Bergquist

Posted by abergquist on August 1, 2008 | Permalink | Comments (3) | TrackBack