Things to consider when deploy a xbase application

José Luis Sánchez
Harbour Magazine
Published in
5 min readOct 29, 2018

Deployment is the activity of installing an application at our user’s computer. Once we have tested our program and consider that it is ready for our user to use, we must proceed to install it on their PC. At this moment there are different ways to carry out the deployment, which vary depending on the type of software that we make. If you want to read more on the subject I recommend the entry from JoelonSoftware titled FiveWorlds — https://www.joelonsoftware.com/2002/05/06/five-worlds/.

In my case, I develop ‘ shrinkwrapped’ software, applications that the user installs or updates on their own, and I have no way to access the user’s PC, so the deployment takes on great importance. I can’t allow a user to update a program and an error occurs, so I have to perform a series of actions within the program to ensure that the update is performed correctly. This is not part of the deployment itself, but they are very important aspects when you need your program not to crash after an installation.

1.- Check that the data files exist.

The first aspect to consider is that all the data files of the application exist in the predetermined path of the program. For that reason when starting our program we must do this check, and in case that some file is missing proceed to create it. A simple way is the one that I show next:

METHOD CheckFiles() CLASS tApplication
LOCAL i := 0
LOCAL nLen := 0
LOCAL aFiles := { "familias.dbf", "familias.cdx", ;
​ "productos.dbf", "productos.cdx", ;
​ "tickets.dbf", "tickets.cdx", ;
​ "lineas.dbf", "lineas.cdx" }

// compruebo que están los ficheros
nLen := Len( aFiles )
FOR i := 1 TO nLen
​ IF !File( ::cDbfPath + aFiles[ i ] )
​ Ut_Actualizar()
​ Ut_Indexar()
​ EXIT
​ ENDIF
NEXT

Later I will explain what functions Ut_Update() and Ut_Indexar() do, but basically they check that the files have all the fields and generate indexes again.

2.- Check if the version of the program has changed.

Together with the verification of the previous point, I check that the version of the program has changed. The versions of my programs always have the following form: “x.y.z” where:

  • x is the major version. When I introduce functionalities that suppose to add tables to an application I increase the major version.
  • y is the minor version. When I release an update I always increase the minor version, and when this update means to add or modify a field of a table I increase the ten of this number, passing for example from 1.1.a to 1.10.a.
  • z is the bug fix for the version. I don’t add functionalities, I only correct errors of the current minor version.indica la versión menor.

When I change the major version or the ten of the minor version I call Ut_Update() and Ut_Indexar().

3.- Create data tables from code.

We arrive at the most important part of the post: you must have coded the definition of tables of your application, so that it can create them or modify their structure whenever you need it. Keep in mind that we are talking about shrinkwrapped software, in that it is not possible that you go to your user’s PC to modify the structure of a table using a DBU type program.

My Ut_Update function does it this way:

// productos
oSay:SetText( 'Fichero de Productos' )
dbCreate( oApp():cDbfPath + 'pr', { ;
{ 'PrNombre', 'C', 40, 0 }, ; // Nombre del producto
{ 'PrFaNombre', 'C', 40, 0 }, ; // Nombre de la familia
{ 'PrPrecio', 'N', 6, 2 }, ; // Precio de compra
{ 'PrIVA', 'N', 5, 2 } } ) // Precio de venta
CLOSE ALL
use &( oApp():cDbfPath + 'pr' ) new
SELECT pr
IF File( oApp():cDbfPath + 'productos.dbf' )
DELETE file &( oApp():cDbfPath + 'productos.cdx' )
APPEND from &( oApp():cDbfPath + 'productos' )
dbCommitAll()
dbCloseAll()
DELETE file &( oApp():cDbfPath + 'productos.dbf' )
ENDIF
dbCloseAll()
rename &( oApp():cDbfPath + 'pr.dbf' ) to &( oApp():cDbfPath + 'productos.dbf' )

What I do is create the table with the name of the alias that I will use with it, I incorporate the data of the real table and then delete it, and finally I rename the file that I have created with the alias to the real name of the table. This for each one of the tables of my application. When I have to modify a field of a table I do it in this function, so that when I start the program again the tables are modified automatically.

Once created the tables what I do is to create the indexes on them. As you can see, I deleted the index file before incorporating the data of the real table, so now I have to create it.

// productos
dbCloseAll()
IF File( oApp():cDbfPath + 'productos.cdx' )
DELETE File ( oApp():cDbfPath + 'productos.cdx' )
ENDIF
Db_OpenNoIndex( "productos", "pr" )
oSay:SetText( i18n( "Fichero de productos" ) )
oMeter:SetRange( 0, LastRec() / nPaso / nPaso )
PACK
INDEX ON Upper( prnombre ) TAG pr01 ;
FOR ! Deleted() ;
Eval ( oMeter:SetPos( nMeter++ ), Sysrefresh() ) EVERY nPaso
INDEX ON Upper( prfanombre ) + Upper( prnombre ) TAG pr02 ;
FOR ! Deleted() ;
Eval ( oMeter:SetPos( nMeter++ ), Sysrefresh() ) EVERY nPaso
UtResetMeter( oMeter, @nMeter )

4.- Controlling all aspects related to the modification of table fields

It is obvious that if you have included a new field in a table, it will appear in one of your forms. But you must also show it in your data grids prior to editing a record, or include the new sort in the grid in which you show the table. And take into account your classes that show data.

In my programs I use the interface that I have called Full Single Document Interface http://www.alanit.com/?s=fsdi, and one of its functionalities is that I save the configuration of the data grid of each maintenance. If I add a new field, this field is not shown in the grid, because when I saved the configuration that field did not exist. That’s why every time I add a field — I modify +10 the minor version of my application — I have to delete the stored configuration from the grid to show all the fields of it.

If in your application you do more things related to the deployment of your application, I would appreciate it if you did not explain in the comments.

--

--