Aspectos a tener en cuenta en el despliegue de una aplicación xbase

Esta entrada se publicó originalmente en Harbour Magazine, mi publicación sobre el lenguaje de programación Harbour.

El despliegue es la actividad consistente en instalar una aplicación en el ordenador de nuestro usuario. Una vez que hemos probado nuestro programa y consideramos que está listo para que nuestro usuario lo utilice, debemos proceder a instalarlo en su PC. En este momento se plantean distintas maneras de realizar el despliegue, que varían en función del tipo de software que realicemos. Si quieres leer más sobre el tema te recomiendo la entrada Cincomundos de JoelonSoftware — https://www.joelonsoftware.com/2002/05/06/five-worlds/.

En mi caso, desarrollo software ‘empaquetado’, es decir aplicaciones que el usuario instala o actualiza por si mismo, y no tengo manera de acceder al PC del usuario, por lo que el despliegue toma una gran importancia. No me puedo permitir que un usuario actualice un programa y se produzca un error, por lo que tengo que realizar una serie de acciones dentro del programa que garanticen que la actualización se realiza de manera correcta. Esto no forma parte del despliegue propiamente dicho, pero son aspectos muy importantes cuando necesitas que tu programa no de un error tras una instalación.

1.- Comprobar que existen los ficheros de la aplicación.

El primer aspecto a considerar es que existan todos los ficheros de datos de la aplicación en la ruta predeterminada del programa. Por ello al arrancar nuestro programa debemos hacer esta comprobación, y en caso de que falte algún fichero proceder a crearlo. Una manera sencilla es la que muestro a continuación:

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

Más adelante explicaré que hacen las funciones Ut_Actualizar() y Ut_Indexar(), pero básicamente comprueban que los ficheros tienen todos los campos y vuelven a generar índices.

2.- Comprobar si ha cambiado la versión del programa.

Conjuntamente con la comprobación del punto anterior hago la comprobación de que haya cambiado la versión del programa. Las versiones de mis programas siempre tienen la siguiente forma: “x.y.z” donde:

  • x indica la versión mayor. Cuando introduzco funcionalidades que supone añadir tablas a una aplicación incremento la versión mayor.
  • y indica la versión menor. Cuando libero una actualización siempre incremento la versión menor, y cuando esta actualización supone añadir o modificar un campo de una tabla incremento la decena de este número, pasando por ejemplo de la 1.1.a a la 1.10.a.
  • z indica una corrección de errores de la versión. No añado funcionalidades, únicamente corrijo errores de la versión menor actual.

Cuando cambia la versión mayor o la decena de la versión menor llamo a Ut_Actualizar() y Ut_Indexar().

3.- Creación de las tablas del programa desde código.

Llegamos a la marte más importante de la entrada: debes tener escrita la definición de tablas de tu aplicación, de manera que esta pueda crearlas o modificar su estructura cada vez que lo necesites. Ten en cuenta que estamos hablando de software empaquetado, en que no es posible que vayas al PC de tu usuario a modificar la estructura de una tabla usando un programa de tipo DBU.

Mi función Ut_Actualizar lo hace de esta manera:

// 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' )

Lo que hago es crear la tabla con el nombre del alias que luego usaré con ella, incorporo los datos de la tabla real que luego borro, y por último renombro el fichero que he creado con el alias en el nombre real de la tabla. Esto para cada una de las tablas de mi aplicación. Cuando tengo que modificar un campo de una tabla lo hago en esta función, de manera que al arrancar de nuevo el programa las tablas se modifican de manera automática.

Una vez creadas las tablas lo que hago es crear los índices sobre las mismas. Como has podido ver, el fichero índice lo he borrado antes de incorporar los datos de la tabla real, por lo que ahora tengo que crearlo.

// 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.- Controlar todos los aspectos relacionados con la modificación de campos de las tablas

Es evidente que si has incluido un nuevo campo en una tabla, este aparecerá en alguno de tus formularios. Pero también debes mostrarlo en tus rejillas de datos previa a la edición de un registro, o incluir la nueva ordenación en la rejilla en que muestras la tabla. Y tener en cuenta tus clases que muestran datos.

En mis programas uso el interfaz que he llamado Interfaz completa de documento único o Full Single Documento Interface https://alanit.com/?s=fsdi, y una de sus funcionalidades es que guardo la configuración de la rejilla de datos de cada mantenimiento. Si añado un nuevo campo, este campo no se muestra en la rejilla, porque cuando guardé la configuración ese campo no existía. Por eso cada vez que añado un campo — modifico +10 la versión menor de mi aplicación — tengo que borrar la configuración almacenada de la rejilla para mostrar todos los campos de la misma.

Si en tu aplicación realizas más cosas relacionadas con el despliegue de tu aplicación, te agradecería que no explicaras en los comentarios.

fotografía 2018

Equipo fotográfico 2018

Una de las aficiones que más estoy disfrutando en los últimos años es la fotografía. Hace ya un par de años que jubilé mi querida Lumix G3 y compré una Nikon 5300. Poco a poco he ido completando mi equipo fotográfico, que basicamente es el que muestro a continuación:

  • Bandolera Crumpler Muli 4500
  • Correa de muñeca Cuff de PeakDesign
  • Cámara Nikon D5300
  • Objetivo AF-S DX Nikkor 35mm f/1.8G
  • Objetivo Sigma 17-70mm F2.8-4 DC MACRO OS HSM Contemporary
  • Minitrípode Manfrotto Pixo al que he acompazo un adaptador arca-swiss
  • Correa Slide de PeakDesign

Equipo fotográfico 2018
Equipo fotográfico 2018

Me gusta mucho la fotografía de calle y de viajes, y llevar la cámara es la motivación perfecta para salir a pasear y conocer una ciudad fijándote en situaciones y escenas que en el día a día pasan desapercibidas.

Soy miembro de la Asociación Fotográfica de Novelda, aunque tengo que reconocer que participo poco en sus actividades, y desde hace poco he retomado la actividad en mi cuenta de Flickr publicando 4 galerías, una de fiestas de Novelda y el resto de un viaje que hice con unos amigos.

2018 * Fiestas de Novelda

2018 * Gante

2018 * Brujas

2018 * Bruselas

nuevas funcionalidades en las ediciones gratuitas

Autocompletado de valores desde tablas auxiliares.

Hasta ahora las diferencias entre las ediciones gratuitas y registradas de los programas eran las detalladas en la siguiente entrada: diferencias entre las ediciones de los programas.

A partir de septiembre de 2018 he eliminado las restricciones de las ediciones gratuitas, que unicamente traen un mensaje de recordatorio de registro que se muestra periódicamente. De esta manera, las diferencias entre las ediciones registrada y gratuita son el nombre del ususario registrado en los distintos listados del programa y el mensaje recordatorio. Nada más.

Por esto, todas las versiones gratuitas añaden desde ahora las siguientes funcionalidades:

  • Selección de claves desde tablas auxiliares. Cuando tienes que introducir una clave desde una tabla auxiliar simplemente haz click en el botón marcado con […] que aparece al lado del campo, selecciona el valor que quieras y se introducirá en el campo.
  • Selección de claves desde tablas auxiliares.

  • Autocompletado de claves auxiliares. Otra manera de completar las claves desdes una tabla auxiliar es simplemente escribiendo en el campo el valor a introducir. Al hacerlo, el programa te mostrará una lista con los posibles valores de las claves que contienen la cadena tecleada y podrás seleccionar el valor que quieras con las flechas de cursor y pulando INTRO.
  • Autocompletado de valores desde tablas auxiliares.
    Autocompletado de valores desde tablas auxiliares.

  • Consulta de ejemplares desde tablas auxiliares. Desde cualquier tabla auxiliar podrás ves los ejemplares de la tabla principal que contienen esa clave, por ejemplo los libros de una materia o las recetas de un cocinero.

Todas las descargas de ediciones gratuitas de los programas se han actualizado con estas nuevas funcionalidades.

Quizá te preguntes ¿ para que quiero registrar el programa si la edición registrada no ofrece más funcionalidades que la edición gatuita del programa ? Pues una respuesta es que te quitas el mensaje recordatorio que aparece cada cierto tiempo, y la segunda es que con tu contribución estás apoyando la existencia de programas que se adaptan a tus necesidades o a tus gustos.