Tips_tutorials   >   Studio103   >   Error Handling Code Guidelines

Error Handling Code Guidelines

Using an error handler affects how you write your code. This section gives you some guidelines to follow for writing code when using an error handler.

Single Exit Point

Adding the $promptonceLastError code to the visual class methods was relatively easy because the methods were written with a single exit point at the end of the method.

For the first few years of writing code in Omnis Studio I wrote methods with multiple exit points. Steve McConnell in his excellent book, Code Complete, pointed out that code with multiple exit points is difficult to read and statistically has a greater number of errors. I switched to writing methods which have a single exit point at the end of the method and have to agree with Steve's findings.

Here is a sample of how I used to write code with multiple exits points.

; Check the incoming parameter
If pReportList.$linecount=0
   Calculate Mssg as "The report list is empty, unable to print the report."
   Do $ctask.errhndler.$logError($cmethod,Mssg)
   Quit method kFalse
End If

; Check the records in the list.
For pReportList.$line from 1 to pReportList.$linecount step 1
   
   Do method checkData (pReportList) Returns FlagOK
   If not(FlagOK)
      ; The called method will have logged an error.
      Quit method kFalse
   End If
   
End For

; Find the report class for printing the report.
Do $clib.$reports.$findname(pReportClassName) Returns rClass
If isnull(rClass)
   Calculate Mssg as con("Unable to find the specified report class, ",pReportClassName,".")
   Do $ctask.errhndler.$logError($cmethod,Mssg)
   Quit method kFalse
End If

; If we get this far we can print the report.
Set report name [pReportClassName]
Print report {(pReportList)}
Quit method kTrue

Here is a sample of how I now write code with a single exit points.

; Note: The FlagOK parameter init value is always set to kFalse.

; Check the incoming parameter
If pReportList.$linecount=0
   Calculate Mssg as "The report list is empty, unable to print the report."
Else
   
   ; Preset the flag to true before the loop.
   Calculate FlagOK as kTrue
   
   ; Check for any inactive records from the list.
   For pReportList.$line from 1 to pReportList.$linecount step 1
      
      Do method checkData (pReportList) Returns FlagOK
      If not(FlagOK)
         ; The called method will have logged an error.
         Break to end of loop
      End If
   End For
   
   If FlagOK
      
      ; Find the report class for printing the report.
      Do $clib.$reports.$findname(pReportClassName) Returns rClass
      If isnull(rClass)
         Calculate Mssg as con("Unable to find the specified report class, ",pReportClassName,".")
         Do $ctask.errhndler.$logError($cmethod,Mssg)
         Calculate FlagOK as kFalse
      Else
         
         ; If we get this far we can print the report.
         Set report name [pReportClassName]
         Print report {(pReportList)}
         
      End If
   End If
End If
Quit method FlagOK

Debugging methods which have a single exit point at the end of the method is much easier. You can always place a breakpoint at the end of the method, let the method execute, and check the return flag or value at the end of the method to see if it is correct.

Always Return Something

Always return something from a method! If the method is so simple that it can never have an error end the method with Quit method kTrue anyway.

When it come to return value I have two categories of methods:

  1. Methods that return a flag (true or false)
  2. Methods that return a value. (string, number, item reference, list, or row)

Communicating an error from these two categories of methods is simple.

  1. Methods that return a flag return kFalse if there is an error.
  2. Methods that return a value return #NULL if there is an error.

Testing Return Values

Testing the return value from methods that return a flag (true or false) is easy. Depending on the situation you test for If FlagOK (good) or If not(FlagOK) (error).

Do method checkData (List) Returns FlagOK
If FlagOK
   
   Do method saveData (List) Returns FlagOK
   
End If
Quit method FlagOK

Testing the return value from a method that returns a value is normally easy. Depending on the situation you test for If not(isnull(RetValue)) (good) or If isnull(RetValue) (error).

Do method retClassRef (ClassName) Returns rClass
If isnull(rClass)
   Calculate FlagOK as kFalse
Else
   
   Do method openClassInstance (rClass) Returns rInst
   If isnull(rInst)
      Calculate FlagOK as kFalse
   Else
      Calculate FlagOK as kTrue
   End If
End If
Quit method FlagOK

Testing for isnull() does not work for list and row variables. For lists or rows you must check the $colcount rather than isnull(). If List.$colcount>0 (good), or List.$colcount=0 (error).

; Testing a list or row for isnull() does NOT work.
Do method retDefinedList (SQLClassName) Returns List
If isnull(List)
   Calculate FlagOK as kFalse
Else
   Do List.$getAllRecords() Returns FlagOK
End If
Quit method FlagOK

; Testing a list or row for $colcount does work.
Do method retDefinedList (SQLClassName) Returns List
If List.$colcount=0
   Calculate FlagOK as kFalse
Else
   Do List.$getAllRecords() Returns FlagOK
End If
Quit method FlagOK

When to Log an Error

This is a very simple rule. The method that first detects an error, logs the error.

When to Prompt an Error

Ideally, only the following methods send $promptonceLastError messages to the error handler.

  1. $construct and $destruct methods of the Startup_Task.
  2. $event method of any visual object.
  3. $construct method of any visual object.
  4. $construct or $print method of a report class.
  5. $timer method of a timer object class.
  6. $construct method of an object class or a table class (try to avoid these).
The above methods are generally at the top of the methods stack because these methods are called by Omnis Studio. If we diligently test the return value in the above methods and send a $promptonceLastError to the error handler in the event of an error condition from the above method, all other methods in our application simply need to return something and theoretically never issue a $promptonceLastError.

What is an Error?

For each method you write, carefully decide what is an error, and what is not an error.

Originally when I wrote the $getAllRecords table class method I returned false if no records were fetched. Later as I wrote more application code I ran into situations where a table might not have any records and in that situation it was not an error.

Now I was in trouble, I had to return false because other methods were depending on false to indicate no records were fetched, but for the empty table, this really was not an error.

Eventually I came to the conclusion that for table class methods, fetching zero records should not be considered an error. Only SQL errors and check data errors would be classified as errors. It would be up to the sender method to check the List.$linecount and decide whether or not zero fetched records was a problem for them.

Always specify in the method description the return value and error conditions.

In the StudioWorks framework we eventually added a special class of errors, called minor errors, with an accompanying $logMinorError method. The $logMinorError method adds the minor error to the errors list in RAM, but does not write the error to the log file.

A situation where we would log a minor error is when the non-visual oLogon object is asked by the visual Sign-In window to $logon to the database, but the user has entered the wrong user name or password. The non-visual oLogon object sends a $logMinorError message to the error handler and returns false to the Sign-In window. The minor error becomes the current error in the errors list, but is not written to the log file. The Sign-In window sends a $promptonceLastError message to the error handler and the error handler opens a prompt window with the current error. The prompt window notifies the user that they have entered the wrong user name or password.

In the StudioWorks framework we also added an errortype property to the errors list. The errortype determines the type of error prompt window the user sees. If the errortype is minor the prompt window title doesn't use the word Error, and the error message information does not include the source information of the method which logged the error.

There is a lot you can do to extend the oErrorHandler object which we have built in this tutorial.