25 de abril de 2005

Evolución del tratamiento de cadenas con TEXTMERGE

Como siempre, VFP ha ido evolucionando, y desde hace tres versiones el manejo de cadenas y TEXTMERGE ha sido uno de los que más impacto y mejora de funcionalidad ha tenido.

A continuación una experiencia que me llevé al manejarlo para crear cadenas de conexión hacia cliente servidor.

Estaba desarrollando una clase para manejo de conexiones y usarios a bases de datos remotas (a servidores de Base de Datos para ser más específico), cuando me tope con un código que según mi vieja memoria, podría optimizarse (o reducirse) un poco.

El caso estaba en que teniendo una tabla con los usuarios y passwords, poder armar una cadena de conexión hacia SQLServer:
SET TEXTMERGE ON
SET TEXTMERGE TO MEMVAR lcStringConnection
Driver={SQL Server};Server=<<alltrim(cServer)>>
;Database=<<alltrim(cDataBase)>>;Uid=<<alltrim(cUser)>>
;Pwd=<<alltrim(cPassword)>>;
SET TEXTMERGE TO
SET TEXTMERGE OFF

Esto funciona correctamente, y hace su trabajo como debe ser, pero puede quedar aún mejor!!, por lo que revisé la ayuda de SET TEXTMERGE, encontrándome que ésta puede simplificarse...
TEXT TO lcString NOSHOW TEXTMERGE
Driver={SQL Server};Server=<<alltrim(cServer)>>
;Database=<<alltrim(cDataBase)>>;Uid=<<alltrim(cUser)>>
;Pwd=<<alltrim(cPassword)>>;
ENDTEXT

Hasta ahí todo funciona correcto, ya he quitado algunas líneas sin perder funcionalidad, pero se me ocurrió algo, que de éste modo estaría atado a escribir en el código cada cadena de conexion si es que quisiera tener más de un servidor de bases de datos, teniendo con esto, que modificar mi código fuente en el caso que dicha cadena de conexión llegara a cambiar en un futuro, o si deseara agregar algún otro tipo de servidor (quizás MySQL, FireBird, PostgreSQL)...

Mi primera idea vino en tener tambien en una tabla DBF, las correspondientes cadenas de conexión según el servidor a usar, y simplemente hacer un textmerge desde esa cadena escrita en mi tabla, sonaba bien en un inico, pero me topé con un problema, cómo macrosustituia mi campo de mi tabla dentro de un bloque TEXT ... ENDTEXT??

Los siguientes fueron mis intentos:
1.- Pondré el campo de mi tabla en el bloque...
TEXT TO lcStringConnection NOSHOW PRETEXT
   cServer.cStringConnection
ENDTEXT

2.- Poner el campo de mi tabla macrosustituyendo...
TEXT TO lcStringConnection NOSHOW PRETEXT
  &cServer.cStringConnection
ENDTEXT

3.- Poner el campo de mi tabla con expresión de nombres...
TEXT TO lcStringConnection NOSHOW PRETEXT
  (cServer.cStringConnection)
ENDTEXT

4.- Poner el campo de mi tabla evaluando...
TEXT TO lcStringConnection NOSHOW PRETEXT
  EVALUATE(cServer.cStringConnection)
ENDTEXT

No está demás decir que ninguna de las opciones funcionó :'(, el problema radica en que lo que se pone en un bloque TEXT... ENDTEXT, se evalua literalmente, por lo que tendría que hacer un TEXTMERGE de lo que contenia la variable...

TEXT TO lcStringConnection NOSHOW TEXTMERGE
  <<cServer.cStringConnection>>
ENDTEXT

Bingo!, ya se expandió la variable, pero no era precisamente lo que quería, ya que el contenido de esa variable era el texto de mi campo, pero aún le faltaba expandir los campos de mi tabla, tales como usuario, password, etc. Por lo que necesitaba algo así como un textmerge anidado :S. Se me ocurrieron varias cosas, como mandarlo a ejecutar con ExecScript primero, retomar el valor y hacerle un segundo TEXTMERGE, hmm, no, demasiado rollo para sólo eso, recorrer la cadena expandida y cambiar los valores... si, quizás, pero nada adecuado.

Una vuelta más a la ayuda de SET TEXTMERGE me dió la solución, la función TextMerge(), que aparentemente hace lo mismo que SET TEXTMERGE TO MEMVAR y los bloques TEXT .. ENDTEXT, probé y milagro!, justo lo que esperaba (segun lo leido), tienen la función... pero con esteroides, ya que también implementa la expansión de variables de manera recursiva, en este caso primero expande lo que hay en cServer.cServidor, y como ésta misma tiene expansión de variables, vuelve a hacer un textmerge de manera implícita, expandiendo igualmente el contenido de las variables de servidor, usuario, contraseña, contenidas en la tabla de perfiles de usuario, con lo que mi código se había reducido a lo siguiente:

IF SEEK(lcTipoServer,"cServer")
  IF SEEK(lcPerfilUsuario,"cPerfiles")
    lcStringConnection = TextMerge(cStringConnection)
  ELSE
     *** Perfil de usuario no encontrado
  ENDIF
ELSE
   *** Tipo de Servidor no encontrado 
ENDIF

Con esto, sólo tuve que mover la cadena a sustituir hacia mi tabla dónde ahora puedo guardar distintas cadenas de conexión y simplemente sustituirlas, de este modo no solo tengo cómo conectarme, sino también la flexibilidad de usar cualquier tipo de servidor que acepte una cadena de conexión (hasta ahora todos los que he usado).

Para colmo, cinco dias después de andar batallando con esto, me encuentro con el siguiente artículo en FoxAdvisor:

--- Understanding TEXTMERGE ---
http://advisorupdate.info/Articles.nsf/nl/16362

Moraleja de la historia, nunca hay que dejar de leer por completo, la ayuda del producto, más en específico, los What's New de cada versión.

Espero que la información les sea de utilidad...

Espartaco Palma Martínez

No hay comentarios. :

Publicar un comentario