Anda di halaman 1dari 24

Lessons March 2017

Learned Number 55

in Version
1 Deep Dive
Lessons Learned in Version Control,
Part 2
Doug Hennig

Control, 6 VFP
Seamlessly Read/Edit/Save numeric

Part 2
data of variable precision
Pradip Acharya

8 VFPX
Continuous Integration with
Doug Hennig Jenkins and Visual FoxPro
Eric Selje

The previous article, the first in this three-part


series, looked at setting up local and remote reposi- 19 VFP
tories and how to deal with files commonly used in With abbreviations to success
multiple applications. This time, we’ll look at how Christof Wollenhaupt
to handle VFP binary files and what should go in
the repository, then discuss the workflow we use at
Stonefield Software. be compared to each other using traditional “diff”
tools. As a result, you can’t compare changes in two
Dealing with VFP binary files copies of a file, nor can you merge changes.
In the last article, we set up version control for a Fortunately, there are several tools available
new application, creating local repositories on two that convert VFP binary files to text files that can
machines (one named Doug and the other named be compared and merged. The latest one is FoxBin-
Peggy) and a central repository on a file server, 2Prg, a VFPX project. There’s even an extension for
Now we’re ready to start working on the applica- it, Bin 2 Text Extension, also a VFPX project, which
tion. We need to add buttons to the form and code adds support for Git and IDE integration.
in the Click method of those buttons. However, if Installing FoxBin2Prg is easy: simply download
we both work on the same form, we’ll have a big it from VFPX (http://vfpx.codeplex.com) and install it
issue: how to consolidate our changes? in any folder. If you use Thor (the most important
As I’m sure you’re aware, some VFP files are add-on there is for VFP and also a VFPX project),
actually tables: VCX, SCX, PJX, MNX, FRX, LBX, it’s even easier: from the Thor menu, choose Check
and DBC (along with their accompanying memo for Updates, turn on FoxBin2Prg in the dialog that
files). Since VFP tables are binary files, they can’t appears, and click Install Updates. I’m going to
assume you’re using Thor for the purposes of this
article. If not, see the FoxBin2Prg documentation
for instructions on how to use it.
To create text versions of all of the binary files
in a project, choose Thor Tools, Applications, Fox-
Bin2Prg, Projects, Convert All Binary Files to Text
Files from the VFP system menu. This function cre-
ates text files from the binary files, using the same
file name as the binary files but with “2” replacing
the final “x” in the extension (for example, MyApp.
pj2 for the text version of the PJX file). A similar
function does the opposite: generating binary files
from the text files. To make it easier to access the
functions, you can create a Thor hotkey for that
using the Thor Configuration dialog. I use Alt-1 to
create text files from the binary files and Alt-2 to
create binary files from the text files.
I created a little Thor tool that both creates text
versions of all binary files in a project and brings up
the TortoiseHg Commit dialog, saving a few mouse
clicks. I assigned a hotkey of Alt-3 to the tool so
when I’m ready to commit changes in the applica-
tion, I simply hit Alt-3, enter a comment, and click
Commit. Creating a Thor tool is beyond the scope Figure 1. The Resolve Conflicts dialog shows which files have
of this article, but all it really does is: conflicting changes that need to be resolved.

do (_screen.cThorFolder + ;
'Tools\Thor_Tool_Repository_' + ;
ferences in MainForm.scx and sct because they’re
'FoxBin2PrgConvertProjectToText.prg') binary files. In this case, it doesn’t matter whether
run /n0 thg commit we choose Take Local or Take Other since we’re
going to regenerate the two files from MainForm.
Thor_Tool_Repository_FoxBin2PrgConvert- sc2 in a moment.
ProjectToText.prg is the Thor tool FoxBin2PRG
After the update, Peggy uses the hotkey she
installs to convert the binary files in a project to text
assigned to generate binary files from the text files.
files.
When she then edits MainForm.scx, she sees both
To add my tool to Thor, simply copy Thor_ of our changes.
Tool_Convert2PRGAndCommit.prg (included
One of the things you’ll note is that when I
in the downloads for this article) to the Thor\
open the project, I’m prompted to change the home
Tools\My Tools folder of wherever you have Thor
directory. After I push changes and Peggy pulls
installed. Choose Configure from the Thor menu
them, she’s prompted to change the home direc-
and assign a hotkey to this tool if you wish.
tory when she opens the project. This gets a little
It’s important to generate text files for the bina- annoying after a while, so we’ll see how to resolve
ries in the project, add them to version control, this next.
push them to the central repository, and pull them
into all developers’ repositories only when all other What should go in the
changes have been synchronized in all repositories.
Otherwise, merging the changes can be a mess.
repository?
If you look at the list of files in the repository, you’ll
To see this in action, I generate text files for the notice I added everything in the project folder hier-
binaries in the project, add them to the repository, archy, including FXP and EXE files. Is that neces-
and push to the server; Peggy pulls from the server sary or desirable? Not really:
and updates so she has the latest version. Then I
• The larger the repository, the longer it
modify the code in the Click method of the existing
takes to clone, commit, push, pull, etc.
button in the form while Peggy adds another but-
ton to the same form. I commit and push, then she • Even if you don’t change the source
commits, pulls, and merges with her local changes. code, rebuilding a project may regener-
Because we both made changes in the same form, ate FXP and other generated files. As a
there are several merge conflicts that need to be result, it looks like you have to commit
resolved (Figure 1). Tool Resolve handles the differ- and push and other developers have to
ences in MainForm.sc2 but it can’t resolve the dif- pull and merge even though nothing’s
really changed.

Page 2 FoxRockX March 2017


The general rule of thumb is that nothing gen- whatever source you used to create it.
erated should go in the repository. In the case of a If you’re using West Wind HTML Help
VFP application, that means you should exclude: Builder, you should include the HBP,
• EXE, APP, and DLL files generated by FPT, and CDX files for the help source
your projects as well as WWHelp.ini and the contents
• FXP, MPX, and QPX compiled program of the BMP, Images, and Templates fold-
files ers (you can actually skip the BMP and
Templates folders since they’re created
• MPR files since they’re generated from
when you create a new help project and
menus
don’t change, but I include them to make
• ERR, BAK, and TBK files it easier to clone the repository). The
• Any files generated by running your rest of the files are generated by Help
application (TXT, HTML, XML, MEM, Builder and so should be excluded. Note
etc.) that since the HBP file is actually a VFP
Other files require some consideration. table, you might want to use FoxBin2Prg
to convert it to a text file and include the
• VFP binary files: assuming you’re using
text file instead.
FoxBin2Prg or something similar as dis-
cussed in the previous section, you can • Testing files: this includes FoxUnit or
exclude the binary files. One benefit of other test programs, any configuration
this is that you’re no longer prompted to settings, and test data. These should be
change the home directory for the project included in the repository. As with the
when you open it, since you’re recreating other points, consider using FoxBin2Prg
it with the current directory when you to create text files from any binary files
use FoxBin2PRG to generate the binary and include those instead.
files. • Documentation: this could be Microsoft
• Data files: it depends: if they’re used but Word documents describing the feature
not maintained by the application—for set, Microsoft Excel documents detailing
example, meta data such as Stonefield the development plans and costs, and
Database Toolkit’s, lookup data, con- so on. Note that these are binary files so
figuration settings not maintained by you won’t be able to merge changes. For
the user, etc.—definitely (don’t forget to that reason, it’s best if only one person be
include any CDX or FPT files in addition responsible for editing these files.
to the DBF). Otherwise, probably not, • Deployment files: your setup source files
or else every time you add a record to (for example, the ISS file if you use Inno
a sample copy of the tables maintained Setup) as well as any other files related to
by the application, they’re marked as deployment (such as BAT or PowerShell
changed and have to be committed, PS1 files) should be included.
pushed, pulled, merged, etc. Database • Visual Studio files: if you’re like me and
container files (DBC, DCX, and DCT) have some in-house .NET components
should be included unless you use GEN- in your applications, you should include
DBC to generate a program to recreate the source files for those components in
the database; in that case, include that the repository. You can exclude the gen-
generated program. You might want to erated files (those in the BIN and OBJ
use FoxBin2Prg to convert tables and folders) and SUO (user preference set-
database containers to text files and tings) file.
include those instead. FoxBin2PRG • hgignore or gitignore file: these files con-
doesn’t handle data files automatically; tain information about which files the
see its documentation for details on how version control system should ignore.
to configure it to do so. They should be included in the reposi-
• Images: these should be included but tory even though they’re not strictly part
keep in mind that they’re binary files so of the project.
changes made by more than one person
at a time can’t be merged. Fortunately,
most developers I know get their images Ignore file
from other sources rather than creating Speaking of the ignore file, you should use one to
them themselves so this is rarely an issue. tell your version control software not to list cer-
• CHM (help) file: there’s no need to tain files in the commit dialog. Otherwise, it’s dif-
include the CHM file (or CHW file if it ficult to tell which are files you routinely exclude
exists) since it can be regenerated from and which are new but haven’t been added yet.

March 2017 FoxRockX Page 3


See http://tinyurl.com/hpgh8yy for a discussion of # Folders to exclude
bin/*
­.hgignore for Mercurial. Listing 1 has the content obj/*
of a typical .hgignore file for a VFP application (this
file is included with the downloads for this article). # Specific files
Note that .hgignore is case-sensitive so file name *.exe
*.EXE
and extensions are usually listed in multiple cases
unless you use regular expression syntax. You can also set up a global ignore file, one
Listing 1. Typical .hgignore file for a VFP application. that’s used for every repository, by editing your
# use glob syntax. global settings file (usually C:\Users \UserName\
syntax: glob Documents\Mercurial.ini, where UserName is the
Windows user name) and adding the following to
# VFP generated files
*.err
the [ui] section:
*.ERR [ui]
*.qpr ignore = Path
*.QPR
*.mpr In place of Path, put the desired path and file
*.MPR
name. For example, if you specify “%USERPRO-
# VFP compiled files FILE%\.hgignore,” Mercurial uses .hgignore in
*.fxp your Documents folder.
*.FXP
*.mpx After setting up the ignore file, remove the files
*.MPX you no longer want under version control by select-
*.qpx ing them, right-clicking, choosing TortoiseHg, and
*.QPX
Forget Files, then commit the changes. Note: one
# VFP backup files issue you might run into when you do this is that
*.bak the case of the file extension somehow changes.
*.BAK For example, I removed “mainform.sct” from the
*.tbk
repository but when I went to commit the change, it
*.TBK
failed because “mainform.SCT” wasn’t part of the
# VFP binary files repository. To fix this, I reverted the change to the
*.pjx file (which now has an SCT rather than sct exten-
*.PJX
sion), then forgot the file again and committed the
*.pjt
*.PJT change.
*.vcx
*.VCX
*.vct Workflow
*.VCT Here’s the process for development to ensure all
*.scx
developers keep updated with other developers’
*.SCX
*.sct changes (see Figure 2):
*.SCT • Make changes as necessary.
*.mnx
*.MNX • Run any tests you have for the project
*.mnt and fix any problems identified by fail-
*.MNT ing tests.
*.frx
*.FRX
• Convert the binary files to text files.
*.frt • Commit the changes.
*.FRT • Pull from the central repository to
*.lbx
*.LBX retrieve changes other developers made.
*.lbt • If anything was retrieved:
*.LBT
o Merge the changes with your local
# Misc backup files folder and resolve conflicts as neces-
*.orig sary.
*.ORIG
*.save o Convert the text files to binary files.
*.SAVE o Repeat all of the above steps.
*.zip
*.ZIP • Once a pull retrieves nothing from the
repository, push to the central reposi-
# Other files
*.chw
tory.
*.suo
*.docstates

Page 4 FoxRockX March 2017


Author Profile
Doug Hennig is a partner with Stonefield Soft-
ware Inc. He is the author of the award-winning
Stonefield Database Toolkit (SDT); the award-
winning Stonefield Query; the MemberData
Editor, Anchor Editor, and CursorAdapter
and DataEnvironment builders that come with
Microsoft Visual FoxPro; and the My namespace
and updated Upsizing Wizard in Sedna.
Doug is co-author of “VFPX: Open Source
Treasure for the VFP Developer,” “Making
Sense of Sedna and SP2,” the “What’s New
in Visual FoxPro” series (the latest being
“What’s New in Nine”), “Visual FoxPro Best
Practices For The Next Ten Years,” and “The
Hacker’s Guide to Visual FoxPro 7.0.” He
was the technical editor of “The Hacker’s
Guide to Visual FoxPro 6.0” and “The Fun-
damentals.” All of these books are from Hent-
zenwerke Publishing (http://www.hentzen-
werke.com). He wrote over 100 articles in 10
years for FoxTalk and has written numerous
articles in FoxPro Advisor, Advisor Guide to
Visual FoxPro, and CoDe. He currently writes
for FoxRockX (http://www.foxrockx.com).
Figure 2. Version control workflow. Doug spoke at every Microsoft FoxPro Devel-
opers Conference (DevCon) starting in 1997
and at user groups and developer conferences
all over the world. He is one of the organiz-
Next time ers of the annual Southwest Fox conference
In the next article, the last in the series, we’ll look at (http://www.swfox.net). He is one of the administra-
when to commit changes, when to push, strategies tors for the VFPX VFP community extensions Web site
for branching, how to fix common problems, and (http://vfpx.codeplex.com). He was a Microsoft Most
using a cloud-based repository. Valuable Professional (MVP) from 1996 to 2011.
Doug was awarded the 2006 FoxPro Community Life-
time Achievement Award (http://tinyurl.com/ygnk73h).

FoxRockX™(ISSN-1866-4563)

Copyright © 2017 ISYS GmbH. This work is an independently produced


dFPUG c/o ISYS GmbH publication of ISYS GmbH, Kronberg, the content of which is the
Frankfurter Strasse 21 B property of ISYS GmbH or its affiliates or third-party licensors and which
61476 Kronberg, Germany is protected by copyright law in the U.S. and elsewhere. The right to copy
Phone +49-6173-950903 and publish the content is reserved, even for content made available for
Fax +49-6173-950904 free such as sample articles, tips, and graphics, none of which may be
Email: foxrockx@dfpug.de copied in whole or in part or further distributed in any form or medium
Editor: without the express written permission of ISYS GmbH. Requests for
Rainer Becker-Hinrichs permission to copy or republish any content may be directed to Rainer
Becker-Hinrichs.

FoxRockX, FoxTalk 2.0, FoxTalk, Visual Extend and Silverswitch are trademarks of ISYS GmbH. All product
names or services identified throughout this journal are trademarks or registered trademarks of their
respective companies.

FoxRockX is published bimonthly by ISYS GmbH

March 2017 FoxRockX Page 5


Seamlessly Read/Edit/Save
numeric data of variable
precision Author Profile
Pradip Acharya is a Toronto based developer. Techni-
cally retired after 50 years in the software field, he still
dabbles in new concepts and helps out customers when
Pradip Acharya called upon. pacharya@sympatico.ca.

Numeric data of Integer and Floating Point types, the limiting values by 2 or even add 1. Instead we
are commonly edited and saved using some have arbitrarily chosen to use the square root of the
derived class based on the Textbox baseclass, then upper limiting value as practical usable range for
setting a Format mask and so on. Integer fields will our xNumeric class and it works out very well.
have no decimal point and floating point fields will The practical limits used for the class are:
have a precision of 2 decimals digits. Now consider
-9.480751908109E-153
the constant Pi which is 3.14 to 2 decimal digits and
9.480751908109E+153
3.1415926536 to 11 decimal digit precision. How
would you like to read in 3.14 from the table field,
type in 3.1415926536, save, and read it back just as The Demo
saved. Chemical, Physical and analytical work or At load time, the Demo Load method creates a tem-
chemical, mathematical or other entities are all of porary cursor with a random file name. When the
variable precision. Example are Hg concentration demo ends. This cursor is closed i.e. Deleted.
in drinking water or engineering tolerances. The As a side discussion, let's talk about creating a
required xNumericbox class and demo program truly random cursor name generation. The RAND()
are outlined in the article. The limiting constraint is function is used for this purpose. This function is
the maximum precision of 14 decimal places in VFP seeded with an initial call in the form RAND(5). If
64-bit double field type on good days. On bad days, you use the same initial seed, the generated num-
it can degrade to 13 decimal places. bers are the same for the same inputargument.
Numeric Data are saved in four types of data For the purpose of this article, we randomize
fields in VFP – I, B and F/N, I for integer, B for the seed which is a combination of a part of Today's
Double floating binary and F/N for 4-byte fixed date and Seconds() of the current time. This creates
decimal point floating point. The method outlined a random file stem which is unlikely to repeat.
here applies only to Double B editing because the Here is the code for random file name genera-
F & N types are permanently stamped with fixed tion:
­nFieldwith and nPrecision attributes, and not in ?"_"+ RIGHT(TRANS(DAY(DATE())),3)+;
pure Mantissa and Exponent bit pattern. RIGHT(TRANS(INT(SECONDS())),3)
Since this class does not enforce any fixed An example of such a random name as the file
­Format mask, this class is not to be used for han- stem would be _27679.
dling every day Money or Integer or fixed Decimal The demo is in two parts. In the first part, you
fields. will load a preset into the B field value in the cur-
sor, which this case is 3.14 which is the value of Pi
Numeric data Precision Limits two decimal places. Now type in additional digits
The 8 byte D field has a usable precision limit of 14 to the right to make 3.1415926536E0 and click on
decimal digits and a magnitude limit of: save. Reload. You will see the field value of D in
the edit box 3.1415926536E0. If you close the demo
4.940656458413E-324
form and reload, You'll be loading in the initial
8.9884656743114E307
value of 3.14.
52 bits are allocated to the Mantissa and 12 bits are
reserved for the Exponent.
The Demo - Part 2
The limiting numeric range in VFP shown Here we will create an external user table and save
above is only of academic interest. It is of little edited data. Come back, and check the edited value
practical value since you cannot even multiply by reloading the field in the xNumeric box.

Page 6 FoxRockX March 2017


In the command window: The markers are:
SELECT 0 idot Position of the Period
CREATE TABLE MYTABLE(D B) iepos Position of the exponent E
APPEND BLANK i1st1to9 Position of the first 1-9 digit
REPLACE D WITH 1234.5678 isminus Is there a -sign in position 1
Now we will load data from the external table n0to9 Total 0-9 digits
for editing. n1to9 Total 1-9 digits
ntextlen Text length of the numeric string
This is done by:
The markers are used by the KeyPress event
m.oXnumeric.xNumeric1.Loadin('Mytable.D ')
to pass or block incoming characters and also for
Note the input parameter is the character string string to numeric conversion.
ALIASNAME.FIELDNAME and not its binary
value of the field. So when you Save the edited Different ranges, different editing
field, it will know where to update the edited value. The ProgrammaticChange procedure of the Class
Change the value to 8756.4321 and save. It xNumeric selects the applicable editing scheme
saved the data to your table. and sets the stage for editing for the incoming Dou-
If you now do: ble Float number by selecting a range based display
m.oXnumeric.xNumeric1.Loadin('Mytable.D ') and editing scheme.
We are using a TextBox of width 20 for display-
You will see the data was permanently saved in
ing and editing a type Double (D B) number as in
its edited form.
our demo form xNumeric.
For type Double (D B) numbers, there are two
Class Library xNumeric.Vcx schemes, based on the data range – non Exp plain
First task is to load binany data and convert to a and Exp type. If the absolute value of the (number
character string for editing. The conversion to <= 999999999999.99) use Non-Exp scheme edit the
string is done by checking the range of the number number as text string = TRANS(Number).
and selecting the appropriate option including the
If the absolute value of the (number <=
Exponent style. To see the ranges and branching,
9.480751908109E+153) range limit edit the
look into the ProgrammaticChange method.
number in Exponent format as Textstring =
Although the theoretical range is TRANS(Number,'@^') Exp style otherwise number
+/-4.94065645841247E-324 to is out-of-range report error 'Data out of range' and
+/-8.9884656743115E307 do nothing.
this is meaningless since these numbers can
not even be squared. An arbitrary limit of Brain damaged F and N
+-9.480751908109E+153 which the square root of Datatypes
the smaller number is set as the range limit. Let's create a table with a B type field and a F type
There are a set of character positions to parse field.
the text and to use these to write out in its table Create table mytable(D B, F F)
storage loation, when done. To be noted, the Loa- Append blank
din procedure accepts the character location as replace D with 123.456, F with 123.456
­Aliasname.Fieldname and uses it to update the To view internal data as in its native form, you
binary data back in the table. need to use the Transform function with format
mask “@^”
Keystroke management ?TRANS(D,"@^")
The class also features sophisticated keystroke han- ?TRANS(F,"@^")
dling code under the keypress procedure to slot You see that D field reflects back the exact data
characters in their right place. and the F field was saved with the format stamped
on it.
Binary to character for editing To me this is a strange concept probably origi-
The event handling required for this binary to nating in the Xbase specifications and widely popu-
string conversion is hard to describe. Let's take it larized by Dbase. Not for me and I don't use it. I
granted since it works. don't know the difference between F and N field
types, probably they are the same.
The Parse function
As each character comes in, this function re-scans Conclusion
the text string to update the marker parsing prop- In order to work with numbers of more then 14
erties. decimal precision, one will need a 128 bit machine.
But then, I don't know if VFP itself will work.
March 2017 FoxRockX Page 7
Continuous Integration with
Jenkins and Visual FoxPro
Eric Selje

This article discusses improving the Software mers did. He started hearing words he didn’t
Development Lifecycle for Visual FoxPro devel- understand, like “Git” when there were no horses
opers. We'll begin by reviewing the typical devel- around, and “Mercurial” when there were no
opment cycle, and show ways to improve and thermometers. He started seeing applications that
automate our current process using a Continuous would automatically update themselves while the
Integration server and open source tools from VFPX user was still running them, and “Nightly Builds”
such as Automated Build, FoxUnit, and source con- dropping onto servers at times when all the devel-
trol tools. opers should long have been home in bed.
When Eric finally discovered the powerful con-
A Typical Day in the Life of a VFP cept of “Continuous Integration,” his mind was
blown. This was a concept that would once again
Programmer change the way he developed forever – if he could
Eric (his name has been changed to protect his get it all to work in Visual FoxPro.
identity) is a typical Visual FoxPro developer. Eric
A True Story
got on board very early with the Fox. He learned
It’s the middle of the night. Your phone rings once,
very quickly in the late 80s that dBase was great twice…on the third ring you pick it up. It’s your boss
but FoxBase was far better. Eric was very excited (or best client, whichever is applicable to you).
to when FoxPro came out, and when Visual Fox-
Groggily you say “Hello?” while hearing a sigh from
Pro 3.0 arrived in 1995, his development careeer
your tossing spouse.”
changed forever.
“Hey!” you hear, causing you to pull the phone two
But in some ways Eric’s Visual FoxPro career inches away from your ear. “All of our nightly reports
hadn’t changed much since. New version of Visual are crashing! Fix it!”
FoxPro arrived regularly (up until recently), but the
Now slightly awake, you see it’s 3 a.m. The reports
basic way he created applications was consistent
just kicked in and immediately failed. You try to get
throughout.
it together enough to recollect what you may have
The Project Manager was always Visual Fox- done that day that would have caused this failure, and
Pro’s command center for development. When you’re actually hoping it was your fault because if it
Eric wanted to work on an application, he’d fire up was another developer then you’re going to have to
Visual FoxPro, open the project in the Project Man- murder someone on your team and then have to go
ager, and do what needed doing. Testing involved through the trouble of finding a replacement.
running the application and verifying it “did what Eventually your synapses recall that one small opti-
it was supposed to do.” Version control meant mization you made to the function that most of the
hopefully remembering to backup the application’s reports use. Yes, that’s it. Easy fix…I’ll just put that
source code directory. Deploying meant building change back. Since your company doesn’t reliably use
an EXE and copying to someplace where the users version control, you’ll hopefully remember what the
could get at it, and likely meant kicking everybody code looked like before and put it back right.
out of the application so the update could be cop- You fire up the laptop, make the change, build it, log
ied. It happened so often the users didn’t think onto your VPN, call the office, wait while everyone is
twice about it, assuming this was just the way it out of the app, and copy the new EXE to the three or
had to be. Eric too thought this was the way all pro- four different places it needs to go for everything to
grammers did it. Perhaps you do too? run smoothly until you get to your desk in… only 3
hours?!
It doesn’t have to be this way.
That “one little fix” took very little time to actually fix
Eric’s slow realization of this truth came when
but quite a bit of time to build and deploy. You hope
he left the insular world of Visual FoxPro devel-
it works, but you’ll find out if it failed when the phone
opment and was exposed to what other program- rings again just as you’re drifting back to sleep.

Page 8 FoxRockX March 2017


I imagine that story makes you cringe. Just Free and Open Source
writing it started stirrings of PTSD inside of me. CruiseControl: This open-source framework was
Now let’s investigate how Visual FoxPro develop- the first one I used because Markus Winhard had
ers like Eric can use Continuous Integration so that already written a configuration file for Automated
none of us will ever have to suffer through an expe- Build. The original CruiseControl was written in
rience like that again. Java but was ported over to the .Net framework in
a fork called CruiseControl.Net.
What is Continuous Integration? Jenkins: Jenkins is my current open-source
At the most basic level, Continuous Integration favorite. It’s very simple to install, has a very small
(CI) means letting a tool, a Continuous Integra- footprint on my development machine, and is infi-
tion Server, automate the day-to-day tasks that you nitely configurable. Jenkins is the tool I’ll be using
have been doing manually. That means automati- in my session and for this whitepaper.
cally running the tests to ensure your code is good, This is not intended to be a comprehensive list.
building the EXE from the project’s source code, Please do some research before deciding which CI
and copying the EXE to a place where others can server is right for you.
get at it. When you’ve chosen and installed your Contin-
Some Visual FoxPro developers have con- uous Integration server and integrated all the plug-
cocted a collection of scripts and batch files to do ins you need for your project, you will have a com-
much of this mundanity, but a true CI Server is a plete system that allows you to deploy software in
comprehensive, integrated tool with hooks you a repeatable, efficient manner that decreases errors
can tap into in order to consolidate those disparate and increases your productivity.
scripts under one management tool. A CI server This diagram (Figure 1) shows the continuous
gives you the extra functionality of creating status integration cycle. The cycle begins when the devel-
reports that can be emailed and web dashboards opers at the top check in their source control to
that allow you to quickly ascertain the status of all the central repository. That causes the CI Server to
of your projects at a glance. run Unit Tests, Integration Tests, Functional Tests,
How much would you pay for this amazing Code Analysis, and whatever else you’ve plugged
Continuous Integration awesomeness? Well, you in. If the tests fail, the check-in will be rolled back,
could pay a lot as there are commercial products but if they succeed the project will be compiled into
available. On the other hand you could not pay an EXE. If the build passes (no compilation errors),
anything at all. Let’s look at some of our options. it can proceed to deploying the final product to
wherever it eventually goes. All along the way
Continuous Integration Servers reports can be generated and sent to the project
There are quite a few CI servers available (http:// team to apprise them of the status of the project.
en.wikipedia.org/wiki/Continuous_integration) , 0F Ideally the tests pass, the build is created, the
but here are some that are probably most likely to executable is deployed, the client is updated, the
be used by Visual FoxPro developers:

Commercial Offerings
Team Foundation Server (TFS): This is
Microsoft’s official offering. It’s much
more than just a CI server, it is also the
successor to Visual SourceSafe for ver-
sion control, has a facility for project
task list tracking, and reporting. This is
commonly used in Microsoft-only shops
and, in my experience, rarely used else-
where.
TeamCity: This program from Jet-
Brains, the Resharper folks, is the most
common name I hear when I ask which
Continuous Integration product other
developers are using. It is built to be
language-agnostic so it works well with
.Net, Java, Ruby, etc., and with plug-ins
can be modified to work with Visual
FoxPro as well. While I’ve listed here as
Commercial, it’s actually free for “small Figure 1. The Continuous Integration Cycle
teams.”
March 2017 FoxRockX Page 9
developers are notified, and the cycle starts again. F1Utils.
The question then becomes, to borrow a title from a If you prefer to do version control from the Win-
classic, “Will the Circle Be Unbroken?” dows filesystem, you can use the fine Tortoise tools
for Subversion or Mercurial. Tortoise integrates
Setting up a Continuous right into Windows Explorer. Tortoise doesn’t seri-
alize the binaries for you automatically though, so
Integration Server you will need to be sure to take care of that manu-
To start, let’s set up a CI Server. This can be on your ally using one of the tools mentioned above. SubFox
development machine (which is the way I do it), does have some nice hooks into Tortoise that auto-
a Windows server on your network, or a ­virtual mates the process.
machine in “the cloud.” It really doesn’t matter
The problem with all of these tools however is
where it’s physically located, but if it’s on your
that invoking the serializer/deserializer is a man-
development machine you are limiting the scale
ual process (SubFox’s hooks do deserialize binaries
to just one developer. If you think there’s a chance
automatically when you check out files from the
you’ll ever involve more than one developer, or
Tortoise interface, so it may have potential to from
you feel more comfortable putting your eggs in dif-
a command line, but this needs further investiga-
ferent baskets, consider putting the CI Server some-
tion into the source code. The other tools could have
where else.
wrappers written that allow them to do it as well.),
You might imagine that a CI service needs to be and with Continuous Integration we need things
running on a powerful server somewhere but this to run automatically. Whichever of those tools you
definitely not the case. It can run on any machine use, some customization will be necessary in order
that has access to the source control as well as any to get them to run from the Windows shell in order
additional tools required to do its job such as the to be integrated into Continuous Integration.
Build tool. It does have to be a Windows machine
For this reason, I recommend that you do check
though, because Visual FoxPro projects are Win-
in the binaries into your version control system
dows-only and require a Windows machine to
along with the serialized version. That way they
build them.
won’t need to be deserialized when you run an
automated build, saving you this manual step.
Step 1: Use Source Control The exception to this rule is the PJX/PJT files:
Version control, while not absolutely necessary, is Do not check those into version control. Automated
preferred because CI servers have the ability to Build, in the next step, will recreate the PJX file
monitor repositories of the most popular types, from the “PJM” file that’s generated. Just be sure to
and usually a source code check-in is the trigger check in your PJM file!
that fires the entire CI process. Hopefully by now
you’ve begun taking advantage of version control!
Even if you don’t work with other developers, you Step 2: Install Automated Build
need to be doing this. This is the utility that takes your source control and
creates an EXE out of it. Visual FoxPro developers
If you don’t have an offline repository yet, just
have always had to do this manually by clicking on
search the web for any number of free/cheap ones.
the Build button in the Project Manager. Now we
In this figure I setup a free repo on ProjectLocker
also have Automated Build, another VFPX tool, to
and joined my test project to it in less than five min-
compile EXEs from the command line.
utes.
Automated Build was written by Markus
Serial Killer ­Winhard. It’s actually just one PRG, BuildProject.
One quirk of Visual FoxPro is that much of the prg along with a few supporting files. See the full
source code is stored in binary files (actually VFP documentation on VFPX, but the installation is
tables themselves) rather than text files. Binary files basically just copying the files needed to a folder.
are problematic for version control systems because Tip: You might be tempted to create an EXE so
it’s rather difficult to “diff” them. The solution for you run Automated Build without needing a full
VFP developers then as always been to serialize the VFP deployment; However Automated Build uses
table, i.e. turn it into an equivalent ASCII text file. the command BUILD PROJECT, which is unsup-
Projects that have been “Joined to Source Con- ported in the VFP Runtime. You’ll have to copy the
trol” using Visual FoxPro’s built-in functionality minimum files needed to run the full Visual Fox-
automatically get their binaries serialized via the Pro onto your CI server. This means you’ll need a
native SCCText.prg. This is an inferior program fully licensed copy of VFP for your CI server if it’s a
and has long been superseded by any number of separate machine than your development machine.
projects, including VFPX’s SubFox and SCCTextX, If you use CruiseControl.Net as your CI server,
Christof Wollenhaupt’s TwoFox, or Toni Feltman’s Automated Build has sample configuration plug-in

Page 10 FoxRockX March 2017


files that make it very easy to inject Automated- And More
Build into your CI Cycle. For Jenkins we’ll have to Continuous Integration Servers have “hooks” that
configure things ourselves as well, which we will allow you to plug-in any number of 3rd party tools.
do later when we set up a real job. Properly modified, it would be possible to integrate
tools into your software development lifecycle that
GenPJM check for “code smells”, plug into your issue track-
BuildProject takes as a parameter the name of a PJM ing system or project management, deploy the soft-
file, which is the serialized version of the project’s ware to FTP servers or Content Delivery Networks,
PJX/PJT file. If your project is joined to a source and so much more.
control provider, the PJM is created automatically
for you. If not, you will need to generate that PJM
manually. A recent addition to the Automated Final Step: Set up a Continuous
Build project on VFPX is GenPJM.prg, which will Integration Framework
serialize your project’s binary into a PJM file that Finally we have arrived at the most necessary
Automated Build can use. step of all in order to implement Continuous
Tip: Call GenPJM from a ProjectHook when- Integration, setting up the CI Server itself. I’m going
ever the project is modified, saved, or built so you to use Jenkins, a popular open-source Continuous
don’t have to remember to do it manually. Remem- Integration framework. You can read all about the
ber, one goal of CI is to automate as much as pos- interesting history of Jenkins here. Installing Jenkins
sible! on Windows is trivial, just download the .ZIP file
and run the SETUP.EXE on the machine that will
BuildProject recreates the PJX file from the serve as your Continuous Integration server.
PJM, compiles the source code, and creates output
that the Continuous Integration server can parse After installing Jenkins, you’ll have a new ser-
and act on. vice running on that machine which is setup to
run automatically when the machine starts. This
Are we done? is likely what you want but you can flip it to start
You could stop right there and just have a CI manually if you’d rather.
server that automates builds periodically or when- The service monitors a port on your machine,
ever you check in source control. That alone would which by default is 8080. Fire up a browser and
probably save you a lot of time and effort. Let’s not navigate to the servername:8080 and you’ll see the
stop there - let’s add a couple more things to really basic Jenkins dashboard (Figure 2).
create a professional development environment.
Start by configuring Jenkins to your liking by
clicking the “Manage Jenkins” link to get the con-
Step 3: Set up Unit Testing Framework figuration page (Figure 3). Notice how Jenkins can
(Optional, but Desirable)
check back with its home server and lets you know
Unit testing is often the first tool fired in the CI
when it is out of date. You can update with the click
cycle, occurring right after new source code is
of a button. Nice.
checked into the repository. A good CI server can
be configured to proceed to a build only after the On the “Configure System” screen, you’ll want
source code passes the unit tests, or rollback a to verify/change which port you’re using for Jen-
check-in that causes unit tests to fail and send the kins (Figure 4), as well as set up the email settings
developer that tried to check in the failing code a in order to receive notifications (Figure 5).
detailed report of the results.
Visual FoxPro developers have the excellent Creating a Job in Jenkins
FoxUnit framework to allow them to interactively After Jenkins is configured we can proceed to cre-
create and run unit tests while they’re developing, ate our first “job”. Jobs are somewhat analogous
but that doesn’t help us in an automated environ- to “Scheduled Tasks” in Windows (or cron jobs in
ment. Now that FoxUnit has also moved to VFPX, *nix), in that they’ll run on a schedule or when a
an effort is underway to expand its functionality specific event happens.
to allow tests to be run from the command line so For our job we’ll create a “free-style” project
they can be integrated into a Continuous Integra- (Figure 6), giving us the most flexibility since we use
tion tool. a non-standard setup. First we name and describe
John Miller wrote an excellent article for CoDE our project, and perhaps give it a few other options
Magazine that details the work you need to do in like how many old builds to keep around (Figure 7:
order to make FoxUnit work with Team Founda- Name and Describe the Job).
tion Server. Only slight modification is needed to Next we can set even more options, like a
customize it to the Continuous Integration frame- “Quiet Period”, which sets an interval between
work of our choice, Jenkins. each build. This is useful when you have quite a
few developers submitting code a lot, to prevent

March 2017 FoxRockX Page 11


Figure 2. Jenkins Dashboard

Figure 3. Jenkins Management Screen

Figure 4. Configure Jenkins Port Number

Figure 5. Configure Jenkins to send general EMail Notifications

Page 12 FoxRockX March 2017


Figure 6. Creating a new "Job" in Jenkins

Figure 7. Name and Describe the Job

the build from constantly firing every time a new you’d rather just do a build on a regular schedule
submission comes in. In Figure 8 (Advanced Job or create a build manually. If that’s the case just
options) I’ve throttled Jenkins to at most do one select “None” for source control and you can set
build every 10 minutes (6,000 seconds). your development folder as your Workspace. You
By default, the “workspace” for a build is in a will lose a lot of scalability doing things this way,
folder underneath the default Jenkins install folder but it might be all you need.
(e.g. C:\Program Files (x86)\Jenkins\jobs\<project If you checked your binaries (VCX, SCX, FRX,
name>\workspace). You might want it to be etc) into source control as I suggested, you now
somewhere else though, especially on Windows have an extra step: You have to deserialize the
machines so you don’t succumb to the virtualiza- text files back into binaries before you can do the
tion issues that come with writing files under C:\ build because VFP unfortunately can’t build based
Program Files. I prefer creating my workspaces on the serialized version (which is unfortunate
under C:\ProgramData. really, because one would think that internally
it’s reserializing the binaries before it does its
Configuring Your Source Control compilation anyway). There are a variety of ways
The next step is to tell Jenkins where your source to do that depending on how they were serialized
code is checked in (Figure 9). Out of the box (I guess but most would involve writing a wrapper around
that expression doesn’t really apply these days) whichever technique you used to serialize in the
Jenkins supports CVS and Subversion, but there first place. There is a lot of opportunity for some VFPX
are plug-ins for Mercurial, Git, and probably just volunteer work to be done here.
about any other version control system out there.
Note again that linking your CI Server to your Set Up the Build
source control isn’t absolutely necessary. If you The most important step is to actually build the
choose not to link them, you will lose the abil- EXE (or DLL) from our project. That means telling
ity to do a build automatically when source code Jenkins when to build and how to do it. Using the
is checked in. That may not be an issue for you if Build Triggers (Figure 10), you can instruct Jenkins

March 2017 FoxRockX Page 13


Figure 8. Advanced Job options

Figure 9. Source Code Management

Figure 10. Scheduling an Automated Build

Figure 11. Build manually anytime by queueing up a build with this button

Page 14 FoxRockX March 2017


Figure 12. New options available after the Jenkins Build Timout Plugin is installed

to poll the Source Control repository if you set one Once you’ve got your Build action described,
up in the previous step, build periodically using go ahead and test it by clicking on the icon to queue
a very flexible syntax, or build after other Jenkins up a build immediately (Figure 11). You should see
projects get successfully built, which is great for the “Last Success” time update automatically and a
projects that are dependent on other projects. pretty sun icon to indicate all is well. Clouds would
You don’t have to schedule the build at all. Any- indicate things aren’t going so well. Clearly the
time you want to build you can fire up the ­Jenkins developers of Jenkins aren’t goth.
dashboard and click on the “Schedule Build” icon.
That will queue up a build to begin immediately. Failed Builds
If an Automated Build fails, perhaps because of a
Tip: Hover over that icon and look at the address
syntax error or a missing file or anything else that
bar on the bottom of the browser. Because Jenkins
can cause a build to fail, it currently just sits there
is web-based and has a well-written API, you can
and doesn’t exit properly. Markus is aware of this
use that same URL to invoke a build (e.g. http://
problem and we’ve spent some time trying to fig-
(CI_Server):8080/job/(Project)/build?delay=0sec).
ure out quite why it’s happening, but it turns out
Just add a button to your VFP Developer Toolbar
there’s a nice workaround in Jenkins for this so it
(which is very easy if you use Thor) that calls that
doesn’t break things at all.
URL (simple with a RUN /N CURL.exe or either
WestWind IP Stuff or VFP2C32) and you can have If you go back to the root of the dashboard
a build fire at the push of a button. and choose “Manage Jenkins” and then “Manage
Plugins” (or type http://<CIServer>:8080/plugin-
The last step in scheduling a build is telling
Manager/? into your browser’s address bar) you
­Jenkins how to do the build (again, Figure 10).
can select from over 300 plug-ins currently avail-
Here we’re using the full version of VFP (because
able for Jenkins. One of them is called the “Jenkins
we need to use the BUILD PROJECT command) to
Build Timeout Plugin” and it’s just what we need.
call automated build (BuildProject.prg). The syntax
Install that plugin to add a new option on the Job
for BuildProject is
Configuration page called Build Environment (Fig-
BuildProject.prg PJMFile [ManifestType, EXE ure 12).
Name]
The plugin is nicely configurable, allowing us
PJMFile is the name of the serialized PJX file to specify an absolute time to abort the build, an
created with either GenPJM or our source control elastic time based on how long previous builds
tool if you’ve joined your project to source control. took, or a “likely stuck” time based on some algo-
ManifestType is either ADMIN or USER to rithms built into the plugin. Here I’ve chosen to say
indicate the execution level (default is USER). “Abort the build if it takes more than 150% of the
time that the last 3 builds took; if there haven’t been
3 builds yet then abort after 2 minutes.”

March 2017 FoxRockX Page 15


Now if I introduce an error into my code and Code Analyst
try to build, I’ll get a cloudy icon indicating all is This VFPX tool runs a “sniff test” on the code to
not well, but Jenkins will not hang. You can find ensure it conforms to standards and is well-written,
the .ERR file in the workspace folder to see what’s based on built-in rules and rules you create. Cur-
causing the problem (Figure 13). rent rules included with the project check your
code for problems such as:
Adding Build Actions • Are there any RETURN statements with-
Now that your build is scheduled and executing in a WITH /ENDWITH block?
properly, you can add more build actions. The • Are you using CTOD() anywhere? (This
new actions can be sequenced either before or after doesn’t internationalize well)
the actual build by dragging the actions around • Does each function have a RETURN?
the screen. There are over 300+ plugins available • And much more, plus you can write your
currently for Jenkins, but very few are applicable own rules.
to Visual FoxPro. Though we are a bit limited as
compared to some of the other development tools Like FoxUnit, Code Analyst will need a wrap-
right now, though perhaps we can use VFPX to get per in order to make it usable from a CI Server, but
our tools up to snuff. Here are some ideas for build we have the talent in our community to do this.
actions:
Deployment Tool
E-Mail Notification This is the tool that copies your EXE to wherever it
Jenkins includes functionality to send emails when needs to go. It may be as simple as copying it onto a
a build fails (Figure 14.). This is useful when you’ve shared folder or as complex as creating a variety of
scheduled your builds to run automatically and Setup executables for different platforms, upload-
aren’t keeping your eyes on the dashboard. ing them to a secure FTP site, and updating a web
page with links that points to the latest versions.
Unit Testing Continuous Integration servers usually come
Ideally you’ll want to run your unit tests before you with functionality to deploy your EXE in the most
actually build, and abort the build process if any of common ways, but you may have unique needs
the unit tests fail. But FoxUnit, the de facto unit test- that require you to use something special.
ing framework for Visual FoxPro, was not created For example you might run an InnoSetup script
with Continuous Integration in mind. It’s not call- that bundles your files into a SETUP.EXE with auto-
able from the command line to execute unit tests, updating ability. That would be nice and fairly easy
and it cannot create the types of “console applica- to integrate into CI, but it’s beyond the scope of this
tions” necessary to write to STDOUT, so we’re in a whitepaper. See Rick Borup’s whitepaper from a
bit of a quandary. few years ago on using InnoSetup with Visual Fox-
Fortunately John Miller has started us down a Pro, and then just add the Inno script as a Build
road that will take care of this problem. Check out Step after the project is built to create the SETUP
the code in the article referenced above. I haven’t file.
quite gotten it working perfectly yet myself and so Once you’ve include a deployment step into
I can’t write as much as I’d hoped to on this topic, your CI cycle you’ve gone beyond Continuous
but I would welcome any contributions to the Fox- Integration into the realm of Continuous Deploy-
Unit project in this direction.

Figure 13. A Failed Build

Figure 14. Send email when a build fails

Page 16 FoxRockX March 2017


ment. This is a logical step to automate and it cre- c­ reate console apps, the binary source code, and the
ates some more possibilities and issues, such as lack of a native deployment tool create difficulties
where to deploy nightly builds vs. stable builds. for the developer. However given some patience
and a few workarounds, we can take advantage of
And More Continuous Integration.
Other development environments may include I’ve attempted to use as many VFPX projects in
other checks to ensure code integrity before a proj- this session as possible, exposing opportunities for
ect is deployed – tools for which no native VFP solu- improving the utilities. I implore you, dear reader,
tion exists. That means there’s room for ­creative to get involved. Throughout this whitepaper I’ve
folks like you construct the tools, or customize an indicated several places where VFP could use a
existing tool to do the job for us. These tools might little more help to make Continuous Integration
include: useful and easy for every programmer.
• Integration Testing: These are tests that I hope the concept of Continuous Integration,
make sure that your code works well and the specifics of how to implement it into your
with others, like the database, the web Visual FoxPro Software Development Lifecycle, are
services, the mail server, etc. I’m not much clearer to you for having read this whitepa-
aware of any frameworks that are made per and/or attended my Southwest Fox session.
specifically for FoxPro but many are This is a necessary process for serious developers
adaptable. It would also be acceptable, to integrate into toolbox, because like any good tool
though perhaps not “pure,” to write Fox- it will make you more efficient and reduce errors.
Unit tests that are actually not strictly
Happy coding, and may your continuous inte-
unit tests but also integration tests.
gration cycle forever be unbroken.
• Functional Testing: These are tests for
the User Interface of your system, rather Dedication
than the business logic which is what More details about the individual VFPX tools used
Unit Testing is aimed at. in this whitepaper are available in the recently pub-
• Load Testing: Tests how your applica- lished book VFPX: Open Source Treasure for the VFP
tion would fare when many users are Developer. Many thanks to the authors of those tools
hitting it at once. These sorts of tests for creating the parts we needed to put together
spin up multiple instances of your app, this entire system.
often on multiple machines, to simu- John Miller was way ahead of his time, writing
late real-world conditions. It’s amazing about pretty much this very topic way back in 2007.
how some apps that work fine when the I came across his article when I was writing about
developer is testing things don’t perform Automated Build for the book mentioned above,
as well under load. but ironically had already forgotten about it when
• Coverage Testing: These tests simulate I was writing this. It was while rereading my own
paths through your code to see which chapter in the VFPX book that I stumbled across
code is getting nailed and which code my reference to his earlier article. This whitepaper
may not be touched at all. They perform restates, probably less elegantly, most of the same
something similar to what would hap- points he made way back then. For more informa-
pen if you SET COVERAGE TO a file in tion about this topic I encourage you to read his
the beginning of your application, ran it article.
all day through every code path possible,
and then ran the coverage analyzer after-
wards. Author Profile
Eric Selje is a software developer in Madison, WI. He has
• So Much More: Take a look at the plugins been programming in Fox since FoxBase 1.21 in 1986 and
page of Jenkins to see the plethora of recalls eagerly waiting for the box from UPS with "FoxPro".
options available to you. Want to Tweet In 1995 he founded MadFox, the Madison, Wisconsin, FoxPro
when your build succeeds? Fire a growl User Group, after attending DevCon in San Diego and real-
notification? Make an entry in a data- izing that learning Visual FoxPro 3 would take a group effort.
base? Run a StyleCop on your HTML Eric works as Salty Dog Solutions, LLC, developing business
code? There’s so much more you can applications for the desktop, web, and most recently mobile devices
do with an extensible CI framework like with an emphasis on database-driven applications. He is also co-
Jenkins. author of VFPX: Open Source Treasure for the VFP Developer.
Besides FoxPro, Eric loves playing Ultimate frisbee and baseball,
Conclusion solving crosswords, and reading all genres of books. To make
Attempting to integrate your Visual FoxPro solu- dependencies even more obvious, you could maintain properties
tions into a Continuous Integration process exposes that keep references to every other object. The code then becomes
the limitations of Visual FoxPro: the inability to

March 2017 FoxRockX Page 17


The key is, if you want to avoid hidden depen-
dencies and invert the control of dependency, you
Continued from Page 24 have to avoid the following common approaches:
With a service locator you solve two problems: • Create an instance of a named class in
There is no need, anymore, to launch the entire code
application environment with all its dependencies. • Access global objects unless there’s
Rather, you can launch the required parts at the an isolated technical reason such as in
time they are required. menus and reports
Moreover, if you now are faced with the • Rely on any other file
requirement to test the code above, you can easily Instead you need to
provide a different service procedure in your test
• Provide only few channels to let objects
program file. Local procedures always have pre-
communicate with each other
cedence over external procedures. This allows you
• Spell out dependencies by using object
to alter the behavior of the service locator without
references in each object
changing the code in the class. You can now easily
provide mock up implementations of all required
services, which also makes the unit test act as a doc- SOLID for VFP developers
umentation of all dependencies. Not all of the SOLID principles are equally suited
LOCAL loCustomer, loInvoice for VFP developers. We need to be aware that we
loCustomer = This.oCustomerFactory.New() don’t have strong typing and therefore can’t worry
loCustomer.Load ("ALFKI") about interfaces. We should keep the relative cost
loInvoice = loCustomer.FindLatestInvoice()
If loInvoice.IsOverdue() ; in mind when we create empty abstract base classes
and loInvoice.IsNotPaidInFull() or use protected properties. Too many objects can
This.oReportService.Print( ; have a negative impact on VFP, so we clearly don’t
This.oUser.GetReportPath()+; want to use as many objects as a C# developer.
This.oReportRepository.get("WHATEVER")
) Also, the syntax in VFP isn’t as concise as the
Endif .NET one. We can’t chain method calls. Creating
instances requires a lot more coding. We there-
Now all references are immediately obvious fore have to strive for a balance between reducing
by looking at the class definition. There are several dependencies between objects and keeping our
possibilites to actually fill those references. source code readable.

DOWNLOAD

Subscribers can download FR201703code.zip in the SourceCode sub directory of the document
portal. It contains the following files:

doughennig201703_code.zip
Source code for the article “Lessons Learned in Version Control, Part 2” from Doug Hennig

pradipacharya201703_code.zip
Source code for the article “Seamlessly Read/Edit/Save numeric data of variable precision”
from Pradip Acharya

christofwollenhaupt201703_code.zip
Source code for the article “With abbreviations to success” fromChristof Wollenhaupt

Page 18 FoxRockX March 2017


With abbreviations to
success
Christof Wollenhaupt

TLAs put into Practice: Object Andy Kramek repeated this over and over in his
sessions about OOP in the 90ties. Many develop-
Oriented Principles in VFP ers have followed this rule, yet, most Visual FoxPro
TDD, OCP, LSP, ISP, DIP, DRY, IoC, LOC, SRP, code violates the Single Responsibility Principle
DbC, CDD, DDD: Our world is filled with three in dramatic ways. SRP applies to the class, not the
letter acronyms that are meant to make our lives method. Let’s look at typical examples:
easier, if only we could remember what each of
Define Class Customer As Custom
them means.
Procedure MoveNext
* moves the record pointer to the next
TLAs explained * customer record
In this article we cover many of the acronyms used EndProc
in object oriented programming, especially in clean
Procedure IsValid
code development. The purpose of this section is
* validates customer data before saving
to act as a reference which you can consult from * them
time to time to refresh some of the principles and EndProc
approaches. You should be able to read each section
Procedure GetInvoices (tnYear)
just by itself.
* Returns a collection of invoice
* objects for the current customer and
* the requested year.
SOLID EndProc
SOLID is neither a principle, nor a three letter
abbreviation. Much like LISA G. in the Visual EndDefine
FoxPro community, SOLID acts as a shortcut for
remembering the order of events during launch- In many frameworks - not just in the Visual
ing a form (Load, Init, Show, Activate, GotFocus). FoxPro world - but also in Java, .NET and other
SOLID’s main purpose is to reinforce some of the languages, business objects act as a dumpster for
more important principles. Those are: about almost everything that is not data related or
• Single Responsibility Principle (SRP) clearly UI. Everything that is loosely related to the
customer or the technical representation of the cus-
• Open-Closed Principle (OCP)
tomer in our application is put into the one single
• Liskov-Substitution Principle (LSP) class called the customer business object.
• Interface Segregation Principle (ISP)
So what’s wrong with that?
• Dependency Inversion Principle (DIP)
Quite a bit, actually.
Certainly I don’t need to point out that the first Can you imagine placing an order on the phone
letter of each principle adds to word SOLID. They and having the operator ask you: Does this order
are important principles, yet not the only impor- exceed your order limit? Probably not. Yet, when
tant ones. Principles such as SoC and DRY play in we model this process in software, we don’t think
the same league as those five. Obviously, Robert C. twice about adding a GetCreditLimit method to
Martin - who came up with the name SOLID - had our customer object. In the real world, we wouldn’t
to pick those that fitted. ask the customer to provide all invoices we have
sent him in the past year. In our software world
SRP - Single Responsibility Principle that’s however often a key function of a customer
To quote Robert C. Martin: business object.
A class should have one, and only one, reason to change. What do a nail and your maintenance budget
A variation of this principle has been known have in common? Both are required to keep your
to the Visual FoxPro community for some time: A house intact. Normally, though, you wouldn’t
method should do one thing and one thing only. think of them as going together.

March 2017 FoxRockX Page 19


In the sample object above we are mixing dialog is OK for those master data forms, but not in
exactly those two. There’s a record pointer that is the order entry dialog, and so forth. Customers can
get moved and invoices that are retrieved. That’s be creative about what they want.
mixing abstraction layers as well as mixing respon- We can continue, but there are always requests
sibilities. that do not fit nicely into these one-to-one or one-
Even more frequently we see this kind of mix- to-many concepts we like to create. If you can’t
ing responsibilities with the form object and the fulfill those requests, it’s not because it’s an unrea-
form class - which are kind of used interchangeable sonable demand from the customer, rather because
in VFP. by merging responsibilities into a single class, you
In 20 years of doing Visual FoxPro consulting increase coupling and decrease flexibility.
virtually every framework that I encountered had Data Validation, Navigating in a data source,
a set of form classes. Mostly they start with a base handling dependencies between input controls on
form, continuing with two branches into dialogs the form, maintaining state, checking permissions,
and edit forms. Edit forms are further split into data initiating reports, showing message boxes, all of
entry forms, one to many forms, and so one. These these are separate responsibilities that belong into
forms have methods like Next(), Print(), Save(), separate classes.
Validate(), etc. that can be called by controls on the If you do more than one thing in a class, the
form or the toolbar. There are properties to specify way you implement one thing impacts the way you
the alias or business object of the main data source. implement other things. Take refreshing the form
Common advice on using these classes usually as an example. You need to do this when moving
goes along the lines of: Add the control you want. the record pointer, when picking a value to show
Then immediately add a method to the form to per- related detail information, when changing data due
form the action and call this method from the Click to validation rules. Because all of this code resides
event (or whatever event we are talking about). more or less directly in the form, there’s no hesita-
Never put code into the Click event. Often this con- tion to directly call the Refresh method.
tinues on how naming conventions are important But what if you later realize that calling Refresh
for form methods and how you should use com- takes a lot of time for some controls because they
mon prefixes to group methods together in the initiate a server lookup over an ODBC connec-
Properties Window that belong together. tion? Now you can’t simply replace Refresh han-
Can you see what’s wrong with this? dling with an optimized version that keeps track
Classes are not procedure libraries that do all of which elements have changed and which have
kind of stuff. They are not units of code that keep dependencies without testing and changing all of
similar methods together. Classes have a purpose, those responsibilities, as well.
have one purpose.
What’s the responsibility of a form? OCP - Open-Closed Principle
A form hosts and groups a number of visual Robert C. Martin defines OCP as
controls in order to let the user interact with them Software entities (classes, modules, functions, etc.)
as a single unit. should be open for extension, but closed for modification.
It’s a visual control. Most controls are visual The basic idea behind this is that you write
controls. Their only purpose - hence their only code, test it and use it. Period. When you need new
responsibility - is to let the user interact with the features you write new code in a new class, test it
application. A form captures and displays informa- and use it.
tion. That’s what a form does on paper, that’s what You don’t modify the original class to add the
a form class does in Visual FoxPro. new feature. Admittedly, this is a principle that is
Every other responsibility has no place in the quite difficult to follow all the time, especially in
form class itself. If a form checks user permissions, Visual FoxPro where classes are no first-class citizens.
validates input, navigates in a table, returns bits In the context of the Open-Closed principle
and pieces of status information, these are all sepa- modifications are changes in the way the class
rate responsibilities. should operate. Commonly those are caused
In most but the simplest applications you will by new requirements. We are not talking about
run into limitations if you place every responsibil- bug fixes and refactoring. The code in a class can
ity onto the form. Can’t you show a second cus- change, should change and must change over time.
tomer on the same form? I want to edit some other The purpose, the responsibility, the interface of a
information on this form, as well. Can’t you show class shouldn’t change, however.
related information on this form, but not on that If you take out your phone and look at it, what
form. Of course, I want to edit data in a row in the you see is quite different from the following pic-
grid. I do it all the time in Excel. The confirmation ture:

Page 20 FoxRockX March 2017


tion you are currently working in. If the same class
is used in other applications, it’s easy to miss those.
When classes are used by a team or an even larger
group of customers, you as the developer simply
don’t know in which contexts this class is used.
Every assumption about safe changes is a risky
one in this case.
The Open-Closed principle favors clear abstrac-
tions and clear hooks. That’s easier done in C++,
C# or Java where you have interfaces and abstract
classes, where you can implement more than one
interface, and where the compiler validates your
code at the earliest possible time.
Source: Flickr/Terekhova However, even in VFP you can identify the pro-
cesses that are likely to change and provide exten-
Can you remember the day this phone turned
sion methods for them. This is obviously much
into your brand new iPhone 7 or a Samsung Galaxy?
easier when your class design follows the Single
I can’t, because those changes came slowly.
Responsibility Principle and you don’t have hun-
One day the dial wheel was replaced with keys, dreds of properties and methods to wade through.
the wire disappeared, microphone, speaker and
dial pad moved into one single device, recharge-
able batteries got added, a single line display was LSP - Liskov-Substitution principle
added, a basic phone book was introduced, the Robert C. Martin explains it like this:
time was shown some day on the display, missed Functions that use pointers or references to base classes
calls and caller ID became available, a multi line must be able to use objects of derived classes without
display was added, menus replaced the program knowing it.
key, new networks where established, small games Obviously, in Visual FoxPro we don’t deal with
where added, as was texting, PDAs merged with pointers, just with references.
phones, MP3s were invented, memory was added The Liskov substitution principle is almost
to phones, videos, internet, browsers, keys dis- always learned to late. When developers start
appeared, pens disappeared, GPS sensors were learning object oriented programming and design,
added, touch screens... there’s usually a heavy focus on inheritance. Deep
Every change was just a minor enhancement inheritance hierarchies are a clear sign for early
over the previous version. If you kept up with tech- designs. At this stage developers usually miss the
nology, every step was easy to learn and came more experience of creating good inheritance structures.
or less natural. Now imagine you had never seen a One might say that the theoretical background is
phone since the early 70ties and were handed an still missing. Or, developers haven’t failed often
iPhone to make a call. enough.
Would you realize that this isn’t the remote for You need to discover a problem to appreciate
your garage door? Would you figure how to unlock solutions that others have found before you.
and dial? Would you wonder why you should eat Later on when developers would be ready to
without feeling hungry when people keep talking apply the Liskov Substition principle more fre-
about menu options? quent, they usually shifted to a composition based
Changes to classes are just like this. approach with fewer sub-classes.
You change a little bit here, a little bit over Essentially, the LSP states that a sub class
there. Add a new method, expand on the param- should behave exactly like the parent class. This is
eter list. A few DO CASE here, support for a new much easier in software development than in real
feature in VFP there. Unless you rigidly test your life where sub classes (kids) usually do the opposite
changes every time, you most likely end up with of their parent classes.
a class that wouldn’t work unchanged with other This seems like an obvious consequence of sub
classes you wrote initially together with this class. classing. After all, you are merely specializing the
The problem is that every change seems to be behavior of a class and inherit the default behavior.
backward compatible, but that’s usually only true New behavior is added but existing not altered. So
in the context of your current focus. If those “non- why make this a rule, and why should it be such
breaking” changes require a tweak in one class and an important principle that it warrants to be in the
another SET CLASSLIB statement in the main pro- SOLID group of principles.
gram, you just add it at this time... in the applica-

March 2017 FoxRockX Page 21


Yet, even in VFP base classes you can find vio- DIP - Dependency Inversion Principle
lation of this principle. Take the form class’ Show Robert C. Martin puts this principle in rather
method as an example. The default behavior of this abstract terms
method is to show the form and continue to run. High level modules should not depend upon low
Now change the WindowType property in a level modules. Both should depend upon abstractions.
subclass and call Show again. Suddenly program ­Abstractions should not depend upon details. Details
execution stops and when execution continues on should depend upon abstractions.
the next line the reference is gone. Of all SOLID principles I found the Depen-
Typically you won’t violate this principle dency Inversion Principle the hardest one to teach
because you change the behavior of the sub class. A in many years of being a consultant. There’s some-
more common reason is limiting functionality. You thing in Visual FoxPro that inherently screams:
might create a toolbar that has a print button. On Violate the Dependency Inversion Principle! Just
every form it calls a print method of some object ignore it! DIP can’t help you, it’s evil.
that in turn prints a default report for whatever Well, that’s a bit of an exaggeration... It’s a fact,
data is currently available. On some forms, you though, that most traditional VFP applications vio-
might realize that there isn’t a single simple report. late this principle.
So instead of printing a report, you either don’t do
anything at all, or display a dialog where the user When one learns writing programs, developing
picks a report. Suddenly, the same method is show- applications, the intuitive approach is a top-down
ing different behavior. approach. You have a problem, a big problem. To
solve this problem, you break it up into smaller,
It’s not a major issue on such high level code. more manageable sub-problems. Rinse and repeat
However, if you work on data related object where until problems are small units that you can solve
in one case transactions become too complicated, easily.
so you just override methods in the class that
deal with transactions, or with the code to revert It’s an obvious approach, it’s a required
changes, you also introduce subtle differences that approach. As humans we need to break up big
work in this particular instance, but will bite you if problems, because we can only deal with so much
you need to extend the scope of the class. at a time. Huge complexity in great detail is not
what our brain was made for. How do you eat an
elephant? One bite at a time. It’s the most common
ISP - Interface Segregation Principle answer, but it’s wrong. That’s not what humans
If there’s a principle that VFP violates is the Inter- would do (assuming we would actually eat ele-
face Segregation Principle: phants).
Clients should not be forced to depend upon interfaces Instead we would dice and slice the Elephant
that they do not use. into smaller parts. We would separate it into good
Visual FoxPro classes have dozens over dozens of parts and bad parts. We would have parts that are
properties and methods. Most of them you never meant to eat, parts to throw away, parts to use for
use. Some of them you use in the designer but something completely different, such as the teeth.
never at runtime. Some you use to move the focus, We would modularize the elephant and then han-
some perform validation, some control behavior, dle modules one bite at a time.
some control instantiation, some handle data con- Modularization, breaking up, are such inher-
versions, some handle color, fonts, size, some deal ent activities that we hardly notice when we break
with managing members, the Tag property is there one thing into several, even less think about how
even “just in case”. and why we do it. We tend to realize modulariza-
If you can’t split responsibilities into multiple tion only through the lack of it. When you have
classes, or if a responsibility requires a different a huge problem and don’t know how to tackle it;
interface depending on which side you are acting when emotions seem overwhelming; when you just
from, then at least you should strive for coherent don’t know what to do. Our top-down approach to
interfaces. development is visible all over the place. This struc-
In .NET and Java we can define interface and ture thrives the way we structure everything else.
implement them separately. In VFP we don’t We create projects, neatly organized into folders by
have this option. Adding layer over layer in VFP type. We group classes in class library or program
wouldn’t help either. But at least try to make sure files. We break up classes into sections of proper-
that naming and documentation make clear which ties, public methods and private methods.
members belong to which purpose of the class. Our application begins with a main program
that launches a menu, logs on the user, instanti-
ates a form, creates an application object. Our user

Page 22 FoxRockX March 2017


interface is separated into parts that allow the user In compiled languages such as .NET, C++ and
to interact, the menu, toolbars, containers. Each of Java we introduce a dependency by using a type. In
them has action items, such as menu items, but- C# this could look like this:
tons, links. Each of those runs a module or a form. Customer aCustomer;
The form breaks up into a UI, a dataenvironment, a aCustomer = ... ;
business layer. We have a main business object and getting the value from somewhere;
subordinate objects. Validation starts at the top and Invoice inv = aCustomer.GetLatestInvoice();
if (! inv.IsPaidInFull && inv.IsOverdue)
uses smaller building blocks to perform validation. ReportService.PrintSomething();
Most structures we look at start with the big
picture and break down to the details. We hard-code references to Customer, Invoice
What is good for some parts such as designing and ReportService objects. Most likely, those
a project or structuring the user interface, is a truly depend on further classes such as a database
bad concept for designing code. connection. Some of those are initialized during
startup, some are only used when you call certain
The form depends on the business objects, methods, or upon certain conditions. The point is:
depends on global manager objects to validate You just don’t know.
security access, depend on communication paths
to show dialogs or update status information. Busi- As long as you execute this code only in the
ness objects depend on data objects. Data objects context of your application, it’s merely an inconve-
depend on configuration. Error methods depend nience to always have to run the application, log
on logging classes. on, open the customer search form, enter a cus-
tomer name, search, open the customer and click
To see this in action, open the project you are on whatever button executes the code above.
currently working on, pick the most complex form
in your application, switch to the command win- Being a developer you likely have found cool
dow and run the form. short cuts to make this process much faster. If a
TESTMODE.TXT file exists, you are automatically
Bets are high that you either receive an error logged on as an administrator into your applica-
because the entire application infra-structure isn’t tion. An auto-run feature in your application auto-
available, or that behind the scenes the entire appli- matically launches the proper form. Searches can be
cation launches including logging you on and dis- saved and easily retrieved with a button, whether
playing a menu. the user requested the feature or not, forms are
A second example: restored when the application restarts.
Take an empty project and add the main pro- Remember though: If it’s cool, fix it!
gram. Now recompile. If your project is organized Adding cool stuff to the application as a short-
as most Visual FoxPro projects are, you should cut to make development easier for you is the
end up with a project file that has pretty much all wrong solution for the wrong problem. It’s prone
required files in it or is at least prompting you for to introduce security risks, adds additional features
those files. to the app that need to be supported and clutters
Both is only possible, because the entire code the user interface with functions that you need, but
structure is organized in a top down way. Or, as the user doesn’t.
Robert C. Martin put it: Your high level modules But I disgress...
depend on low level modules and abstractions upon
details. In other words: The application design vio- If you look at the code from a Visual FoxPro
lates the Dependency Inversion Principle. point of view, the problem virtually becomes non-
existent. Variables can hold any reference, not just
Dependency Inversion is a strange term if not a specific type. GetLatestInvoice can return any
explained. In real life we see dependency inversion object. As long as it has the required properties, the
when parents depend on their kids from a certain code continues to work.
age on. However, that’s not what we’re talking
about here. More literature is written for C# and Java
developers than for FoxPro developers. If you see
If a class, an object, or a module needs some a discussion about code dependencies, you likely
other resource to fulfill its primary purpose, than see hard coded types and typed variables in most
we call this a dependency on this resource. Just to examples followed on why this approach is so
make this clear here: A resource doesn’t necessarily problematic.
have to be a class or an object. It can be an external
file, a database, or in the case of VFP even the value Then you read about interfaces being the solu-
of a setting or a public variable. tion, because now you can separate the interface
and the implementation. You smile and keep read-
A top-down approach makes these resources ing. FoxPro is smarter than C# or Java. You don’t
available very early on.

March 2017 FoxRockX Page 23


need no interfaces. All of this overhead isn’t neces- The problem with using goApp is twofold.
sary in Visual FoxPro. Any object will work, you First, we rely on an object being available. Our
can even add properties at runtime. One sure can’t code fails if the goApp object isn’t there. Second,
beat flexibility... Interesting problem, the Depen- we hide dependencies this way. If you look at the
dency Inversion Principle. But it doesn’t apply to public interface of the class, or at all properties and
you, just to strictly typed languages. methods in the properties window, you can’t see
Wrong. on which object the class depends. Does it make
user of goApp.oErrorLog?
Just because Visual FoxPro doesn’t introduce
the same dependencies as C# and Java, doesn’t Does one of the parent classes use this object?
mean that they exist. Let’s look at this code: You need to go through the code in the class,
LOCAL loCustomer, loInvoice the code of all objects being called, the code of all
loCustomer = CreateObject("BizCustomer") parent classes of this object, the code of all parent
loCustomer.Load ("ALFKI") classes of all objects that are called... In the end, you
loInvoice = loCustomer.FindLatestInvoice() pretty much need to wade through the entire appli-
If loInvoice.IsOverdue();
and loInvoice.IsNotPaidInFull() cation to find out whether there are further depen-
goApp.ReportService.Print(; dencies when you run this code.
goApp.User.GetReportPath()+"\whatever.FRX")
So what’s the solution? First of all, forget
Endif
goApp, forget global application objects. If you
There’s a hard coded business object being want to reduce the dependencies of objects you
instantiated here. One can safely assume that this must minimize the amount of possible interfaces.
business objects somehow accesses a data base. As You also must spell out all dependencies clearly in
there’s no mention of a database connection in this the class definition. There are several approaches
code snippets between obtaining an instance of the to this.
customer object and first using it, we can conclude One is the service locator. This is what Java
that the data access layer is somewhere hidden does in its J2EE implementation. Instead of relying
within the customer object but we don’t know how on multiple objects, all objects only rely on a single
exactly. object, the service locator. Such a service locator
We also have a dependency on a global appli- has a very simply and clear interface. Usually, its
cation object and its report service object. only operation should be to request a service object.
With a service broker, the code above becomes:
There’s another dependency on the user object
to figure out the path. And finally, a bit hidden, loFactory = Service("CustomerFactory")
loCustomer = loFactory.Create()
there’s a dependency on a separate FRX file. No loCustomer.Load ("ALFKI")
matter whether this file is embedded in the EXE, or loInvoice = loCustomer.FindLatestInvoice()
external on disk, it’s external to the class definition If loInvoice.IsOverdue() ;
and therefore a dependency, either way. and loInvoice.IsNotPaidInFull()
loReportSevice = Service("ReportService")
Whether you have a strict typed variable, or loUser = Service("UserService")
you refer to some external resources you introduce loReportRepository = Service(;
a dependency in both cases. As a FoxPro developer "ReportRepository")
loReportService.Print( ;
a global object has long been touted as a way to loUser.GetReportPath()+;
decouple an application. loReportRepository.GetName("WHATEVER")
Instead of relying on a goUser object, we have )
Endif
this mediator object goApp that provides us with
an oUser property containing the user object. We You still have dependencies on other objects,
can always replace this user object with a different but those are now channeled through a single ser-
one, if we wish. Hence, we decoupled the code and vice locator that is accessed by a service function.
achieved true separation of responsibilities and
If you provide service brokers - which is another
flexibility.
name for service locator - do not make them an
Nope. object. Then you need to know how to instantiate
We didn’t. We merely replaced a single name the object which isn’t as easy it might sound when
goUser with a compound name goApp.oUser and your application consists of several EXE, external
thereby violated the Law of Demeter and intro- FXP files or a lot of EXECSCRIPT snippets. A pro-
duced another dependency. We now need two cedure is a better solution in this case.
objects instead of one that are tightly coupled. A
goApp object might be seen as a mediator object,
but it’s in fact a so called God object.
Continues on Page 18

Page 24 FoxRockX March 2017

Anda mungkin juga menyukai