OpenEdge Development: Progress 4GL Handbook
![]() ![]() ![]()
|
Running Progress 4GL ProceduresThis chapter describes how you can work with and run different types of Progress 4GL procedures. It covers the following topics:
Running a subprocedure
To run a procedure from within another 4GL procedure, you use the
RUNstatement. Like theCOMPILEstatement, theRUNstatement has a lot of options, some of which you’ll learn about in later chapters. For now a very simple form of the statement will do:
If you are running a procedure file like your
h-CustSample.p, then the convention is to include the.pextension in the procedure name. For example, if you bring up a new Procedure Window again, you can enter this statement to run the sample procedure you’ve been working on, then press F2:
![]()
This is a little different than just pressing the F2 key in the procedure window where you’re editing
h-CustSample.p. When you press F2 or select CompileRun you are compiling and running the current contents of the Procedure Editor window, which might be different from what you have saved to a named file. You can compile and run a procedure without giving it a name at all. Indeed, that’s what you just did: you created a procedure containing a single
RUNstatement, and then simply compiled and executed it by pressing F2. This temporary procedure in turn ran the named procedure you had already saved and compiled.Now, why did you put the
.pextension on theRUNstatement when you have already saved a compiled.rversion of the file out to your directory? When you write a 4GLRUNstatement with a.pextension on the procedure name, Progress always looks first for a compiled file of the same name, but with the.rextension. If it finds it, it executes it. If it doesn’t, it passes the source file to the Progress compiler, which compiles it on the fly and then executes it. In this way, your application can be a mix of compiled and uncompiled procedures while you’re developing it. If you always use the.pextension in yourRUNstatements, then your procedures are always found and executed whether they’ve been compiled or not. By contrast, if you specify the.rextension in aRUNstatement, then Progress looks for only the compiled.rfile, and theRUNfails if it isn’t found.Using the Propath
Before you continue on to look at the parameters that you can pass to a
RUNstatement, you should understand a little about how Progress searches for procedures. When you type RUN h-CustSample.p, how does Progress know where to look for it?Progress uses its own directory path list, called the Propath, which is stored as part of its initialization (
progress.ini) file. When you install OpenEdge, you get a defaultprogress.inifile in thebinsubdirectory under your install directory. You can also create a local copy of this file if you want to change its contents.If you want to look at and maintain your Propath, there’s a tool to do that for you. It’s one of a number of useful tools you can access from the Procedure Editor.
To access these tools from the Procedure Editor, select Tools
PRO*Tools. The PRO*Tools palette appears:
![]()
You can reposition and reshape the PRO*Tools palette.
To retain the palette’s new shape and position permanently:
- Right-mouse click on the PRO*Tools palette outside the area where icons are displayed. The Menu Bar pop-up menu item appears:
![]()
- Select the Menu Bar pop-up item. A File menu appears at the top of the palette:
![]()
- Select File
Customize. The PRO*Tools Customization dialog box appears:
Using this tool you can add more useful tools of your own to the palette. For now, however, all you need to note is that the Save Current Palette Position and Orientation toggle box lets you save these settings permanently, so that the PRO*Tools palette always comes up the way you want each time you start OpenEdge.- Leave the Save Current Palette Position and Orientation toggle box checked on, then choose OK.
To view and edit your Propath, choose the Propath icon
from the PRO*Tools palette.
The Propath Editor shows you a list of all the directories in which Progress looks, both to find all its own executable files and other supporting files, and to find your application procedures. Here is the default Propath you get when you install the product:
![]()
As you can see, Progress first looks in your current working directory (by default,
C:/Program Files/OpenEdge). This is where it expects to find any procedures you have written and are trying to compile or run. After that it looks in theguidirectory under your OpenEdge install directory. This is where it expects to find the compiled r-code for all the 4GL procedures that support your development and run-time environments. Remember that most of the OpenEdge development tools, including the Procedure Editor, are themselves written in the 4GL.In the
guidirectory are a number of procedure libraries, each with a.plfilename extension. These are collections of many.rfiles, gathered together into individual operating system files. Theadecomm.plfile, for example, is a library of many dozens of procedures that are common to the development tools, called the Application Development Environment (ADE). It’s more efficient to store them in a single library because it takes up less space on your disk and it’s faster to search.Following these procedure library files is the install directory itself. This holds a few startup files as well as the original versions of the sports2000 database and other sample databases shipped with the OpenEdge products.
Next is the
bindirectory. This is where all the executable files are located, including all the supporting procedures for compiling and running your application, which are not written in the 4GL. Finally, there are a couple of directories that support building a custom OpenEdge executable.Do not modify any of the directories below the current directory. If you try to, Progress resets the Propath, because it recognizes that the tools will stop running if it can’t find all the pieces it needs. But you can add directories above, below, or in place of your current working directory. For example, if you save your r-code into a different directory using the
SAVE INTOoption on theCOMPILEstatement, then you must add this directory to your Propath.When you
COMPILEorRUNa procedure, you can specify a partial pathname for it relative to some directory that is in your Propath. For example, if you save something calledNextProc.pinto a subdirectory calledmorecode, you can run it using this statement:
Note: You should always use forward slashes in your Progress code, even though the DOS and Windows standard is backslashes. Progress does the necessary conversions to make the statement work on a PC, but if some of your Progress 4GL code is written for another platform, such as UNIX, then it requires the forward slashes.You can modify your Propath by using the Add, Modify, and Remove buttons in the Propath Editor.
If Progress is having difficulty locating a procedure that you’ve written, or if you have different versions of a procedure in different directories you can check to see how Progress searches for a particular procedure.
To verify which file Progress finds:
- In the Propath Editor, choose the Search button. The Propath File Search dialog box appears:
![]()
- Enter the name of the procedure, including the relative pathname if that’s part of your
RUNstatement. Also include the same filename extension you are using in your code.- Choose the Search button. Progress displays all the versions of that file it can find, in the order in which it uses them. Thus the one at the top of the list is always the one Progress tries to execute. In this example, the display confirms that when you run
h-CustSample.p, it chooses the.rfile (if there is one) in preference to the source procedure:
![]()
If you save your r-code to a different directory (or set of directories) than you use for the source procedures, remember to construct your Propath in such a way that the r-code is found first if it is there, but that the source procedures are also found, at least when you are in development mode, without requiring a special relative pathname in your code to locate them.
In the OpenEdge install directories, for example, all the compiled 4GL procedures are under the
guidirectory. The corresponding source procedures are in an equivalent directory tree, but under ansrcdirectory. If you were to add thesrcdirectory to your Propath, then Progress could locate source procedures to compile and execute on the fly as needed. If you were to place thesrcdirectory above theguidirectory in your Propath, then Progress would use all the source procedures and compile them on the fly because it finds them first. This would slow down your program execution dramatically. This is just an example of how, as you develop complex applications, you must be careful to arrange your Propath so that Progress can find the procedures it needs efficiently, without doing a lot of unnecessary searching or running uncompiled procedures.Using external and internal procedures
A separate source procedure, such as
h-CustSample.p, is known as an external procedure. This is to distinguish it from another kind of the procedure that you’ll write as the next step in your sample. you can define one or more named entry points within an external procedure file, and these are called internal procedures. You run internal procedures in almost exactly the same way that you run external procedures, except that there is no.pfilename extension on the internal procedure.For your sample, you will run a procedure called
calcDaysthat calculates the number of days between the ShipDate for each Order and today’s date. In order forcalcDaysto do the calculation, you need to pass the ShipDate to the procedure, and get the count of days back out. Progress supports parameters for its procedures to let you do this. Follow these guidelines:
- In a
RUNstatement, you specify the parameters to theRUNstatement in parentheses, following the procedure name. Use commas to separate multiple parameters.- An
INPUTparameter can be a constant value, an expression, or a variable or field name. AnINPUTparameter is made available by value to the procedure you are running. This means that the procedure can use the value, but it cannot modify the value in a way that is visible to the calling procedure.- An
OUTPUTparameter must be a variable or field name. Its value is not passed to the called procedure, but is returned to the calling procedure when the called procedure completes.- You can also define a parameter as
INPUT-OUTPUT. This means that its value is passed in to the procedure, which can modify the value and pass it back when the procedure ends. AnINPUT-OUTPUTparameter must also be a field or variable.For each parameter in theRUNstatement, you specify the type of parameter and the parameter itself. The default isINPUT, so you can leave off the type forINPUTparameters. You cannot have optional parameters in a Progress procedure. You must pass in a parameter name or value for each parameter the called procedure defines. They must be in the same order, and they must be of the same data type.To run the
calcDaysprocedure from your sample procedure:
- Add the following
RUNstatement to yourh-CustSample.pprocedure, inside theFOR EACH Order OF Customerblock, just before theENDstatement. Pass in the ShipDate field from the current Order and get back the calculated number of days, which uses the iDays variable you defined earlier when you learned to use the intelligent editor’s aliases:
- Following this, still inside the
FOR EACH Orderblock, write another statement to display the value you got back along with the rest of the Order information:
Writing internal procedures
Now it’s time to write the calcDays procedure. Put it at the end of
h-CustSample.p, following all the code you’ve written so far.Each internal procedure starts with a header statement, which is just the keyword
PROCEDUREfollowed by the internal procedure name and a colon.Following this you need to define any parameters the procedure uses. The syntax for this is very similar to the syntax for the
DEFINE VARIABLEstatement. In place of the keywordVARIABLE, use the keywordPARAMETER, and precede this with the parameter type—INPUT,OUTPUT, orINPUT-OUTPUT. Note that the keywordINPUTis not optional in parameter definitions. Here’s the declaration for thecalcDaysprocedure and its parameters. The parameter names start with the letter p to help identify them, followed by a prefix that identifies the data type asDATEorINTEGER:
PROCEDURE calcDays:DEFINE INPUT PARAMETER pdaShip AS DATE NO-UNDO.DEFINE OUTPUT PARAMETER piDays AS INTEGER NO-UNDO.
Now you can write 4GL statements exactly as you can for an external procedure. If you want to have variables in the subprocedure that aren’t needed elsewhere, then define them following the parameter definitions. Otherwise you can refer freely to variables that are defined in the external procedure itself. You’ll take a much closer look at variable scope and other such topics later. Be cautious when you use variables that are defined outside a procedure unless you have a good reason for using them, because they compromise the modularity and reusability of your code. For example, if you pull your procedures apart later and put the
calcDaysprocedure somewhere else, it might break if it has a dependency on something declared outside of it. For this reason, you pass the calculated number of days back as anOUTPUTparameter, even though you could refer to the variable directly.Assigning a value to a variable
The
calcDaysprocedure just has a single executable statement, but it’s one that demonstrates a couple of key new language concepts—assignment and unknown value.You’re probably familiar with language statements that assign values by using what looks like an equation, where the value or expression on the right side is assigned to the field or variable on the left. Progress does this too, and uses the same equal sign for the assignment that is used for testing equality in comparisons. However, you can use the keyword
EQonly in comparisons, not in assignments.In this example, you want to subtract the ShipDate (which was passed in as the parameter
pdaShip) from today’s date (which, as you have seen, is returned by the built-in functionTODAY) and assign the result to theOUTPUTparameterpiDays.To make this change in your sample procedure, add the following statement:
This is simple enough, but it won’t work all the time. Remember that some of the Orders have not been shipped, so their ShipDate has the Unknown value. If you subtract an Unknown value from
TODAY(or use an Unknown value in any other expression), the result of the entire expression is always the Unknown value. In this case you want the procedure to return 0. To do this you can use a special compact form of theIF-THEN-ELSEconstruct as a single 4GL expression, appearing on the right side of an assignment. The general syntax for this is:
This syntax is more concise than using a series of statements of the form:
For your purposes you can refine the assignment statement in this way to allow for Unknown values:
Finally, you end each internal procedure with an
END PROCEDUREstatement. The keywordPROCEDUREis optional, but is always a good idea as it improves the readability of your code:
To see the calculated days displayed, Run the procedure once more:
![]()
In summary, you define an internal procedure just as would an external procedure, with the exception that each internal procedure must start with a
PROCEDUREheader statement and end with its ownEND PROCEDUREstatement. An external procedure uses neither of these.You
RUNan internal procedure just as you would an external procedure, except that there is no filename extension on the procedure name. Here are a couple of important related points:First, it is possible for you to run an external procedure by naming it in a
RUNstatement without any filename extension, for example,RUN CustSample. In this case, Progress searches for these forms of the procedure in this order:To do this, however, would be considered very bad form. When a person reading Progress code sees a
RUNstatement with no filename extension, it is natural for that person to expect that it is an internal procedure. There is rarely a good reason for violating this expectation.Second, and even more exceptional, is that because a period is a valid character in a procedure name, it would be possible to have an internal procedure named, for example,
calcDays.p, and to run it under that name. You should never do this. Always avoid confusing yourself or others who read your code.When to use internal and external procedures
The decision as to when to use a separate external procedure file versus when to make an entry point for an internal procedure in a larger file is largely a matter of style and application organization. It’s a good idea to group together in a single procedure file related small procedures that call one another or that are typically called by multiple other procedures. On the other hand, large procedures that perform complex operations on their own should probably be independent external procedure files. Keep in mind that when you need to run an internal procedure, Progress first needs to load the entire procedure file that contains it, and this can consume excessive memory if you make your procedure files extremely large. Later in "Advanced Use of Procedures in Progress," you’ll learn about persistent procedures and how you can preload a file that contains many internal procedures that other procedures need to call.
Adding comments to your procedure
The final step in this exercise is to add some comments to your procedure to make sure you and everyone else can follow what the code does. In Progress you begin a comment with the characters
/*and end it with*/. A comment can appear anywhere in your procedure where white space can appear (that is, anywhere except in the middle of a name or other token). You can put full-line or multi-line comments at the top of each section of code, and shorter comments to the right of individual lines. Just make sure you use them, and make them meaningful to you and to others. The intelligent editor colors comments in green by default. The editor can also type the comment symbols for you, if you type CMT and press the SPACEBAR in the editor window. Here’s the final procedure with a few added comments:
You’ve learned a tremendous amount about the Progress 4GL in the course of writing a procedure barely over twenty lines long, which retrieves and merges data from two different database tables, and performs a number of extra calculations and formatting operations along the way. These first chapters have tried to provide you with some insight into the power of the Progress 4GL. Feel free to experiment with the language statements and functions you’ve encountered.
In the next chapters, you’ll look at another major OpenEdge development tool, the AppBuilder, and use it to generate the code you need to create an application window that looks a lot closer to the kind of graphical interface you might expect in your applications. Later chapters then introduce you to a world of creating applications with almost no code at all!
|
Copyright © 2005 Progress Software Corporation www.progress.com Voice: (781) 280-4000 Fax: (781) 280-4095 |
![]() ![]() ![]()
|