libro genexus

177

Upload: msnbond009

Post on 16-Feb-2015

129 views

Category:

Documents


8 download

TRANSCRIPT

Page 1: Libro Genexus
Page 2: Libro Genexus

2

Page 3: Libro Genexus

3

GeneXus X – Episodio Uno © Cecilia Fernández – Daniel Márquez Lisboa Diseño de tapa: Fiorella Franzini Aporte artístico: María Inés Carriquiry

Page 4: Libro Genexus

4

Prólogo a la 2da. Edición

Todo el equipo de Artech ha trabajado mucho en la versión X de GeneXus. Ésta no es, simplemente, una versión más: representa un gran salto adelante en muchos aspectos y, muy especialmente, en su capacidad de integración y de extensión, ya sea por Artech o por la propia Comunidad GeneXus.

La Comunidad GeneXus ha participado entusiastamente en las pruebas de las versiones CTP y está adoptando la primera versión liberada del producto, utilizándolo y, en muchos casos, construyendo extensiones. En el XIX Encuentro esperamos la exposición de muchas experiencias importantes.

En el año 2007 hemos encomendado a Daniel Márquez Lisboa (autor de “GeneXus, Desarrollo Basado en el Conocimiento – Guía Práctica” editado por Magró en el 2006) y a Cecilia Fernández, con una muy buena experiencia como instructora en nuestros cursos, un libro introductorio sobre la Versión Rocha de GeneXus. Ahora que la versión X es una importante realidad, hemos pedido a Daniel y Cecilia una segunda edición del libro, actualizando aquella historia, contada sobre la marcha del desarrollo de la versión, a la realidad actual donde la versión X está en plena producción.

Decía entonces: “No niego que, en una primera instancia, me sorprendió el tono informal del libro. Luego de leerlo cuidadosamente, veo que el “tono informal” es un acierto más de los múltiples que han tenido los autores…”: dos años después ratifico aquellas palabras.

Espero una muy buena acogida de esta segunda edición del libro que, acompasándose con los tiempos y la tecnología, se puede obtener libremente en www.genexus.com/genexus/libroepisodio1ed2 y, desde ya, felicito a los autores por su trabajo y les deseo el mayor de los éxitos.

Ing. Breogán Gonda Presidente de Artech

Page 5: Libro Genexus

5

Estimado lector

Para el XVII Evento Internacional de Usuarios GeneXus, realizado en setiembre de 2007, nos pidieron escribir un libro de ágil y amena lectura sobre la versión de GeneXus que se encontraba en plena gestación: la entonces denominada versión Rocha, hoy la X, una verdadera refundación del producto.

Desde ese entonces “la X” ha crecido mejorando funcionalidades e incorporando algunas otras. Por esa razón, sentimos la necesidad de adaptar aquella primera edición de nuestro libro “GeneXus Rocha – Episodio Uno” para que siga cumpliendo con su objetivo inicial: mostrar GeneXus a quien, nuevo en esta herramienta, tenga inquietud o deseos de descubrirla, así como a quien, conocedor de versiones anteriores, quiera redescubrirla en esta nueva y fundamental etapa que inicia.

He aquí entonces la segunda edición actualizada de aquel libro. Fue elaborada con GeneXus X, upgrade 4. Actualmente, Artech se encuentra trabajando para liberar en unos meses más GeneXus X Evolution 1, que incluye nuevas e importantes funcionalidades, como el permitir generar, además de aplicaciones Web, aplicaciones Win.

Esperamos que pueda ir siguiendo las páginas de este libro con el mismo entusiasmo que nos llevó a escribirlas, y que pueda adaptar la lectura a sus ganas y necesidades. Hay capítulos más técnicos que otros, en especial el 3 que trata sobre Patterns. No pudimos ceder ante la tentación de mostrar toda su potencia, lo que nos llevó a desnudar cada vez un poquito más. Quien quiera mucho, lo leerá todo, quien no, lo leerá más superficialmente. Tómese esa libertad, que fue la que nos permitió dar rienda suelta a nuestras ganas de contar. Los últimos capítulos probablemente sean los más ágiles y disfrutables.

Para entender más cabalmente lo explicado, sugerimos ir realizando con la versión Trial de GeneXus, de distribución gratuita (www.genexus.com/trial), la aplicación que nuestros personajes irán desarrollando a lo largo del libro. Si bien podrá elegir el generador y DBMS que prefiera, si es usted nuevo en GeneXus, le recomendamos elegir los default que son lo que utilizarán nuestros

Page 6: Libro Genexus

6

personajes en el libro. Encontrará en nuestro Download Center, www.gxtechnical.com/genexus/libroepisodio1ed2/zip para ser descargado en forma gratuita, un zip con el contenido final del proyecto desarrollado. Podrá crearse otro proyecto (KB) e importarlo. Le servirá para comparar lo que va haciendo, con la aplicación ya implementada.

Encontrará repetidamente en este texto citas a un whitepaper de Breogán Gonda y Nicolás Jodal, “Desarrollo basado en conocimiento. Filosofía y fundamentos teóricos de GeneXus”, publicado en mayo de 2007.

Por último, volvemos a agradecer a todos los compañeros que en oportunidad de la primera edición estuvieron de un modo u otro aportando a este trabajo. A todos: ¡gracias!

Mary, Diego, Mike y Julia, esperan ansiosos entrar en escena… ¡Buena lectura!

Los autores. Montevideo, Mayo de 2009.

Page 7: Libro Genexus

7

Sinopsis Capítulo 1: El encuentro. Diego y Mary se reencuentran luego de años sin verse, dando inicio a esta historia de encuentros…con GeneXus. Capítulo 2: Érase una vez…un Proyecto. Julia, Mike y Diego definen e inician el análisis y desarrollo del sistema para una agencia de viajes, a la vez que Diego acompaña a Mary, café mediante, en su primera experiencia de acercamiento al IDE y a la creación de los primeros objetos del proyecto. Capítulo 3: Hecho a máquina, configurado a mano. Mientras Julia va haciendo uso de lo máximo en capacidad de productividad gracias a los patterns, Diego da rienda suelta a su creatividad poniendo de manifiesto que los deseos de los clientes no siempre deben ser un duro escollo a sortear. Capítulo 4: Vuelo al mundo de los Subtipos. A velocidad match 3, Diego enseña a Mary, esta vez en su oficina y no en el bar, por qué los subtipos son la solución ideal al problema de las ambigüedades que se pueden presentar en un modelo de datos, mientras pone el punto final a las últimas transacciones. Capítulo 5: Cuando el “qué” no alcanza. El interés de Mary parecía ir in crescendo. Encuentros frecuentes en la oficina o en el bar. Preguntas todavía sin respuesta. Las transacciones permiten declarar, ¿pero qué pasa con los procesos ‘batch’? Procedimientos GeneXus. Después, más declaraciones: Business Components. Y ¿cómo realizar reportes pdf? Atracciones que invitan. Invitaciones que atraen. Una cena en ciernes ¿Y las consultas dinámicas por ejemplo? Capítulo 6: Más declaraciones. Data Providers para permitir cargar y devolver una salida estructurada, sin programar, sino declarando intenciones. Diego declara sus intenciones. Debe enviar un xml a la aerolínea. Capítulo 7: Soplan nuevos vientos. Su casa (la de ella), vino, música, su nerviosismo (el de él) y más GeneXus. Sabemos que todo tiene un final, pero lo que Diego apenas sospechaba, era que esto podía llegar a ser el principio de algo más.

Page 8: Libro Genexus

8

Page 9: Libro Genexus

9

Capítulo 1 El encuentro

Alguien cuenta que esperando por fin ser atendido en un banco, escuchó

casualmente una conversación entre un hombre y una mujer, que también esperaban. Según parecía, se reencontraban luego de años sin verse; ¿antiguos compañeros de estudio?

- ¿Cómo estás Mary? ¿Cómo van tus cosas? ¿Sigues trabajando donde siempre?–dijo el hombre con voz profunda, impostada, mirada fija, aire seductor.

- Bueno Diego, luego de tantos años trabajando en la empresa, logré ascender y ahora soy Developer Team Manager desde hace ya dos años – dijo la mujer de rostro sensiblemente cansado.

- Supongo que debo felicitarte, pero ¿por qué esa cara? ¿tienes problemas en el departamento? He escuchado que los sistemas de ustedes, además de ser de gran porte, son fiables…

- Lo son, pero no sabes lo que cuesta su mantenimiento. Me estoy enfermando, Diego, no duermo bien; muchas responsabilidades, grandes cargas horarias, poco descanso. Siento en el pecho…

- …una presión – completó Diego – Entiendo, Mary – dijo al tiempo que depositaba su mano derecha sobre el brazo izquierdo de ella-. ¿Cómo están trabajando? ¿Qué herramientas están usando?

- Y… de todo un poco, lo usual… al final todo es lo mismo…– contestó Mary ajena a esa cercanía- Tengo un equipo importante de personas trabajando en los desarrollos y mantenimientos, pero aún así no damos abasto.

- Bueno, no todo es lo mismo… ¿Qué te parece si terminamos este trámite aquí y vamos a tomar un café y me sigues contando? –preguntó Diego a la vez contento y turbado.

Page 10: Libro Genexus

10

¿GeneXus?

Ya en el bar, café mediante, Diego escuchaba atentamente a una Mary angustiada. Recordaba su antigua firmeza, su empuje. Sentía un egoísta dejo de alegría de poder ser ahora él quien la ayudara.

- ¿En cuántos proyectos están? – preguntó Diego para instaurar un clima confesional.

- Ahora en uno solo. Pero es más que nada en tareas de mantenimiento. Nuestros clientes nos están solicitando cosas nuevas todo el tiempo, pero nos está siendo imposible satisfacer nuevos requerimientos. Además, esta tarea de mantenimiento es realmente costosa, y ya no puedo justificar a la Dirección la incorporación de más recursos humanos; nos saldríamos del punto de equilibrio.

- ¿No será que los sistemas están un poco anticuados, tecnológicamente hablando? ¿No les convendría hacer una reingeniería de todo a nuevo? – preguntó Diego con liviandad.

- Sí, eso quisiera, y eso he propuesto. Pero los tiempos realmente no nos dan, y como te dije, puede que estemos ya pasados de costos. No sé qué voy a hacer… estoy agotada Diego, me siento en un callejón sin salida.

- Mary, no desesperes, ¿sabes cuántos están como tú? Tu caso es uno más entre muchos. Las aplicaciones son cada vez más complejas, y con las herramientas manuales de las que disponíamos, se está tornando imposible desarrollar y peor aún, mantener esos sistemas, pues se están volviendo inmanejables. Pero no todo es negro, Mary. Hoy en día hay soluciones. La alternativa es delegar en procesos automáticos el trabajo que hacemos los humanos, de manera que podamos dedicarnos a las nuevas complejidades y no a tareas como la codificación que pueden ser efectuadas por un programa. Nosotros, sin ir más lejos, estamos llevando adelante varios proyectos importantes en simultáneo, y no tenemos demasiados inconvenientes. ¿Conoces algo de GeneXus?

- Ah, GeneXus. Sí, algo he escuchado, pero no he tenido tiempo de interiorizarme demasiado… ¿Ustedes trabajan con esa herramienta? He oído

Page 11: Libro Genexus

11

que cada vez más gente no técnica puede usarla, lo que me despierta cierta curiosidad.

- Mira Mary, no sé si GeneXus sea la panacea, pero sí sé que cura varias de las dolencias que tienen empresas como la tuya. Es un programa que hace programas. Automatiza la construcción y mantenimiento de tus aplicaciones y de tu base de datos. Obviamente está en un nivel de abstracción superior a otras herramientas, ¿comprendes? Contéstame a esta pregunta: si tú pudieras disponer de un software que te permitiera lograr hacer esto automáticamente, ¿lo usarías?

- Absolutamente.–.. Se incorporó en su silla–. Sí, justamente ése es nuestro problema. Tenemos que hacer casi todo a mano…

La gran dificultad de la empresa de Mary, pensó Diego, era el de tantas otras: imposibilidad de enfrentar nuevos proyectos porque los tiempos de mantenimiento de los sistemas actuales, fundamentados en metodologías tradicionales, lo impedían. Y eso, para una empresa que vive del desarrollo, es una luz roja de peligro.

Bueno, no lo diga en voz alta, pero usted, estimado lector, se estará preguntando: ¿Cómo?

- ¿Cómo? – preguntó ya más vital. - ¿No te parece razonable pensar que dado un conjunto de visiones de

datos, - se interrumpió Diego con cierta malicia - exista un único modelo relacional mínimo que lo satisfaga?

- Hmmm… puede ser, no estoy segura. ¿Pero qué tiene esto que ver con mis problemas de mantenimiento?

- Y si eso es cierto –continuó Diego, ignorando la vacilación de su compañera- ¿no te parece posible encontrar un procedimiento de ingeniería inversa que partiendo de ese conjunto de visiones dé como resultado el esquema de esa base de datos relacional mínima? Bueno, si me vas a decir “ver para creer”, con gusto acepto una apuesta, como en los viejos tiempos; pero desde ya te adelanto: vas a perder.

Page 12: Libro Genexus

12

Una Apuesta

GeneXus es una herramienta que parte de las visiones de los usuarios;

captura su conocimiento y lo sistematiza en una base de conocimiento. A partir de esta última, GeneXus es capaz de diseñar, generar y mantener de manera totalmente automática la estructura de la base de datos y los programas de la aplicación, es decir, los programas necesarios para que los usuarios puedan operar con sus visiones.

- Mary, GeneXus almacena en una Knowledge Base todos los elementos necesarios para construir la aplicación, y luego la utiliza para el desarrollo del sistema, construyendo automáticamente el modelo de datos en

GeneXus trabaja con conocimiento puro, cuya validez es totalmente independiente de las tecnologías de moda.

“El objetivo de GeneXus es (a través de la descripción de las visiones de los usuarios) conseguir un muy buen tratamiento automático del conocimiento de los sistemas de negocios.”.

Breogán Gonda & Nicolás Jodal

Page 13: Libro Genexus

13

forma normalizada, y también utilizando el lenguaje de programación y base de datos que se le indique. Así que te permite obtener un proyecto con el conocimiento de la empresa…

- ¡Ah! Entonces eso… - Sí –le interrumpió Diego completando la idea-, te permite ganar algo

muy importante: la portabilidad futura de la aplicación. Todo ese conocimiento -reglas de negocio, diálogos, controles, listados- que hoy están en un lenguaje de programación determinado, pueden ser convertidos a otros lenguajes sin necesidad de arrancar de cero. O sea que se reutiliza el conocimiento porque la Knowledge Base es conocimiento independiente de la plataforma.

Mary parpadeó rápidamente un par de veces y, sin mediar palabra alguna, pidió otra ronda de café. Base de Conocimiento y Modelos Externo y Relacional

Nuestros personajes continuaron su conversación en el café. A esas alturas, Mary ya se mostraba particularmente interesada en esa herramienta, y Diego en...

- Bueno, por un lado está el Modelo Relacional, que como bien sabes, está orientado a obtener una buena representación de los datos en una Base de Datos, obedeciendo a algunas condicionantes muy deseables.

- Cierto… – aseguró ella –. Eliminar las redundancias e introducir un pequeño conjunto de reglas para evitar las mayores fuentes de inconsistencia de los datos…

- Eso mismo, reglas que chequean la unicidad y la integridad referencial y además también proveen de un conjunto de operadores para poder manipular los datos a buen nivel – acotó Diego, alegre por el clima de complicidad generado.

- Sí, claro, por eso todo el mundo las usa. ¿GeneXus no las usa? - Bueno, sí, ya voy a llegar a eso. ¿Te acuerdas también del Modelo

Externo, aquel donde se representaban las visiones externas? Bueno, GeneXus

Page 14: Libro Genexus

14

pone el énfasis en este modelo, porque es donde realmente está el conocimiento genuino, es el realmente importante para los usuarios y los desarrolladores. En él se recoge el conocimiento exterior y todo lo demás, como otros modelos auxiliares que pudieran ayudar, puede inferirse automáticamente a partir de ese Modelo Externo.

- Ah…el Modelo Externo…no puede contener ningún elemento físico o interno como archivos, tablas, entidades, relaciones entre entidades, índices… ¿verdad?

- ¡Exacto! O cualquier otro que se pueda inferir automáticamente – exclamó Diego animado, recuperando el entusiasmo que siempre le provocaron las conversaciones con Mary– El Modelo Externo será utilizado para obtener y almacenar el conocimiento. Pretende la representación más directa y objetiva posible de la realidad.

- Claro, y con eso te independizas de la implementación, porque tienes una descripción del sistema en alto nivel, descripción que sólo cambiará si cambian las visiones sobre el mismo; ¿voy bien?

- ¡Perfecto! Por ello tomamos las visiones de los diferentes usuarios… Visiones que son almacenadas en el modelo. Luego se captura todo el conocimiento contenido en ellas y se lo sistematiza para maximizar las capacidades de inferencia…

- Eso es lo que no termino de creer. Me suena a pura teoría… ¿tú intentas decirme que es posible sólo describir y que un programa, con sólo un par de pases de varita mágica te haga realidad la aplicación? – preguntó Mary intentando ser convencida por los razonamientos apasionados de Diego.

- Bueno, yo no diría que es magia. Es un software al fin de cuentas. Y no digo que no tengas que programar algo, pero sí digo que “casi” nada, lo

Page 15: Libro Genexus

15

menos posible. En GeneXus vas a tener algún código que será procedural, pero el lenguaje en que lo escribes, no es el lenguaje al que estás acostumbrada… en él tampoco tienes que nombrar tablas, ni índices, ni nada de lo físico de una base de datos.

- Pero… no entiendo entonces dónde entra el Modelo Relacional – preguntó Mary confundida.

- Es el que se utiliza para representar y manipular los datos: es el modelo interno o físico, pero no será creado por ti, será inferido por GeneXus con su procedimiento de ingeniería inversa. Mi apuesta fue tramposa.

- ¿Y cómo describes las visiones de los usuarios, el Modelo Externo, de manera que pueda aplicarse ingeniería inversa sobre ellas y obtener todo eso que dices? Porque tiene que ser lo bastante formal y rigurosa esa descripción como para que no haya ambigüedades y que un programa pueda “inferir” todo eso que los programadores hacemos todavía a mano.

- Sí, lo es. Las descripciones se realizan a través de determinados “objetos-tipo” de GeneXus que indican “el qué”. A través de ellos, GeneXus encuentra el “cómo”. Por lo que, para aprender a usar GeneXus, tienes que aprender a describir, a usar esos objetos-tipo. No necesitas grandes conocimientos técnicos; cada vez menos.

Diego continuaba hablando, llenando el reducido silencio que su interlocutora había dejado. A pesar de tantos años sin verse, conocía bien a Mary, conocía esa expresión de ojos pensativos, y sabía que su silencio lo invitaba a proseguir. No podía tener mejor suerte - ¡Bendito GeneXus! - pensó, y continuó en tono confidente, manejando las pausas, seguro de lograr los efectos buscados.

Page 16: Libro Genexus

16

- Para lograr todo esto, GeneXus tiene una base de conocimiento, que inicialmente tiene asociado un conjunto de mecanismos de inferencia y algunas reglas de aplicación general, como las que aseguran la consistencia (las de integridad referencial, por ejemplo). Luego, cuando el analista GeneXus comienza a describir la realidad creando objetos, estas descripciones (el Modelo Externo) son sistematizadas automáticamente y pasan a estar contenidas en la base de conocimiento… Además, sobre ese conocimiento, obtiene un conjunto de resultados que le ayudan a mejorar la eficiencia de las inferencias posteriores.

- ¡Es una máquina de inferencias! – exclamó Mary. - Nada más cierto. Por ejemplo, dada una visión de los datos, puede

inferir automáticamente el programa necesario para manipularla.

“GeneXus trabaja permanentemente sobre la Knowledge Base. Todo el conocimiento de la Knowledge Base es equivalente al contenido del Modelo Externo (subconjunto de ella), ya que consiste en el propio Modelo Externo más reglas y mecanismos de inferencia independientes de él y un conjunto de otros elementos que son automáticamente inferidos a partir del mismo.

El desarrollador puede alterar, modificando objetos de la realidad del usuario, el Modelo Externo y las modificaciones se propagarán automáticamente a todos los elementos que lo necesiten: otros elementos de la Knowledge Base, Base de Datos y programas de la aplicación. De la misma manera, el desarrollador no puede alterar directamente ningún elemento que no pertenezca al Modelo Externo”…

TODO el conocimiento está contenido en el Modelo Externo y, por ello, mañana podríamos soportar la Knowledge Base de una manera totalmente diferente y el conocimiento de nuestros clientes seguiría siendo utilizable sin problema alguno”.

Breogán Gonda & Nicolás Jodal

Page 17: Libro Genexus

17

- Diego, debo irme, pero quiero saber más. Me has dejado con la cabeza

confusa. Es muy grande el cambio de mentalidad, quisiera palpar esto más de cerca, verlo funcionando. ¿Te parece de encontrarnos la semana próxima, misma hora, mismo lugar?

- Ciertamente – expresó Diego en tono pretendidamente sereno.

Page 18: Libro Genexus

18

Page 19: Libro Genexus

19

Capítulo 2 Érase una vez… un Proyecto

Travel Agency & Co. es una novel compañía de venta de tickets de viajes y turismo que ha contratado a la software house ACME Business Software Solutions a fin de que le sea desarrollado un sitio en Internet que permita a sus clientes realizar búsquedas de destinos, de vuelos existentes, de servicios brindados, reservar tickets y obtener pasajes y servicios turísticos (front-end). Incluirá también todo el sistema para el mantenimiento de la información relacionada (back-end).

ACME, que viene trabajando con GeneXus en sus sucesivas versiones desde hace un tiempo, está integrada por varios técnicos; entre todos ellos, Julia, Mike y Diego fueron los seleccionados para llevar adelante este desarrollo. Diego y Mike son Analistas GeneXus y los que normalmente se encargan de la implementación que les toque en suerte. En particular, Mike se encarga casi siempre de la tarea de testear las aplicaciones, mientras que Diego se encarga del desarrollo. Julia, en cambio, no es técnica en GeneXus, si bien ha trabajado en muchos proyectos relevando requerimientos; su especialidad es documentar y moderar las charlas entre los integrantes del equipo a medida que se van produciendo los avances. El hecho es que los tres conforman un equipo donde todos tienen voz y voto.

Las primeras tareas recaen sobre los hombros de Julia y Diego mientras Mike realiza los testings de las otras aplicaciones de ACME en curso para los desarrollos en otros clientes.

Desde siempre, Julia documentaba en procesadores de texto, y en proyectos de envergadura le resultaba extremadamente engorroso el mantener decenas de carpetas con cientos de archivos, conjugarlos entre los desarrolladores, coordinar las discusiones del equipo (cuando lograba hacerlo), entre otras tareas. Usted comprenderá el impacto que sintió luego de enterarse que GeneXus X contenía, en forma embebida, un perfil de documentación muy particular que facilitaría su vida de ahora en más.

Page 20: Libro Genexus

20

El tiempo es tirano

Un breve tiempo atrás, los directores y el equipo de desarrollo se pusieron de acuerdo en el objetivo de un proyecto a corto plazo, tres semanas o algo así. El hecho de que iban a trabajar con GeneXus X, a sabiendas del gran aumento de usabilidad y productividad que ésta traía incorporado, les había permitido reducir a la mitad los plazos que normalmente pactaban con sus clientes.

Afortunadamente, Travel Agency & Co. ya tenía su esquema interno de funcionamiento bastante sólido, con abundante documentación y esto ya era una ventaja para el equipo de desarrollo. Julia, Diego y Mike acordaron que debían tener reuniones en forma paralela con los involucrados de cada sector e ir recogiendo sus visiones.

Querían rápidamente comenzar a representar la realidad de la empresa; entre otras cosas, detectaron que Travel Agency & Co. apuntaba fundamentalmente al turismo de diversión y esparcimiento con destinos muy

“Cada usuario tiene una o múltiples visiones de los datos que utiliza cotidianamente. Entre estas visiones, podemos pensar en un primer tipo: el que agrupa aquellas que se utilizan para manipular los datos (introducirlos, modificarlos, eliminarlos y visualizarlos en forma limitada), a estas visiones de usuarios les hemos llamado Transacciones y constituyen el primer objeto-tipo de GeneXus”.

Breogán Gonda & Nicolás Jodal

”Es necesario un dramático aumento de productividad (en el desarrollo de sistemas) pero la productividad de los lenguajes de programación ha llegado hace ya bastante tiempo a una estabilización”… “¿Cómo lograr, entonces, el aumento de productividad que se necesita? Haciendo desarrollo basado en conocimiento y no en programación: ¡la solución es describir en vez de programar!”.

Breogán Gonda & Nicolás Jodal

Page 21: Libro Genexus

21

variados en el mundo. ¿Qué les decía esto? Antes que nada, que si los destinos eran puntos turísticos de ciudades distribuidas a lo largo y ancho del mundo, debían disponer de un almacenamiento para ello.

Es así que el equipo completo se reunió y luego de intercambiar notas e impresiones, concordaron en que Julia comenzara a describir las primeras pautas obtenidas, mientras el resto, en especial Diego, comenzaría a representar en GeneXus aquellas visiones de los usuarios más claramente descriptas.

El IDE a primera vista

Ahora intentemos adentrarnos un poco en el ambiente de desarrollo de GeneXus X. Si usted lo desea podrá seguir los pasos de Julia para tomar contacto con los elementos que componen este ambiente. Así, creará la base de conocimiento del proyecto y el primer objeto de tipo Transacción.

Generalmente conocido por sus siglas (IDE, Integrated Development Environment), en realidad este ambiente de desarrollo supera largamente el verdadero significado de las siglas en su acepción más general.

¿Por qué? Porque el concepto más elemental de IDE es el de un ambiente que ofrece una buena dosis de comodidad, de ventanas en donde seleccionar elementos, de ventanas donde codificar, de cajas de herramientas y de fuentes, y alguna cosa más.

Sin embargo, el IDE de GeneXus X no sólo satisface las mínimas especificaciones, sino que pone al alcance del usuario mecanismos de operatividad integral que van más allá.

Julia inició la sesión en una computadora con GeneXus X recién instalado, y apareció ante su vista una imagen semejante a la que se ilustra a continuación.

Page 22: Libro Genexus

22

Inicialmente, podemos notar dos áreas contenedoras claramente definidas. A la izquierda, tenemos el contenedor Knowledge Base Navigator; su objetivo es ofrecer distintas vistas. Está compuesto por una zona (ahora en blanco) que contendrá, en forma de árbol, los elementos que integren la base de conocimiento en uso, y un grupo de paneles horizontales rotulados con descripciones inherentes a otras vistas.

La ventana de la derecha, que podríamos considerar la principal (la mayor parte de nuestra actividad se realizará en ella y así la llamaremos de ahora en adelante), es en principio otro contenedor de múltiples funciones.

Note que en su esquina superior izquierda se encuentra un tab (o solapa) rotulado Start Page. Es el primer contenedor abierto dentro de la ventana principal. A medida que usted vaya trabajando, la presencia de nuevos tabs indicará la disposición de nuevos espacios conteniendo elementos del proyecto tales como listados de navegación, diagramas, estructuras de transacciones, etc.

Ahora veamos el contenido del único tab que tenemos. Observe que incluye otros contenedores con información. Por ejemplo, Recent Knowledge Bases nos

Panels

Page 23: Libro Genexus

23

ofrece los nombres de las últimas bases de conocimiento que han sido abiertas, con sus fechas de última modificación; note que también están presentes links que permiten abrir o crear una nueva base de conocimiento.

Debajo, un contenedor Extensions presenta herramientas desarrolladas por Artech o terceros, que pueden ser instaladas y utilizadas libremente, y permiten extender en todas direcciones la potencia de la herramienta.

El sector rotulado GeneXus Community, contiene sindicaciones de contenidos de sitios Web vinculados a la Comunidad que se actualizan con frecuencia (RSS). Note que debajo de cada noticia aparece More… como link al documento propuesto.

A su vez observe la celda Address que le permitirá navegar por la Web o por su sistema de archivos sin salir del IDE.

Proyecto nuevo, Base de Conocimiento nueva

La primera tarea cuando se comienza a desarrollar una aplicación con GeneXus es crear la base de conocimiento.

En el siguiente encuentro que mantuvieron en el café, Mary, con ojos aún graves, preguntó:

- Me contaste que GeneXus automatiza la creación de base de datos y programas, pero ¿cómo sabe en qué ambiente, en qué lenguaje debe codificar esos programas? Por ejemplo, si tuvieras que desarrollar una aplicación en Java y suponte, otra, o la misma, en .Net…, o en…

- Bueno, cuando te creas la Knowledge Base (le solemos llamar KB, para abreviar) le puedes decir que quieres generar el proyecto en un ambiente Java. Como ya te comenté, la KB es independiente de la plataforma de ejecución: si tú generaste la aplicación Java, más adelante, con el mismo conocimiento, puedes generarla para .Net, y no tienes que volver a describir los objetos de la realidad que ya habías descrito. Simplemente tienes que indicarle que ahora necesitas asociarle otro ambiente. Cuando le asocias un ambiente, tiene por ahí un lugar donde especificar todo lo referido al mismo. Por ejemplo,

Page 24: Libro Genexus

24

en Java tendrás que indicar cosas tales como dónde estarán las clases en la webapp, algo que en .Net no tiene sentido. Es decir, en base al ambiente que hayas elegido, te muestra propiedades necesarias para configurarlo, para que GeneXus pueda luego decirte: “sus deseos son órdenes” y te implemente la aplicación como se lo pediste.

Casualmente, mientras Diego y Mary intercambiaban palabras y miradas en el café, Julia realizaba esos mismos pasos que lo invitamos a seguir a usted también. 1. Seleccione File/New/Knowledge Base (o el link New Knowledge Base de la

Start Page). A continuación verá en la pantalla el siguiente diálogo:

Observe cómo desde aquí se asocia un ambiente de implementación al proyecto. Dejamos la opción por defecto, “C# Environment”. También observe que por defecto se asocia el lenguaje English para la aplicación a ser generada. Todos los mensajes, botones, etc., estarán por defecto en inglés. Pero luego será posible generar la misma aplicación en otro idioma, tanto en forma estática como dinámica (el usuario, en tiempo de ejecución, elige el idioma en que desea utilizar la aplicación) a un costo mínimo.

TravelAgency

Page 25: Libro Genexus

25

Observe además que la KB será almacenada en una base de datos que por defecto recibirá el nombre GX_KB_nombre en la instalación del SQLServer local que usted tenga. Si no tiene una local, podrá utilizar una de la red. Presionando el botón [Advanced] podrá modificar estos valores.

2. Pulse [Create]. GeneXus creará una base de conocimiento vacía, y usted debería ver una imagen semejante a la que se observa a continuación.

Observe que han cambiado algunos contenidos. En el panel Folder View ha aparecido un árbol cuya raíz es la propia base de conocimiento (con el nombre que usted le ha dado) y sus ramas los distintos elementos que la componen: Domains, Objects, Tables, Customization, Documentation, etc. También note que se ha agregado un nuevo sector dentro de la Start Page rotulado Knowledge Navigator conteniendo atajos a las tareas más comunes que probablemente usted querrá ejecutar luego de haber creado o abierto una base de conocimiento.

Page 26: Libro Genexus

26

Sobre esta base de conocimiento trabajarán los demás integrantes del

equipo; Julia está ahora lista para comenzar a documentar. Documentando desde dentro

Documentos, notas, listas de tareas, son sólo ejemplos de la documentación

que rodea a cualquier proceso de desarrollo de software. La documentación es una parte importante de cualquier aplicación si se encuentra actualizada y es fácilmente accesible.

Esta es la razón por la cuál GeneXus contiene funcionalidades de documentación integradas a la KB.

Documentos, archivos, diagramas, objetos pueden estar todos interrelacionados mediante links, fácilmente accesibles por cualquier miembro del equipo. Por tal razón decimos que la documentación es activa e integrada, constituyendo un verdadero wiki.

Files

Julia había recibido por e-mail varios archivos de documento de los jefes de sección de cada departamento con requerimientos para el sistema, donde listaban las prioridades y características de sus sectores, algunos flujos de procesos, etc.

Page 27: Libro Genexus

27

Como estos documentos ya estaban hechos por terceros, decidió que sería importante incorporarlos en su estado original dentro de la base de conocimiento, de forma tal que siempre estuvieran disponibles para su consulta. Eran importantes las “palabras” de los usuarios expuestas por ellos mismos.

Así, expandió el nodo Documentation del árbol y seleccionó Files abriéndolo con doble clic. Como resultado, un nuevo tab apareció dentro de la ventana principal, rotulado Files. Allí presionó Add New File y se le abrió una ventana que le permitió explorar su sistema de archivos, y elegir cada archivo a ser insertado en la KB. Así quedó el tab Files luego de esto.

Images

También le habían enviado por e-mail el logo de la empresa, por lo que accedió al nodo Customization del árbol del Folder View, abrió haciendo doble clic sobre Images el tab correspondiente e importó, almacenándolo en la KB, el archivo Logo.bmp que sería utilizado en el encabezado de las páginas de la aplicación. En ese mismo e-mail recibió decenas de archivos con imágenes sobre atracciones turísticas que luego necesitarían almacenar en la base de datos, por lo que momentáneamente las guardó también aquí, para tenerlas centralizadas en la KB.

Page 28: Libro Genexus

28

Main Document

Ahora Julia se apresta a empezar a escribir la página principal del wiki de la KB: observe que bajo el nodo Documentation del árbol aparece un objeto Main Document. Cada equipo de desarrollo tiene metodologías diferentes respecto a la documentación. En ACME utilizan esta página principal para describir las generalidades del sistema bajo desarrollo, y para acceder desde aquí a los distintos elementos de documentación.

También podría elaborarse aquí mismo el Plan Director del proyecto, en vez de insertarlo como archivo. Pero usted y su equipo de desarrollo utilizará esta página como mejor le parezca.

Julia abre Main Document y un nuevo tab aparece en la ventana principal, rotulado Document:Main. Dado que lo que desea es comenzar a escribir, pulsa el selector Edit que se encuentra al pie de la ventana y redacta (dándole formato) lo que se observa en la siguiente imagen. Luego pulsa el selector Preview y tiene la vista normalizada de lo que escribió. Guarda los cambios (Preview se transforma en Browse).

Note que le ha dado algún formato al texto. Para ello se valió de la toolbar formatting:

Page 29: Libro Genexus

29

Julia ha cambiado el tipo de letra, su tamaño, e incluso puso en negrita el tópico sobre el cual se comenta.

Pero además, y bien importante para lograr una documentación integrada, Julia necesitaba agregar links hacia los documentos recibidos e insertados en la KB instantes antes. Para ello se valió de la toolbox1, seleccionó Table de la misma, arrastrándola a la página y luego de insertada la tabla, en cada fila escribió:

Queriendo tener una vista previa, presionó el selector

Preview y, satisfecha, grabó, luego de lo cual se le mostró lo que aparece en la siguiente figura:

1 El IDE de GeneXus ofrece varios paneles con utilidades que se pueden acoplar en cualquier parte de la ventana y también se pueden ocultar automáticamente cuando pierden el foco para que no ocupen sitio en la ventana principal. Usted puede activarlas mediante View/Other Tool Windows.

Page 30: Libro Genexus

30

Observe que como consecuencia de los símbolos que empleó en la segunda columna de la tabla, los documentos se encuentran subrayados: son links a los archivos almacenados. Esto aporta un gran dinamismo a la documentación, y la integra en forma activa en el proyecto a través de la KB.

El editor de documentación de GeneXus utiliza dos paréntesis rectos de apertura ([[) y dos de cierre (]]) en el texto para crear links.

Si usted está siguiendo el ejemplo en forma práctica, habrá notado que, apenas pulsado el segundo paréntesis de apertura, el editor le propuso una lista de objetos válidos para seleccionar, tal como mostramos en esta imagen:

Page 31: Libro Genexus

31

Julia seleccionó File y al digitar ‘.’ se abrió la lista de los archivos disponibles (también podría haber arrastrado desde la toolbox el objeto File y habría obtenido igual resultado que al escribir “[[File.”).

Entonces seleccionó el deseado en cada momento, pulsó <Enter> y el texto quedó rematado con los dos paréntesis finales.

Creando la primera Transacción: Attraction

Diego piensa, mientras conversa con Mary (la mirada perdida en el café humeante), que ya debe dar inicio al volcado de las primeras visiones obtenidas a través de los usuarios que entrevistó en el Departamento de Informaciones Turísticas de Travel Agency & Co. Este departamento tiene a cargo la búsqueda de información que permite agregar o suprimir los destinos e itinerarios que ofrece la empresa a sus clientes. Piensa que ni bien vuelva al trabajo, deberá crear la transacción Attraction, cuando Mary interrumpe su interno discurrir:

- ¿Sabes Diego? No comprendo muy bien cómo puedes describir rigurosamente las visiones de los usuarios como para que no haya ambigüedades y se pueda inferir todo automáticamente.

- Bueno. ¿Recuerdas que te comenté que estamos trabajando en un proyecto nuevo? Bien, pues me he estado reuniendo con los usuarios quienes me contaron que manejan atracciones turísticas de cada país y ciudad, para poder ofrecer a los viajeros. Manejan el nombre de la atracción, como podría ser “Centenario Stadium” o “Roman Colisseum” o “Disney World”; manejan el nombre de país y ciudad al que pertenece la atracción, una imagen de la misma. También me contaron que las atracciones están categorizadas. Por ejemplo,

Page 32: Libro Genexus

32

“Centenario Stadium” pertenece a la categoría “Great Monuments” o algo así, y “Disney World” a “Entertainment”. Estas son sus visiones de los datos…

- Ajá. ¿Y cómo describes esas visiones objetivamente? - Mediante atributos… y objetos de tipo Transacción. Déjame explicarte

un poco. Los atributos son el marco de referencia sobre el cuál hacer las demás descripciones. No es exactamente lo mismo que un atributo en un modelo relacional. El atributo es el elemento semántico fundamental. El significado del mismo vendrá dado por su nombre, por lo que los nombres de los atributos pasan a ser esenciales en GeneXus.

Diego tomó una servilleta de papel de la mesa del bar y garabateó algunos nombres: AttractionName, CountryName, CityName, AttractionImage, AttractionCategoryDescription. Y prosiguió:

- Estos atributos tienen un contenido semántico claro, que cualquier persona podrá entender sin necesidad de contexto alguno. No son sólo “Name” o “Description”. Existe una fuerte relación entre el nombre del atributo y su significado. Ese nombre, a su vez, es una secuencia de caracteres, algo sintáctico, único, con lo que cualquier programa puede trabajar, sin ambigüedad. Si yo coloco AttractionCategoryDescription en algún lado, tú y yo sabemos que estamos hablando de la descripción de la categoría de la atracción. GeneXus lo reconoce, por su sintaxis.

- Ajá, entonces –interrumpió Mary– claramente no puedes usar el mismo nombre de atributo cuando quieras referirte a otro dato, pues ahí sí habría confusión y ambigüedad.

- Exacto. Un atributo deberá tener el mismo nombre en todos los lugares donde aparezca y no podrá haber dos atributos diferentes, con significado diferente, que compartan el mismo nombre. En eso se basa. Hay una excepción, pero no te quiero confundir ahora. Estas visiones que me expresaron los usuarios, corresponden a aquellas que se utilizan para manipular los datos (ingresarlos, modificarlos, eliminarlos, visualizarlos), y son las que se representan en GeneXus mediante el objeto de tipo Transacción. Cada transacción tiene un conjunto de elementos: una estructura, reglas, fórmulas,

Page 33: Libro Genexus

33

elementos de presentación. Es decir, en un mismo objeto, se matan varios pájaros de un tiro: al tiempo que dices con qué información se va a trabajar, también diseñas la pantalla que se desplegará al usuario final para hacerlo, declaras el comportamiento que deberán tener esos datos cuando se vayan ingresando, y demás.

Diego volvió a tomar la servilleta que había dejado sobre la mesa, y esta vez escribió (y luego dijo):

- -

- Los datos se presentan de acuerdo a una estructura que debe recogerse con todo rigor. Esta sería la estructura de la transacción Attraction. El asterisco que sigue a AttractionId significa que para cada atracción, existe un único AttractionId, es decir, es lo que le da unicidad a la atracción: un identificador. Toda información debe poder identificarse. Una atracción debe identificarse mediante algún o algunos de sus atributos.

- Espera, Diego. Me surgen algunas dudas… en definitiva ¿me estás hablando del concepto de clave primaria de una tabla en un modelo relacional? ¿Esos atributos que listas no serán las columnas de una tabla física que almacenará esa información? En definitiva, con la transacción, ¿no estás diseñando una tabla?

Page 34: Libro Genexus

34

- Sí, indirectamente. Y no: yo no me preocupé de “diseñar” una tabla física del modelo relacional. Solamente me limité a especificar los atributos con los que el usuario final interactuará al ingresar atracciones al sistema. Especifiqué una vista de usuario, no una tabla. Aquí entra lo de la ingeniería inversa y el Modelo Relacional “inferido” por GeneXus. Si no hubiesen más transacciones que ésta en la base de conocimiento, entonces GeneXus “inferiría” una tabla física compuesta exactamente por los atributos listados. Pero la cosa cambia si existen otras transacciones compartiendo algunos de los atributos mencionados. Allí habrá que “normalizar”. Pero no me quiero apurar, espera un poco que ya te contaré de esa parte.

- Creo que te voy entendiendo, Diego. Pero me llama la atención que pusiste en la estructura de la transacción atributos que no dijiste que hubieran mencionado los usuarios. Todos los que terminan con Id… Por otro lado, ¿por qué no utilizaste el nombre de la atracción como identificador?

- ¿Y si el nombre de la atracción pudiera repetirse para atracciones de distintos países y ciudades? Suponte: “Disney World” que existe en París, Miami, Las Vegas, Orlando. En este caso, necesitamos un atributo que dé unicidad al conjunto de elementos de información que constituyen una atracción. Toda estructura de transacción debe tener un atributo o atributos que identifiquen a cada instancia. En principio, en esta transacción, coincidirá exactamente con la clave primaria de la tabla “inferida”. Pero no siempre será así, como verás cuando te muestre un ejemplo de transacción con niveles. No te preocupes ahora. En nuestro caso, podremos decirle a GeneXus que AttractionId será numérico y que queremos que se numere automáticamente.

- ¿Y qué me dices de los atributos AttractionCategoryId, CountryId, CityId? ¿Más identificadores?

- Sí, serán identificadores en otras transacciones. Me explico: evidentemente los países y ciudades, así como las categorías de atracciones, corresponden en sí mismas a entidades independientes de la atracción turística. Por ejemplo, no hemos entrado a ver eso aún con este cliente, pero evidentemente los vuelos aéreos también tendrán país y ciudad (de origen y de

Page 35: Libro Genexus

35

destino). Es decir, resulta evidente que deberemos crear una transacción para representar e ingresar la información de países y sus ciudades, así como de las categorías de atracciones existentes. Esos identificadores de los que me preguntabas, serán, casualmente, los que identifiquen cada dato (instancia) de estos.

- A ver si entiendo: una vez que definas esas otras transacciones, GeneXus inferirá tablas para almacenar los datos que manipulan, y a partir de allí, se dará cuenta que esos atributos que colocaste en la estructura de la transacción Attraction, (AttractionCategoryId, CountryId y CityId) son los que se traducirían en claves foráneas en la tabla relacional asociada.

- ¡Sigues siendo la misma luz de siempre, Mary! – exclamó Diego con ojos iluminados.

- Pero entonces, ¿qué hacen en esa estructura CountryName, CityName, AttractionCategoryDescription? En un modelo relacional normalizado nunca podrían estar en esa tabla.

- Es que no lo estarán. Por eso te decía que la estructura de una transacción no se corresponde exactamente con la de la tabla física asociada. Ya te lo explicaré, no quiero abrumarte con detalles. Traeré mi notebook al próximo encuentro… porque lo habrá, ¿no? –inquirió Diego vacilante, y prosiguió sin esperar respuesta–, y te mostraré…

Ahora el lector verá los pasos que siguió Diego en la creación de la transacción mediante el IDE de GeneXus al día siguiente en su trabajo.

Primero que nada, seleccionó el link New Object de la Start Page y apareció a su vista la ventana cuyo diálogo permite la creación de un objeto dentro de la KB. De la caja Select a Type, con la lista de objetos GeneXus disponibles, seleccionó Transaction y luego, en la celda Name escribió el nombre “Attraction”, quedando una imagen semejante a la que se muestra a continuación.

Page 36: Libro Genexus

36

A continuación, pulsó [Create], tras lo cual la transacción se abrió. Obsérvese en la imagen siguiente que aparece un nuevo tab en la ventana principal con el nombre de la transacción. ¿Qué se está editando? Una parte de la transacción: su estructura.

La ventana de edición de la estructura contiene una serie de columnas que el desarrollador utiliza para crear un atributo; es decir, darle un nombre, un tipo de dato, una descripción, etc.

Los objetos que se abren en la ventana principal del IDE aparecen como tabs, donde uno solo, el activo, se presenta en la ventana central. El objeto abierto puede tener varios elementos o partes, en cuyo caso será uno el activo en cada oportunidad. En el tope inferior de la ventana aparece una barra de acceso que permite, mediante Selectores, seleccionar otra de las partes del objeto activo.

Page 37: Libro Genexus

37

Corresponde en GeneXus, a la estructura dibujada por Diego en una servilleta de papel. Diego asignó nombres a los atributos utilizando lo que se conoce como nomenclatura GIK (GeneXus Incremental Knowledge), un estándar creado por Artech y adoptado por la Comunidad, cuyo fin es dotar a un atributo de un nombre único que transmita lo más claramente posible su concepto, su semántica.

Usted habrá deducido correctamente que la columna Type pretende dotar al atributo con alguno de los tipos de datos soportados por GeneXus.

Selectors

Page 38: Libro Genexus

38

El tipo de datos Blob posibilita el almacenar una gran diversidad de tipos de información (videos, planillas, documentos de todo tipo, archivos de música, imágenes digitalizadas, etc.) en la propia base de datos. La idea del atributo AttractionImage es justamente almacenar una imagen de la atracción, que podrá ser un archivo bmp, jpg, etc.

La columna Description pretende que se asigne una descripción ampliada del atributo. Por ahora dejaremos las sugerencias de GeneXus a partir del nombre del atributo.

Para continuar agregando atributos, simplemente pulse <Enter> luego de terminada una definición y repita el mecanismo.

Atributos Clave. Como puede usted apreciar, el atributo clave que Diego

indicaba con un asterisco en la servilleta, se indica en GeneXus por el ícono de una llave.

Cuando Diego estaba asignando el tipo de datos en la columna Type para el

atributo AttractionName, consideró oportuno crear un dominio Name para todos los atributos que fueran nombres “de algo”, así como uno Id para todos los que tuvieran la característica de ser identificadores numéricos.

Usted estará ya acostumbrado a trabajar con Dominios. Si no es así, lea el siguiente párrafo que le aclarará la cuestión.

¿Dominios? Es común en las aplicaciones tener atributos que comparten las

mismas definiciones de tipo de dato, tamaño y algunas otras características. Por ejemplo, en la transacción Attraction hay varios atributos que son Id (AttractionId, AttractionCategoryId, CityId, y CountryId). Diego ha creado un dominio al que llamó Id, con todas las características propias de los identificadores y se lo aplicará a todos aquellos atributos que tengan la funcionalidad de representar un valor inexistente en la realidad pero necesario para lograr la unicidad.

Page 39: Libro Genexus

39

Definición de Dominios

Diego hizo doble clic sobre el nodo Domains en el árbol del Folder View

tras lo cual se abrió en la ventana principal el tab que muestra y permite editar dominios. Ingresó así los dominios Id y Name. El mecanismo es semejante al de crear atributos en una transacción.

Existen algunos dominios que son creados automáticamente por GeneXus cuando se crea la KB.

Teniendo los primeros dominios definidos, Diego volvió a la transacción, sustituyó por estos dominios y en ese momento se percató de que le faltaba definir un dominio Description para AttractionCategoryDescription. En vez de volver a la ventana Domains, se posicionó en la columna Type del atributo y escribió “Description=Character(20)”2. Al abandonar el campo, ese dominio se crea automáticamente. Luego grabó y pudo ver:

2 En general en GeneXus existen varios caminos que le permiten realizar lo mismo, para que el efectuar su tarea le resulte siempre cómodo.

Cada dominio es un conjunto de características únicas que pueden ser compartidas por varios atributos. Esto proporciona consistencia y facilita el mantenimiento, ya que cambiando alguna de las características de un dominio se provocará la propagación del cambio a todos los atributos basados en él.

Page 40: Libro Genexus

40

Observe que el asterisco que seguía al nombre de la transacción en el tab desapareció (el asterisco indica que los cambios que se han realizado aún no se han grabado) y que el selector “Structure” cambió de color. Atributo Descriptor. Ahora también apareció un ícono con la imagen de una lupa, precediendo al atributo AttractionName. Es una forma de indicar que si uno tuviera que quedarse con uno solo de todos los atributos, de forma tal que sea el que mejor describa a la entidad, ése será el elegido (el de mayor carga semántica). Ahora no nos preocuparemos de esto, servirá a herramientas que automaticen. La Tabla ATTRACTION

Por defecto, cuando GeneXus diseña las tablas les asigna como nombre el mismo que el de la transacción en la que están basadas3. En todo momento puede verse el diseño de la tabla (su estructura, índices, etc.) abriendo el nodo Tables del árbol que se encuentra en el panel Folder View y haciendo doble clic sobre el nombre de la tabla. Para ATTRACTION se verá:

3 En lo que sigue en este material, para diferenciar los nombres de las tablas de los de las transacciones usted verá a las tablas escritas en mayúsculas.

Page 41: Libro Genexus

41

Dada la estructura de la transacción, GeneXus diseñará también un form para la misma, que será la pantalla a través de la cuál el usuario ingresará atracciones cuando la transacción sea convertida en programa ejecutable. Puede verse escogiendo el selector WebForm4 de la barra de acceso:

4 Con GeneXus X usted podrá generar exclusivamente aplicaciones Web 2.0. Para generar una aplicación Win, podrá utilizar GeneXus X Evolution 1. Allí la transacción presentará también un WinForm.

Page 42: Libro Genexus

42

Este form podrá ser personalizado, moviendo de lugar, insertando, modificando, eliminando controles, cambiando su aspecto, etc.

Conforme con el resultado, Diego recordó que no había documentado nada sobre la transacción. Es norma de los integrantes de ACME el ir documentando a medida que se producen los avances, sobre todo en lo que respecta a las entidades, tal como la transacción Attraction.

Así que abrió Main Document, insertó una nueva tabla, esta vez titulada “Objects” y agregó en la primera fila un link hacia la transacción recién creada (del mismo modo en que lo hizo Julia varias páginas atrás, al incluir los links a los documentos), junto con una breve descripción.

Page 43: Libro Genexus

43

Podemos ver Attraction como link. ¿Qué sucederá cuando Diego haga clic sobre el mismo? Se abrirá un nuevo tab de documentación, esta vez de la transacción, donde podrá editarla, e incluso abrir la propia transacción:

Supóngase que necesita abrir la transacción para mirar su estructura antes de documentar. Pues siguiendo el link Open Transaction: Attraction, podrá

Page 44: Libro Genexus

44

hacerlo, y luego para efectivamente ir a la ventana de edición de la documentación, podrá directamente elegir el selector Documentation. Como podrá constatar usted mismo, estimado lector, en GeneXus también se aplica aquello de que “varios caminos conducen a Roma”.

Creando la segunda Transacción: Country

Luego Diego se abocó a crear la transacción que contendría la información inherente a los países y ciudades. Tenía dibujada la estructura en la servilleta de papel que guardó en su bolsillo cuando se despidió de Mary en el encuentro anterior:

Page 45: Libro Genexus

45

Con Main Document abierto en edición, agregó un nuevo link en la tabla,

ahora a la transacción Country. Pero ¡todavía no la había creado! Observe que aparece un link, sí, pero sobre un símbolo ?:

Page 46: Libro Genexus

46

Cuando Diego haga clic sobre ? automáticamente se creará la transacción que aparecerá abierta en la estructura, lista para que él comience a ingresar los atributos, y el link quedará ahora definido en Main Document. (Sin ánimo de ser reiterativos: ¿cómo era aquello de los caminos a Roma?).

Country: una Transacción de dos niveles

Como todos sabemos, los países contienen ciudades, es decir, la realidad dictamina que se tienen n cantidad de ciudades por país. GeneXus dispone de una forma muy sencilla y práctica de representar esta realidad, y lo hace a través de transacciones de dos niveles.

La transacción Country consta entonces de dos niveles: el primer nivel, también conocido como “prólogo” o simplemente “cabezal”, queda de hecho implícito, por lo que no es necesario identificarlo. En este caso, los atributos del prólogo implican que habrá una sola instancia para cada país.

Sin embargo, como existen varias ciudades para un mismo país, se recurre a una representación (en el papel: se recurre a un juego de llaves antecedidas de un nombre para el nivel) que determina un grupo de atributos que son repetitivos para el prólogo: se lo conoce como “cuerpo” o segundo nivel.

Ahora, note usted que ambos niveles (cabezal y cuerpo) tienen identificadores propios: el atributo CountryId es el identificador del primer nivel, y el atributo CityId es el identificador del segundo nivel, lo que significa que para un país dado (CountryId) no puede repetirse la misma ciudad (CityId).

A partir de esta estructura, GeneXus determina que debe crear dos tablas: la tabla COUNTRY y la tabla COUNTRYCITY. En la primera, la clave primaria es CountryId, mientras en que la segunda, la clave primaria está compuesta por CountryId y CityId.

La siguiente imagen muestra cómo quedó la estructura luego de que Diego la ingresara5.

5 Obsérvese que aparecen nuevos dominios. Es de suponer que Diego los creó, o bien antes o bien en el mismo momento de ingresar estos atributos, por ejemplo, al ingresar CountryFullName, en la columna Type pudo haber digitado: “LongName = Character(50)” con el mismo resultado. Suponiendo que Diego ya tuviera definido un dominio Details como VarChar(500,200),

Page 47: Libro Genexus

47

Como CountryId y CityId ya existen en la KB (recuerde que fueron creados al grabar la transacción Attraction) al comenzar a digitar las primeras letras de cada uno, GeneXus le abrirá una lista conteniendo los atributos de la KB que empiezan igual, de modo tal que usted pueda seleccionar el que corresponda y evitar seguir digitando el nombre completo. Ídem con CityId y CityName.

Normalización de la Base de Datos

Al definir la transacción Country se produjeron algunos cambios importantes. En particular, la tabla ATTRACTION ha variado en su composición por la sencilla razón de que GeneXus la ha normalizado en virtud de las nuevas dependencias funcionales que se introducen. Vea las composiciones de las tablas ahora.

también es interesante destacar que cuando inserte el atributo CountryDetails, GeneXus ya inferirá como su tipo de datos, el dominio Details (ya que lo contiene como última parte de su nombre).

Para insertar un segundo nivel en una transacción, sitúese en el atributo anterior y pulse <Ctrl+L> (en este caso, sitúese sobre el atributo CountryFlag). Luego prosiga con el resto de los atributos según el mecanismo natural. También lo logra haciendo botón derecho sobre el atributo, donde se le desplegará un menú contextual para, por ejemplo, insertar un nuevo nivel, mover el atributo de lugar, etc.

Page 48: Libro Genexus

48

Recuerde que una dependencia funcional X Y es una restricción entre dos conjuntos de atributos de la base de datos, que establece que si para 2 registros coinciden los valores de X, entonces también tienen que coincidir los valores de Y. Esto significa que los valores de los atributos en Y en cualquier registro, dependen de los valores de los atributos en X, o están determinados por ellos.

¿Percibe los cambios? Al crear la transacción Country, y al designar a los atributos CountryId y CityId como identificadores de sus respectivos niveles, se está diciendo que: 1. Todo país se identificará (de manera única) por el valor de CountryId y que

este valor tendrá asociado (determinará) un CountryName (y sólo uno), CountryFullName, etc. (establece esas dependencias funcionales). En un Modelo Relacional normalizado, esto deriva en una tabla física con clave primaria CountryId para almacenar esta información respetando esta unicidad establecida.

Page 49: Libro Genexus

49

2. Toda ciudad se identificará para un país dado (CountryId), por el valor de CityId, y que por tanto, este par (CountryId, CityId) determinará un CityName y CityInformation. De esto se desprende que deberá existir una tabla física con clave primaria {CountryId, CityId} para almacenar la información de cada ciudad6.

Pero entonces: ¿qué hace GeneXus con los atributos de igual nombre en

diferentes transacciones? Teniendo presentes los dos enunciados mandatarios en la filosofía GeneXus: • Conceptos iguales deben tener el mismo nombre de atributo, y • Conceptos diferentes no deben tener el mismo nombre de atributo. encontrar atributos de igual nombre en distintas transacciones sólo puede querer decir una cosa: ¡son lo mismo!. Entonces ¿qué puede hacer GeneXus para mantener la base de datos inferida normalizada? Transformar en la tabla ATTRACTION los atributos CountryId y CityId en claves foráneas, y quitar, por tanto, a los atributos CountryName y CityName de dicha tabla (pasan a poder “inferirse” a través de los anteriores; si se dejaran se tendría información redundante) Atributos: almacenados e inferidos

Aquí viene a cuenta la pregunta que Mary le hiciera a Diego sobre la confusa presencia de los atributos CountryName y CityName referenciados en la estructura de la transacción Attraction, dado que no van a estar presentes en la tabla física asociada… ¿para qué Diego los colocó en Attraction entonces?

Si el lector revisa unas páginas atrás (página 34) y observa el form de la transacción, podrá constatar que aparecen esos atributos. ¿Para qué?

La transacción se va a convertir en un programa ejecutándose. El usuario final va a acceder a la transacción mediante su Browser, y a través de una

6 Observe que GeneXus nombra la tabla concatenando el nombre de la transacción al nombre del nivel: COUNTRYCITY

Page 50: Libro Genexus

50

pantalla cuyo diseño será el del WebForm, podrá, por ejemplo, ingresar una nueva atracción (nuevo registro en la tabla ATTRACTION subyacente). Sería deseable que cuando el usuario final colocara un identificador de país en el campo correspondiente a CountryId, y uno de ciudad en CityId pudiera visualizar automáticamente el nombre de dicho país, CountryName y de dicha ciudad, CityName. Es decir, que CountryName y CityName sean traídos en ejecución de las tablas COUNTRY y COUNTRYCITY a través de la clave foránea {CountryId, CityId}. Para poder colocar en el form atributos a los que se llega a través de claves foráneas, es necesario que estén presentes, inferidos, en la estructura.

¿Qué sucedería si el usuario digitara para CountryId, CityId un par de valores inexistentes? ¿Y si mediante la transacción Country se quisiera eliminar una ciudad para la que existieran atracciones? Si se nos permitieran hacer estas operaciones, se estaría violando una de las condiciones más importantes de los modelos relacionales para mantener la consistencia de los datos: la integridad referencial. De hecho GeneXus agregará automáticamente en las transacciones el código necesario para realizar estos controles sin que el desarrollador se moleste en hacerlo. Integridad Referencial

Diego comunicó a Julia que ya había definido las dos primeras transacciones. A los efectos de comprobar que el modelo reflejara la realidad, ella hizo un Diagrama de Bachman (diagrama de relaciones entre tablas) sobre las tablas inferidas por GeneXus a partir de las transacciones.

Para ello expandió el nodo Tables del contenedor Folder View, luego, posicionada sobre cualquier tabla, pulsó el botón derecho para abrir el menú contextual y escogió New/Object, tras lo cual se abrió la ventana Create New Object donde seleccionó el objeto Diagram para finalmente pulsar [Create]. Seleccionó las tres tablas simultáneamente y las arrastró hacia la hoja en blanco, obteniendo el siguiente resultado.

Page 51: Libro Genexus

51

En este tipo de diagrama, la punta simple de la flecha representa la existencia de una instancia de la tabla apuntada para cada instancia de la otra; es decir: para cada ciudad existe sólo un país; para cada atracción turística existe una sola ciudad. Recíprocamente, la punta doble representa la ocurrencia de varias instancias de la tabla apuntada para cada instancia de la otra, es decir: para cada país existen muchas ciudades relacionadas; para cada ciudad muchas atracciones.

Esto permite determinar las relaciones entre ellas. Por ejemplo, la relación entre COUNTRY y COUNTRYCITY es de 1 a N (1 a muchos) y viceversa es N a 1 (muchos a 1). La de COUNTRYCITY con ATTRACTION también es 1 a N.

Para asegurar la integridad referencial, deberá entonces controlarse que cuando se inserte o modifique un registro en la tabla ATTRACTION, exista el registro relacionado en la tabla COUNTRYCITY. Y cuando se elimine un registro de la tabla COUNTRYCITY, no existan registros en la tabla ATTRACTION relacionados. GeneXus resuelve esto automáticamente, agregando esta lógica a las transacciones Attraction y Country, en forma transparente para el desarrollador. Análogas consideraciones pueden hacerse

Page 52: Libro Genexus

52

respecto a los controles sobre las tablas relacionadas COUNTRY y COUNTRYCITY.

Concepto de Tabla Base y Tabla Extendida

Mientras Julia comprobaba que estuviera todo bien, al recuerdo de Diego vinieron fragmentos de la última conversación en el bar:

- Mira, Mary, por una cuestión práctica, le llamamos tabla base, en el contexto que sea, a la tabla en la que estés “parada” en un momento dado, la que sea foco de tu atención en ese momento, en el que estés queriendo hacer algo con sus registros. Así es que en la transacción Attraction, decimos que su tabla base es la del mismo nombre, porque siempre que trabajes con la transacción, vas a trabajar con un registro de esa tabla, ya sea para insertarlo, modificarlo, o eliminarlo. Y luego vas a seguir por otro, y por otro y por otro. Pero además, cuando estás trabajando con un registro individual de una tabla base, podrías acceder también a la información que está unívocamente relacionada con él, de otras tablas.

- Ajá, siguiendo las relaciones N a 1, desde el registro de la ¿dijiste tabla base? en la que estemos parados, hasta llegar al registro que tiene la información que queremos. Sí, no hay nada nuevo en eso.

- Es verdad. Solo la terminología: le llamaremos tabla extendida de una tabla base dada, al conjunto de las tablas a las que se llega partiendo de la tabla base, a través de relaciones N a 1.

- A ver si entiendo… Si no fuera por la necesidad de tener la información desperdigada en varias tablas para evitar redundancias, la tendrías toda en una misma tabla física. En el caso de las atracciones, tendrías una sola gran tabla con todos los atributos de ATTRACTION, más los atributos de COUNTRYCITY, más los de COUNTRY. No puedes, por lo de las posibles inconsistencias que provienen de hacer los datos redundantes, pero de todas formas le das un nombre a la tabla que tendrías si sí pudieras: y ese es el

Page 53: Libro Genexus

53

concepto de tabla extendida. Una tabla que físicamente no existe, pero que te la imaginas.

- Absolutamente cierto. Verás que utilizamos el concepto a lo largo y ancho de GeneXus y por eso se le ha puesto un nombre.

Mientras Diego permanecía absorto en sus recuerdos, Julia comprobó que se cumplían correctamente las necesidades de acceso a la información entre las tablas.

Comenzando a Prototipar

Para poder dejarle paso a Mike, y que éste pueda comenzar a jugar su papel de tester del proyecto, Diego pulsó <F5> para dar comienzo a la prototipación.

Recuerde que ya se había seleccionado el ambiente cuando se creó la KB, de modo que varias propiedades ya aparecían configuradas7, entre ellas el DBMS Default (SQLServer). Pero aún falta algo para poder implementar el sistema… Un asistente (wizard) le pedirá a Diego la información del servidor de base de datos (DBMS), y la base de datos, por esta única vez.

7 Si desea modificar alguna de ellas, alcanza con seleccionar el panel Preferences del contenedor Knowledge Base Navigator y allí buscar la preferencia a configurar, por ejemplo el DBMS asociado, o el lenguaje de implementación, etc. y con <F4> sus propiedades (en la ventana que se abre).

Mirando un Diagrama de Bachman es fácil determinar cuál es la tabla extendida de cada tabla (base). Todas las tablas a las cuales se pueda llegar siguiendo las flechas de punta simple (relaciones N-1) desde la tabla base, formarán parte de su tabla extendida. La tabla extendida no tiene existencia física, sino sólo conceptual. Nos permitirá referirnos a la información unívocamente relacionada con el registro en el que estemos posicionados.

Con GeneXus no hay diferencia entre prototipar e implementar el sistema. La prototipación no es ni más ni menos que la aplicación generada en una plataforma determinada: ¡lo mismo que la aplicación final! La diferencia entre una cosa y otra es solamente el uso que se le da.

Page 54: Libro Genexus

54

Su DBMS es Microsoft SQL Server 2005 Express y se encuentra instalado en el servidor DevelopeSrv de la red. El nombre que Diego dará a la base de datos es TravelAgencyTest:

Si la base de datos aún no existe, podrá crearla presionando [Edit conection]

y luego el link Create database. También puede posponerlo y GeneXus lo hará automáticamente unos pocos pasos más adelante.

Luego GeneXus procederá a la generación y ejecución del Developer Menu8, a fin de que los desarrolladores puedan testar inmediatamente las aplicaciones. Una vez que el ambiente está completamente configurado, cada vez que se presione <F5> GeneXus procederá a efectuar todos los pasos pendientes, necesarios para ejecutar la aplicación. Diego esperaba ver lo que se observa en la siguiente imagen: el primer Análisis de Impacto de esta base de conocimiento.

8 Un sencillo menú de prototipación creado por GeneXus (es un xml conteniendo links a los distintos objetos GeneXus creados, para poder invocarlos en forma fácil y rápida como se verá en breve).

Page 55: Libro Genexus

55

El Impact Analysis Report (IAR) que estaba observando le decía que se iban a crear las tablas que se mostraban a la izquierda, y cuyas estructuras y demás información como índices, etc. 9, podían verse a la derecha.

¿Qué es el Impact Analysis Report? GeneXus diseña a partir de la base de conocimiento la base de datos. En todo momento pueden hacerse modificaciones a los objetos existentes, o pueden crearse nuevos objetos, modificándose la KB. El IAR es el resultado de un análisis que realiza GeneXus del impacto causado por las definiciones nuevas o cambios del modelo de desarrollo en una KB sobre la base de datos física asociada. Por tanto, este análisis tiene por finalidad informar al desarrollador sobre los

9 Obsérvese que abajo se muestran las sentencias SQL que se utilizarán para reorganizar (en este caso crear) esa tabla.

Page 56: Libro Genexus

56

cambios estructurales que GeneXus deberá llevar a cabo sobre la base de datos para dejarla en el nuevo estado, consistente con la KB.

De conformidad con el IAR, Diego pulsó [Create] dando paso a la reorganización de la base de datos10. Como en este primer paso ésta no existía, se va a crear. Será en sucesivos cambios que será reorganizada, en tanto reestructurada. Ahora bien, ¿cuándo se generan los programas? ¡Inmediatamente, listos para ser probados! Desde que Diego presionó <F5> e ingresó la información que le faltaba a la plataforma (nombre de BD y servidor), hasta que se le abrió una ventana del Browser con el Developer Menu listo para ejecutar, no pasó más de un minuto. Diego estudió detenidamente unos listados desplegados por GeneXus antes, que le permitieron ver que iba por buen camino… pero no se apure, estimado lector, que sobre este punto volveremos más adelante y Mike está un poco ansioso por entrar en escena. Mike 007… con licencia para matar

Era su turno. Debía destrozarlas. A las transacciones, claro. Con su vista fija en el Developer Menu (desplegado en el Browser tras el

<F5>) conteniendo links a las transacciones, entrecruzó sus dedos, los hizo sonar, y tras una breve pausa, se dijo “allá vamos”.

10 El término reorganizar refiere a efectuar cambios físicos.

Page 57: Libro Genexus

57

La idea era comenzar cuanto antes a probar, cargando información en la base de datos recién creada por GeneXus y verificar el comportamiento de las transacciones.

Pulsó sobre Country y apareció ante su vista una imagen semejante a la que se observa a continuación.

Esta es la transacción Country en plena ejecución.

Page 58: Libro Genexus

58

Inmediatamente se percató de que el atributo CountryId debería ser

autonumerado, es decir, que el usuario no debería preocuparse por asignarle un valor específico ya que CountryId no representa nada de la realidad, es sólo un número que otorga unicidad al registro a ser insertado.

Asimismo, cuando Mike se enfrentó a CountryWorldRegion, atributo numérico de apenas un dígito, no supo qué valor numérico representaba cada región del mundo (¿cómo indicar que se trata de un país africano, norteamericano, europeo, etc.?) “¡Hay que mostrarle al usuario final los nombres de las regiones y continentes para que escoja el apropiado!”, se dijo.

Así que decidió iniciar con Diego una conversación sobre el tema, de forma tal que todos estuvieran de acuerdo sobre lo que proponía. Volvió a la KB, seleccionó Country y luego ingresó a su sección de documentación. Agregando conversación

Entre los selectores que se presentan al abrir la documentación, aparece uno: Talk, que tiene por finalidad permitirnos crear un espacio donde establecer un diálogo con otros usuarios sobre el objeto en cuestión.

Note que el texto indicador superior está declarando un documento Talk para la transacción y que finaliza con un signo de interrogación. Este signo indica que aún no existe una página Talk para este objeto, y antes de comenzar

Page 59: Libro Genexus

59

a escribir, ésta debe ser creada. Cuando Mike salve este espacio de memoria, se generará una página con su contenido.

Al cliquear sobre el signo de interrogación, cambió al estado de edición e ingresó el texto, grabó, y pudo ver:

Determinando un Atributo Identificador como Autonumber

Rato después, en cuanto Diego leyó lo que había escrito Mike, contestó positivamente en el Talk de la transacción Country y se dispuso a realizar los cambios.

Quería que se numerara en forma automática el atributo CountryId, identificador numérico, utilizando la funcionalidad Autonumber de los DBMSs que la soportan.

Todos los objetos de GeneXus contienen propiedades que afectan tanto su apariencia como su comportamiento.

Las propiedades de todo objeto, atributo, etc. pueden editarse en cualquier momento con <F4> y se abrirán en una ventana flotante.

Page 60: Libro Genexus

60

Por lo que Diego se posicionó sobre el atributo CountryId y en la estructura de la transacción Country pulsó <F4>11.

Primeramente, asignó el valor “True” a la propiedad Autonumber, y configuró las propiedades como mostramos en la figura.

11 Sin necesidad de entrar a la transacción, también puede buscarse el atributo para editar sus propiedades con <F4>, a través de la ventana Attributes, que se accede mediante View/Work With Attributes. Las propiedades que se modifiquen de cualquiera de las dos maneras aplican al atributo en general, no al atributo en una determinada transacción en particular.

Page 61: Libro Genexus

61

Cirugía estética para un atributo

Para completar el siguiente pedido de Mike en el Talk, Diego se situó en el WebForm de la transacción, se posicionó sobre el control atributo CountryWorldRegion y pulsó <F4> (Diego tiene la ventana de propiedades con Auto Hide, así que ahora la ventana Properties se desplazó hacia afuera). Ya en la ventana de propiedades del control, en la propiedad ControlType seleccionó “Combo Box”; a continuación, seleccionó la propiedad Values para abrir la ventana Values Editor y cargó los valores para el atributo. Eso fue todo.

Page 62: Libro Genexus

62

Si en lugar de haber hecho esto específicamente en las propiedades del control CountryWorldRegion del form lo hubiese hecho en las propiedades del atributo CountryWorldRegion en la estructura, habría hincado el bisturí más a fondo: significaría que en todos los forms donde se colocase ese atributo, por defecto se mostrase como Combo Box, con la definición indicada y no como control Edit. Cuando se percata de su distracción, Diego decide hacer esa definición a nivel de la estructura. Aquí queda claro cómo es que el conocimiento se infiere en la base de conocimiento, y es reutilizado.

Pero allí reconoce que lo más conveniente, sin embargo, será crear un dominio enumerado para ser utilizado por cualquier atributo que quiera representar regiones del mundo, en particular, CountryWorldRegion. Edita entonces los dominios y crea uno nuevo, WorldRegion de tipo Numeric(1.0), y en la ventana de propiedades (<F4>) se posiciona en la propiedad Enum values y pulsando sobre el botón abre la ventana para ingresar los valores que tomará el dominio enumerado:

Page 63: Libro Genexus

63

De esta manera evitará en la aplicación tener que recordar que el valor 8 correspondía en su sistema a South America, y a partir de allí, cuando se quiera referir a ese valor, escribirá WorldRegion.SouthA, que será sin duda más fácil de recordar que 8.

Por tanto edita las propiedades del atributo CountryWorldRegion (por ejemplo, posicionándose en ese atributo en la estructura de Country y pulsando <F4>) y modificará su tipo de datos para que ahora sea del dominio WorldRegion.

Page 64: Libro Genexus

64

Observará inmediatamente que el valor de la propiedad ControlType del atributo automáticamente pasa a ser “Combo box”, y sus valores los del enumerado.

Reorganizando (reorg)

Diego cambió propiedades de dos atributos de la transacción Country, ¿esto indirectamente disparará algún cambio en la tabla física? Veamos el siguiente Impact Analysis Report que se observa luego de que pulsara <F5>:

Page 65: Libro Genexus

65

Sí, GeneXus encontró que debe realizar una reorganización de la tabla.

Note que ahora aparece el texto “Autonumber” a la derecha del tipo de datos en el atributo CountryId. La reorganización deberá “encender” esa propiedad a nivel del DBMS. Pero, ¿qué pasó con el otro cambio, con el de CountryWorldRegion? ¡Nada! No se trata de un cambio a efectuar sobre la base de datos, sino que solamente vale a nivel de la KB: el dominio WorldRegion sigue siendo de tipo Numeric(1.0) por lo que el atributo CountryWorldRegion no cambia su tipo a nivel de la base de datos. El cambio principal será estético: en todos los forms donde se presente ese atributo como control, lo hará como Combo Box con los valores del dominio enumerado.

Aquí se evidencia que no todos los cambios que se efectúen en un objeto GeneXus necesariamente provocarán una reorganización. Veremos otro ejemplo de ello a continuación cuando hablemos de las reglas de una transacción.

Page 66: Libro Genexus

66

Diego puede decidir pedirle a GeneXus que haga efectivamente los cambios

informados ([Reorganize]) o cancelar la reorganización, con lo cual la próxima vez que se presione <F5> volverá a realizarse el IAR. Elige reorganizar.

Al releer lo charlado en el Talk, con intenciones de contestar que todo fue hecho, encuentra que Mike proponía que por defecto todos los Ids de transacciones fueran autonumerados. (Su confusión vino a raíz de que Mike, en lugar de haber documentado esto en el Talk del Main Document, lo hizo en el de la transacción Country). Pero entonces, para implementar lo sugerido, ¿tendría que ir una por una a cambiar la propiedad Autonumber de esos atributos? ¡No!, pronto recordó que todos ellos estaban basados en el dominio Id. Alcanzaba con editar el dominio, sus propiedades y allí, de igual manera que se hizo para el atributo CountryId, “encender” la propiedad Autonumber a nivel del dominio. ¡Todos los atributos basados en el presente y en el futuro en ese dominio, automáticamente heredarán la propiedad!

Pero…“¡#@!#!@...!”, maldijo en voz alta. “CityId no es clave primaria simple”. Para dotar a un atributo con la característica de autonumerado, éste debe ser clave primaria simple. CityId es “parte” de la clave primaria de COUNTRYCITY12.

¿Cómo hacer, entonces, para que este atributo no tome la propiedad Autonumber como “True” dada a su dominio Id?13 Sencillo. Se editan las propiedades del atributo CityId (como antes se hiciera con CountryId) y se modifica la Autonumber, llevándola a “False”. Se está cambiando, pues, el comportamiento por defecto dado por el dominio.

Lo mismo puede decirse del atributo AttractionCategoryId que por el momento es de Attraction y no es clave primaria, aunque pronto lo será cuando Diego cree la nueva transacción AttractionCategory, unos pasos más adelante. Por esta razón, decidió dejar la modificación en la definición del dominio Id

12 Para dar valores consecutivos a este atributo, habrá, pues, que apelar a una regla: Serial. Le proponemos la investigue luego de ver algunas reglas, en la sección siguiente. 13 De todas formas, como GeneXus sabe de esta restricción, si Diego no se hubiera percatado del problema, GeneXus igualmente hubiera actuado con inteligencia y no hubiera definido el atributo en la base de datos con autonumber. A efectos didácticos preferimos hacer trabajar a Diego.

Page 67: Libro Genexus

67

(para “encender” la Autonumber) para más adelante, cuando cree la nueva transacción (dejando este “To-Do Diego” que, creado seleccionando el objecto Document desde la ventana New Object, al guardarlo quedó colgado en Orphan Documents).

Transacciones: no sólo una cara bonita, también personalidad

Diego decidió incorporar a las transacciones el comportamiento que los usuarios habían descrito respecto a la manipulación de los datos.

“Nunca debe ingresarse al sistema una atracción a la que no se le haya dado un nombre”. Abrió, por tanto, la transacción Attraction, pulsó sobre el selector Rules y escribió lo siguiente: Error('Attraction must have a name')

if AttractionName.IsEmpty();

En ejecución, si el usuario dejara vacío el campo “Attraction Name”, le aparecerá el mensaje indicado y el control quedará en el mismo campo sin dejarle continuar. Tampoco se le permitirá grabar el registro en la tabla correspondiente.

“Nunca debe ingresarse al sistema un país sin nombre y nombre completo” “Sí se permite no ingresar detalles del país, pero en este caso se le quiere dar un mensaje al usuario, advirtiéndole por si lo dejó vacío por distracción”. Seleccionó, pues, el selector Rules de la transacción Country y escribió las siguientes tres reglas en un orden cualquiera: Error('Country must have a name')

if CountryName.IsEmpty(); Error('Country must be in full')

if CountryFullName.IsEmpty(); Msg('No country datails were provided. Do you want to continue?')

if CountryDetails.IsEmpty();

Page 68: Libro Genexus

68

La tercera regla también emite un mensaje, pero a diferencia de la Error, no traba el paso del usuario hacia el campo siguiente, y permite grabar el registro.

Existen muchas más reglas, que permiten programar el comportamiento específico que tendrá la transacción. Obsérvese que se escriben siguiendo un estilo declarativo: salvo excepciones no importa el orden. En nuestro caso, se dispararán de acuerdo al orden en que se encuentren los atributos CountryName, CountryFullName y CountryDetails en el form.

En una transacción de dos niveles, se están manipulando registros de dos tablas. Por tanto, cuando el usuario ingresa un nuevo país y sus n ciudades mediante el form de la transacción Country, se estarán insertando n+1 registros: 1 en la tabla COUNTRY y n en la tabla COUNTRYCITY. Se insertarán en el orden en que se hayan ingresado en el form. Puede querer dispararse una regla inmediatamente antes o inmediatamente después que una grabación de estas se efectúe. Para ello las reglas pueden condicionarse no sólo con cláusulas if como las mostradas, sino también con eventos de disparo como son el BeforeInsert o el AfterInsert.

Luego de grabarse los n+1 registros de la transacción (cabezal y sus n líneas) se realiza automáticamente un Commit. Puede ser deshabilitado por el desarrollador mediante una propiedad de la transacción. También existen eventos de disparo de reglas, que capturan el momento inmediatamente anterior a realizarse el Commit, o el inmediatamente posterior. Por ejemplo, si quisiera realizarse un listado del país recién ingresado con sus respectivas ciudades, podría invocarse a otro objeto GeneXus de otro tipo: Procedimiento, asegurándose de invocarlo luego de haberse insertado los n+1 registros, enviándole por parámetro el identificador de país recién ingresado… y si se quiere que sea antes del Commit se escribirá en la sección de reglas:

PrintCountry.call( CountryId ) on BeforeComplete;14

14 Obsérvese que se está utilizando un método de invocación corriente en GeneXus: el Call, que está apareciendo aquí como una regla. También obsérvese que lo que precede al call es el nombre del objeto invocado, en nuestro caso será otro objeto GeneXus de tipo Procedimiento, y por último, obsérvese que los eventos de disparo aparecen identificados en la sintaxis de una regla: Rule if condition on Event

Page 69: Libro Genexus

69

Y si se quiere que sea luego del Commit se escribirá:

PrintCountry.call( CountryId ) on AfterComplete;

Diego deja documentadas las modificaciones efectuadas. Piense cuáles fueron los cambios en la KB desde el <F5> anterior. Lo que se esconde tras el <F5> 1. Lo primero que hace GeneXus tras un <F5> es un análisis del impacto que

tendrá los cambios efectuados en la KB, sobre la base de datos. En caso de que haya impacto, desplegará el Impact Analysis Report que puede ser confirmado, en cuyo caso se reorganizará físicamente la base de datos, o cancelado, como ya vimos.

Sabiendo que lo único que modificó desde el anterior <F5> fue agregar

reglas a ambas transacciones, anticipa que cuando ahora vuelva a pulsar <F5> no habrá reorganización, pero sí los pasos que siguen.

2. GeneXus da paso a la etapa de especificación, que es un análisis de todo lo

definido en cada uno de los elementos que componen los objetos que usted haya cambiado, como por ejemplo, las estructuras, los forms, las reglas incorporadas, y más. Esta tarea da por resultado un listado de navegación en el cual GeneXus informa de la lógica que ha interpretado para cada objeto que haya sufrido cambios desde el <F5> anterior, junto con advertencias y errores (en caso de haberlos).

En el caso de nuestros amigos, dos objetos han cambiado: Country y

Attraction, por lo que esto será lo que Diego verá tras su <F5>:

…donde condition es una condición booleana y Event es uno de los eventos de disparo válidos, que invitamos al lector interesado a buscar en las distintas fuentes de documentación del producto. Listamos algunos: AfterValidate, BeforeInsert, BeforeUpdate, BeforeDelete, AfterInsert, AfterUpdate, AfterDelete, BeforeComplete, AfterComplete, etc.

Page 70: Libro Genexus

70

No hubo advertencias ni errores. Observe el lector cómo al elegir Country a la izquierda, le informa las tablas en las que realizarán las grabaciones y la integridad referencial que controlará cuando se pretenda eliminar un país, o una ciudad a través de esta transacción. 3. Luego de especificar, se pasa a la etapa de generar el o los objetos que

GeneXus entienda necesarios para que la ejecución pueda realizarse en forma

Page 71: Libro Genexus

71

completa (si hay objetos que no han variado, y fueron generados en un <F5> anterior, ¿para qué hacerlo nuevamente?). La generación implica la escritura de líneas de código que implementen la programación de los objetos en el lenguaje elegido por usted para el ambiente de desarrollo (por cada una de las dos transacciones de Diego, se generará entre otras cosas un archivo .cs. No olvide que él eligió C# como su lenguaje de desarrollo). Además mientras el desarrollador no cree su propio objeto principal, se creará un xml: el Developer Menu, que contendrá links a todos los programas (objetos) generados.

4. Por último, GeneXus compila los programas y ejecuta (hasta que no se cree un

objeto principal de la ejecución) el Developer Menu en el Browser instalado en esa máquina.

Todo este proceso es enteramente automático. Usted nunca tendrá que

especificar y/o generar un objeto en forma explícita, pues GeneXus es lo suficientemente inteligente como para determinar qué acciones disparar en cada momento.

Así fue que, avisado por Diego, previo paso por el Developer Menu, Mike ejecutó Country, ingresó unos cuantos registros, modificó algunos, eliminó otros, y documentó los resultados en la Documentation de la transacción otorgándole el status de okay.

Observe lo que sucedió cuando accidentalmente, teniendo al país 1, Argentina, en edición, borró su nombre y salió del campo. ¿Recuerda que Diego programó una regla Error con exactamente ese mensaje un poco antes?

Page 72: Libro Genexus

72

Le faltaba aún probar Attraction (habiendo encontrado cosas a modificar en Country, había decidido esperar un poco), cuando vio en el Talk de la misma que Diego le indicaba que postergara la prueba hasta que él creara la siguiente transacción relacionada. Creando la Transacción AttractionCategory

En la transacción Attraction, Diego nombró al par de atributos como AttractionCategoryId y AttractionCategoryDescription sabiendo que más tarde crearía una transacción AttractionCategory, que los contendría.

Page 73: Libro Genexus

73

La finalidad de esta transacción es categorizar los variados puntos de atracciones de una ciudad. Ejemplos de esto pueden ser las categorías “Buildings/Structures”, “Nightlife”, “Relaxation”, “Adventure”, “Safaris”, “Gastronomy”, “Business”, etc.

Con las estructuras de las transacciones Attraction y AttractionCategory estamos diciendo implícitamente que: una “atracción” pertenecerá a una y solo una “categoría”, mientras que una “categoría” podrá contener n “atracciones”.

El siguiente Diagrama muestra la nueva relación existente ahora entre las tablas involucradas hasta el momento en el proyecto.

GeneXus deberá reorganizar la base de datos en el próximo <F5>, creando la nueva tabla ATTRACTIONCATEGORY y quitando de ATTRACTION al atributo AttractionCategoryDescription, que pasará ahora a ser inferido, a partir de la nueva clave foránea, AttractionCategoryId.

Antes de dar <F5>, Diego recordó modificar el dominio Id para que fuera Autonumber (como había dejado anotado en la documentación para hacer cuando creara la transacción AttractionCategory).

Ahora sí, <F5>. Analizó los Impact Analysis Report correspondientes:

Page 74: Libro Genexus

74

Observe cómo informa sobre los cambios habidos en Attraction. Y para AttractionCategory informa que la tabla es nueva, deberá ser creada. Además presenta una advertencia15:

15 ¿Y si Mike hubiese testado anteriormente la transacción Attraction y hubiese insertado registros antes de esta reorganización? Supóngase que hubiera ingresado una atracción con Id de categoría 1 y descripción “Nightlife”… ¿qué pasaría entonces en la reorganización, cuando se cree la tabla ATTRACTIONCATEGORY y AttractionCategoryId pase a ser clave foránea en ATTRACTION y AttractionCategoryDescription inferido? ¿Qué pasará con el registro pre-existente en ATTRACTION? Para que la base de datos quede en estado consistente, tendrá que crearse un registro en ATTRACTIONCATEGORY, con Id 1… y con descripción “Nightlife”. Esto lo hará el propio programa de reorganización. ¿Qué pasará si existían dos o más atracciones con el mismo Id de categoría y distinta descripción? Es lo que informa la advertencia. Investíguelo.

Page 75: Libro Genexus

75

Tras el [Reorganize] confirmado por Diego, obtuvo la nueva base de datos y los nuevos programas generados. Cerró el Browser (pues la tarea de testar todo era de Mike) y finalmente documentó la nueva transacción y los cambios de Autonumber.

Mike leyó lo escrito por Diego y se dispuso a prototipar todas las transacciones. <F5> nuevamente…

Así, cuando Mike probó la transacción Attraction, al momento de ingresar la categoría de la atracción “Tour Eiffel”, no recordaba el código que correspondía a “Buildings/Structures” que debía asociarle. Entonces presionó en la imagen que sucede al campo para ingresar el Id de la categoría, , y se le abrió la lista de selección de categorías creada automáticamente por GeneXus, como aparece en la siguiente imagen:

Para que el usuario final no tenga que recordar códigos para las claves foráneas, GeneXus crea objetos automáticamente que se conocen como Prompts y que se invocan mediante la imagen que aparece en ejecución al lado de todo campo clave foránea. Se trata de listas de selección que muestran toda la información de la tabla referida, para que el usuario seleccione la deseada.

Page 76: Libro Genexus

76

Con esa lista abierta, Mike elegirá la primera opción, “Buildings/Structures”, y el control volverá a la transacción Attraction, donde aparecerá en el campo AttractionCategoryId el valor 1, correspondiente; el foco estará allí, y el usuario podrá pasar a ingresar el siguiente campo (en nuestro caso, el Id de país, para lo que el procedimiento podría repetirse).

También probó que estuvieran funcionando bien los controles de integridad referencial, por lo que colocó en AttractionCategoryId un valor inexistente de categoría, y vio cómo en ejecución aparecía un mensaje de error indicándoselo16.

16 Segundos más tarde, probaría intentar eliminar en la transacción AttractionCategory una categoría con atracciones asociadas, esperando ver que fallara la eliminación, asegurándose así la integridad referencial. Amigo lector: pruebe y verá.

Page 77: Libro Genexus

77

Para que el usuario final no tenga que estar abriendo estas listas de selección cada vez que tenga que ingresar el valor de una clave foránea, GeneXus provee de la facilidad de “disfrazar” en ejecución un atributo por otro. Mike propuso esto último; Diego lo realizó cambiando dos propiedades del atributo AttractionCategoryId, y como resultado, ahora para ingresar la categoría “Buildings/Structures” Mike sólo debe recordar con qué letras empieza, digitarlas y tendrá autocomplete:

El atributo en el que el usuario final estará ingresando la descripción, es en

realidad la clave foránea AttractionCategoryId, que está “disfrazada” de AttractionCategoryDescription, de manera que el usuario final ingresa descripciones, pero lo que en realidad queda almacenado es ni más ni menos que el Id correspondiente, que se fue a buscar a la tabla, en forma transparente.

Page 78: Libro Genexus

78

Ahora quedaba mejorar un poco la usabilidad y la apariencia de lo que iba del proyecto, y dejaron por escrito que habría que comenzar a hacer uso de algunas de las otras herramientas de GeneXus, como los GXpatterns. Y tal vez ya podrían reunirse todos para discutir si poner en producción alguna parte de la aplicación en el sector Tourism de la agencia, de tal forma que pudieran ir probando, cargando países y ciudades y enviando de retorno las sugerencias que encontraran.

Por las características de GeneXus, todo se puede probar en cualquier momento y, en particular, se puede prototipar cada objeto en el momento en que se considere oportuno hacerlo. Esta posibilidad es siempre muy importante y es consecuencia directa de la teoría de GeneXus y, en particular, de su propagación automática de los cambios.

“La prototipación es un gran recurso, y utilizándola adecuadamente, se evita una exagerada dependencia de la prueba final o, aún, de la puesta en marcha (el “Día D”, donde todo ocurre y donde la ley de Murphy se aplica especialmente)”.

Breogán Gonda & Nicolás Jodal

Page 79: Libro Genexus

79

Capítulo 3 Hecho a máquina, terminado a mano

Planes Inmediatos

El proyecto de Travel Agency & Co. ya ha avanzado lo suficiente como para que varias de sus partes sean puestas en preproducción. Es momento de pruebas por parte de los usuarios y a la vez de seguir incrementando el sistema. El problema: pobreza de la aplicación

Julia asiste con los usuarios finales para tener un feedback temprano y corregir posibles desvíos. Cuando éstos comienzan a probar el ingreso de datos mediante las transacciones, manifiestan estar satisfechos respecto a los controles y la forma de ingresar los datos individuales, pero quieren consultas con vistas más completas y con algún aumento de funcionalidad. Por ejemplo, quieren poder trabajar con los países de una forma más vistosa y amigable. Quieren poder visualizar en un grid todos los países existentes, seleccionar uno de ellos para poder modificarlo o eliminarlo, o incluso ver la información completa de ese país, incluyendo sus ciudades y toda otra información que pueda tener relacionada.

La solución: aplicación vistosa en sólo tres clics

Sin titubeos Julia se dijo “Patterns, allá vamos…” Con la transacción Country en edición, tres clics y un <F5>, preguntó a los usuarios finales “¿Es esto lo que desean?”.

Los usuarios finales ven en el Developer Menu un nuevo link, Work With Country, que al ser cliqueado abre un nuevo objeto en el browser, WWCountry, que no sólo muestra en un grid los países existentes en el sistema, ordenados

Page 80: Libro Genexus

80

por su atributo más significativo, el nombre17, sino que además permite filtrar en ese grid también por nombre (a medida que el usuario final va digitando en la variable de filtro, automáticamente se va filtrando en el grid), así como pagina automáticamente el grid18:

17 Ver página 32. Cuando se ingresan los atributos en la estructura de una transacción, el primero de tipo Character o VarChar ingresado, se marca como el descriptor. Se puede cambiar de descriptor posicionándose en la estructura sobre el nuevo atributo descriptor, y –vía botón derecho- eligiéndolo mediante “Toogle description attribute” en el menú contextual desplegado. Aunque como veremos apenas más adelante, Julia tendrá otra alternativa para hacerlo… 18 El valor por defecto es de 10 registros por página (es decir, carga 10 líneas del grid). Lo hemos modificado a 3 en un lugar centralizado del Pattern Work With del que luego hablaremos.

Para desplazamiento entre las páginas del grid

Filtro sobre el grid

Page 81: Libro Genexus

81

Y no sólo eso, también permite realizar acciones tales como:

• insertar un nuevo país, cliqueando en la imagen que aparece arriba y a la derecha del grid, , lo que llamará a la transacción Country en modo Insert19 (para insertar un país y sus ciudades) y retornar.

• modificar información de uno de los países, cliqueando en la imagen

que aparece en la línea del grid correspondiente a ese país (lo que también llamará a la transacción Country pero en modo Update, para actualizar ese país y retornar).

• eliminar un país del sistema (cliqueando ahora la imagen ) lo que

también llamará a la transacción, en modo Delete y luego retornará. • visualizar toda la información de un país determinado: observar el link

que aparece sobre el nombre del país; cliqueando aquí se abrirá un nuevo objeto (creado automáticamente), ViewCountry, que mostrará toda la información relacionada con el país (en tabs):

19 El objeto Transacción tiene una variable predefinida, &Mode, que identifica el modo en que se encuentra en un momento dado (modo Insert, modo Update, modo Delete o modo Display) de acuerdo a la operación que se vaya a realizar. Si se le envía por parámetro el modo y se recibe en esta variable, entonces la transacción sabrá qué operación se quiere realizar.

Page 82: Libro Genexus

82

Es decir, se creó un verdadero “Trabajar con Países” (Work With Countries). Lo mismo podrían pedir los usuarios para trabajar con las otras entidades: atracciones y categorías de las atracciones. ¿Patrones?

En líneas generales usted ya sabe de qué se habla cuando se dice patrón: conocimiento común que puede ser aplicado a situaciones distintas. Un pattern en GeneXus es muy potente; evita que usted deba crear “manualmente” objetos de gran versatilidad funcional; permite el reuso del conocimiento; y es un utilitario abierto: llegado el caso, usted mismo puede crear sus propios patrones. Introducen un alto nivel de productividad y generan aplicaciones de calidad, con interfaces más uniformes, y una lógica consistente. Es un generador de conocimiento a partir de conocimiento.

Page 83: Libro Genexus

83

Todo lo que Julia hizo fue aplicar el patrón Work With (uno de los existentes) a la transacción Country, obteniendo un trozo de aplicación que implementa todo lo necesario para poder “Trabajar con…” la información asociada, en nuestro caso: “países”.

Podemos pensar al patrón Work With (así como muchos otros) como una máquina, cuya entrada es una transacción (y la KB) y cuya salida es un conjunto de nuevos objetos GeneXus (algunos de los cuales mostramos antes en ejecución), más cambios en algunas propiedades de la KB, más la propia transacción modificada20…

Personalización: siempre hay un “pero…”

Los usuarios respondieron a la pregunta de Julia: “Sí, es esto lo que deseamos, pero… 1. En el grid sólo queremos ver el nombre del país y la región del mundo (el

resto de la información no nos interesa). 2. Además quisiéramos poder ordenar los países mostrados no sólo por

nombre de país, sino también por región del mundo, así como también por el par {región del mundo-nombre de país}, y por último queremos poder…

20 En particular la transacción deberá declarar que recibe parámetros, dado que ahora se la quiere poder invocar desde el objeto Work With Countries (WWCountry).

Page 84: Libro Genexus

84

3. …filtrar el grid por región del mundo (además de por nombre)”.

Todo lo que Julia había hecho para aplicar el pattern Work With fue abrir la transacción Country, pulsar sobre el selector Work With que se halla al pie (1er. clic), marcar el check box “Apply this pattern on save” (2do. clic) como podrá observar en la imagen de abajo y grabar (3er. clic). Luego <F5> (tras lo cual los usuarios pudieron ver la aplicación funcionando). Observe los nuevos objetos creados y desplegados en el listado de navegación:

Sin embargo, en la siguiente imagen usted apreciará que el selector Work With no sólo permite marcar el check box, sino que edita una estructura arbórea que Julia ni miró al realizar los pasos anteriores. Aquí es donde tendrá que trabajar ahora para realizar las modificaciones pedidas.

Page 85: Libro Genexus

85

Esta imagen muestra una “instancia” del pattern Work With, la instancia para Country, que permite personalizarlo21. Se puede observar una estructura jerárquica conteniendo nodos y un conjunto de propiedades cuyos valores determinarán la apariencia y el comportamiento de lo generado por el pattern para esta instancia.

El pattern Work With generará, entre otras cosas, dos nuevos objetos GeneXus, que pudimos ver en ejecución, y que hemos incluido para usted sobre la imagen de la instancia, de un tipo del que aún no habíamos hablado: Web

21 No hemos definido “instancia”. Puede pensarse como la declaración de cómo se aplicará el patrón al caso particular de este objeto.

Configura el web panel WWCountry:

Configura el web panel ViewCountry

Page 86: Libro Genexus

86

Panel. Permite, grosso modo, hacer consultas interactivas a la base de datos, de modo amigable22.

En la imagen hemos indicado qué nodos de la instancia del pattern permiten configurar las características de qué web panel. Los usuarios le han pedido a Julia modificaciones sobre el primer web panel. Nada han dicho por el momento del segundo (pero tal vez lo hagan…).

Observe el lector lo primero que aparece bajo el nodo “Level (Country)”. Recuerde que CountryName era el atributo descriptor de la transacción (el de mayor carga semántica). ¿Qué repercusión tiene el atributo descriptor en lo generado por el pattern? Mucha. Por ejemplo, define cuál será el atributo del grid del WWCountry que contendrá un link hacia el web panel ViewCountry creado automáticamente para ver la información de ese país. Pero este es sólo un ejemplo, como veremos, se utiliza en varios lugares. No obstante, el atributo descriptor, que es heredado de la transacción, si bien puede ser cambiado por otro en el árbol, no implicará que sea también cambiado en la estructura; es sólo el descriptor para el pattern.

Web panel WWCountry: nodo Selection (Work With Countries)

Obsérvese que el nodo Selection contiene como subnodos: Attributes, Orders y Filter (además de acciones: Ins, Upd, Del, Dis).

El subnodo Attributes muestra por defecto todos los atributos del primer nivel de la transacción Country, y son los listados (¿casualmente?) en el grid del web panel WWCountry (“Work With Country”)23 generado…

Los usuarios pidieron a Julia solamente mostrar CountryName y CountryWorldRegion, por lo que lo único que ésta debe hacer es eliminar de

22 En un caso tenemos un web panel que nos permite entre otras cosas listar los países, filtrando por nombre, y en el otro tenemos un web panel que nos muestra toda la información de un país determinado y sus ciudades (éstas en un grid dentro de un tab). 23 Este web panel, WWCountry tiene su descripción en singular “Work With Country”, sin embargo, en su pantalla se presenta como “Work With Countries”. Por lo que para referirnos al mismo, utilizaremos indistintamente el singular y el plural, de acuerdo a lo más natural al contexto en el que lo estemos referenciando.

Page 87: Libro Genexus

87

ese nodo, con <Supr>, el resto de los atributos, con la excepción de CountryId. Este último no puede ser eliminado tan alegremente. Aunque no se le muestre al usuario final en el grid, deberá estar presente, invisible, como justificaremos en breve. Por lo tanto, lo que hará Julia en ese caso será posicionarse sobre el atributo en ese nodo, y, editando sus propiedades con <F4>, modificar la propiedad Visible pasándola a “False”.

Así quedará la instancia; vea la repercusión en ejecución.

¡Requerimiento 1 listo! El subnodo Orders indica el orden por el que en forma predeterminada se

desean recuperar los datos que se cargarán en el grid. El orden elegido por defecto es (¿casualmente?) el de CountryName, el atributo descriptor. Los usuarios pidieron poder elegir entre ese orden y uno por CountryWorldRegion, así como uno compuesto por ambos.

¿Cómo puede el usuario decidir en ejecución por cuál criterio quiere ordenar la información a recuperar? Mediante un combo box que le muestre las posibilidades. Julia necesitará agregar los nuevos ordenamientos pedidos bajo el subnodo Orders, a los que les dará un nombre (World Region, Both) que

Page 88: Libro Genexus

88

serán las opciones que el usuario final verá en ejecución en el combo insertado automáticamente por el pattern.

Así quedará la instancia y luego el web panel generado (en ejecución):

Observe un detalle: puede también ordenar en ejecución lo que haya cargado en el grid en un momento dado, por alguna de sus columnas, simplemente cliqueando sobre la misma. ¡Pero cuidado! Si está trabajando con paginado, aquí estará ordenando sólo la página actualmente cargada.

¡Requerimiento 2 listo! Sólo queda el 3. Los usuarios quieren también poder filtrar los países a cargar en el grid por

región del mundo. Evidentemente: subnodo Filter de la instancia. Obsérvese lo que aparece en forma predefinida bajo este nodo (vea página 77). Otra vez, ¿casualmente CountryName? Tómese un tiempo, estimado lector, para deducir qué significan los dos subnodos Attributes y Conditions, mirando el web panel

Page 89: Libro Genexus

89

WWCountry que GeneXus implementó y la instancia en la página 77. ¿Y si ahora le mostramos lo que hizo Julia en la instancia y la repercusión que esto tuvo en el web panel?

Al agregar el atributo CountryWorldRegion bajo el subnodo Attributes, se creará automáticamente una variable del mismo nombre, &CountryWorldRegion24, que se colocará sobre el grid del web panel y que permitirá que el usuario final seleccione una región del mundo para que en el grid se le muestren sólo los países de esa región (¿por qué aparece como combo box25 ?). ¿Cómo sabe que debe hacer ese filtro? Pues por lo que Julia escribió bajo el subnodo Conditions. Observe algunos detalles: hay dos filtros definidos (por nombre de país y región). ¿Cuál aplica? Ambos, a menos que alguno no deba ser aplicado (observe las cláusulas “when” de cada filtro. Por ejemplo, cuando &CountryWorldRegion tenga el valor del dominio correspondiente a None. En tal caso, no aplicará ese filtro).

¡Requerimiento 3 listo! Obsérvese un hecho interesante y nada casual: cuando Julia mostró en

ejecución a los usuarios cómo modificar un país o eliminarlo, se abrió la

24 Ya habrá deducido que las variables aparecen identificadas en todo el código GeneXus por un ‘&’ que es colocado como prefijo del nombre de la variable. 25 Recuerde que el atributo CountryWorldRegion está basado en el dominio WorldRegion de tipo enumerado. De ese modo cuando se presenta como control en un form, será un combo box. Aquí se trata de una variable que se basa en este atributo y observe cómo hereda el mismo comportamiento, sin tener que decirle nada.

Page 90: Libro Genexus

90

transacción Country, editándose el país seleccionado del grid. Para ello el pattern WorkWith tuvo que modificar las reglas de la transacción, agregando algunas que se suman a las que ya habíamos declarado. ¿Para qué? Para poder instanciar, editar el país, recibirlo por parámetro. Echemos un vistazo:

Cuando desde el “Work With Countries” se elija un país del grid para modificarlo, se invocará a esta transacción enviándole el modo ‘UPD’ que es recibido en la variable &Mode y se enviará además el CountryId correspondiente (es por este motivo que tuvimos que dejarlo en el grid, oculto). Cuando se quiere insertar, se envía ‘INS’ y el valor vacío como segundo parámetro, y así…

La forma que tiene todo objeto GeneXus de declarar los parámetros que recibe y devuelve, es mediante la regla Parm.

Page 91: Libro Genexus

91

Web panel ViewCountry: Nodo View (Country Information)

Cuando hacemos clic en ejecución sobre el nombre del país, se abre el web panel ViewCountry que nos muestra la información del país correspondiente (ver página 74). Observemos qué datos nos muestra: el nombre del país recibido por parámetro (¿otra vez casualmente CountryName?) y luego en un tab General todos los atributos del primer nivel de la transacción Country para ese país, y dos botones para poder llamar a la transacción pasándole ese país por parámetro (para modificar el país y eliminarlo respectivamente) y en un tab City, nos muestra en un grid todas las ciudades del país.

Observe detenidamente el nodo “View (Country Information)” de la instancia del Work With de Country. Si el usuario pidiera que además del nombre del país, apareciera el nombre completo en la parte fija, luego en el tab General sólo se mostraran los atributos CountryWorldRegion, CountryDetails y CountryFlag, y si además quisiera que en el tab City, en el grid de las ciudades sólo se mostrara el nombre de las mismas, ¿qué cambios debería hacer sobre estos nodos para lograrlo? ¿Y si no se deseara el tab City en absoluto?

En la instancia aparecerá un “Tab” por cada tabla subordinada (muchos a 1)26 … pero Julia podría agregar otros Tabs… con otras cosas… No nos apuremos…

26 COUNTRY sólo tiene a COUNTRYCITY en esta relación (muchas ciudades por cada país) y por ello sólo aparece un Tab. Obsérvese que el nombre que eligió GeneXus para nombrarlo fue la descripción del nivel 2 de la transacción Country (la descripción de las líneas, que era City).

Page 92: Libro Genexus

92

Concluyendo: Cuando Julia marca “Apply this pattern on save” en el selector Work With de Country, al grabar, se generarán objetos GeneXus como los que estuvimos viendo en ejecución, pudiendo acceder a ellos en el Folder View, bajo la transacción. Asimismo, cada vez que modifique la instancia, al grabar, se regenerarán los objetos que corresponda, en forma automática.

¿Más? Los usuarios, que por momentos parecen tener como especial misión en el mundo encontrar las flaquezas de los desarrolladores y volver sus vidas miserables, pidieron a Julia que luego de modificar un país, no se volviera al llamador, WWCountry, sino que les mostrara toda la información del país recién modificado (es decir, ir al “View Country”).

Julia, inmutable, se posicionó en el nodo raíz de la instancia y cambió el valor de la propiedad AfterUpdate para “Go to View”…

Obsérvese que inicialmente toma un valor “<default>”. En algún lado tiene que decir cuál es este (y los demás) “<default>”. Este valor especifica cuál es el comportamiento predeterminado para todas las instancias (aquí tenemos la de Country, pero también podríamos querer un Work With Attractions y un Work

Page 93: Libro Genexus

93

With AttractionCategories). Si en la mayoría o todos los Work With de la aplicación queremos que el comportamiento por defecto sea que luego de modificar se vaya al View, entonces en lugar de cambiar a mano una por una esta propiedad en la instancia de cada transacción, podemos cambiar en un lugar centralizado el “<default>”.

¿Cuál es este lugar centralizado? Se encuentra bajo el grupo Preferences de la ventana Knowledge Base

Navigator: nodo Patterns. Observe en la imagen que sigue que al momento sólo se encuentran los patterns Work With y Category. Pero ya habrá otros de usted, de Artech y de terceros…

(“Extensibilidad”… “integrabilidad”… piezas claves en la ideología X)

Obsérvese que aquí aparecen todos los comportamientos predeterminados que heredarán las instancias particulares del Work With para cada transacción. Por ejemplo, si queremos cambiar para todos los Work With la cantidad de

Page 94: Libro Genexus

94

registros por página de los grids (cosa que ya habíamos hecho sin mostrarle, pasándola a 3), tenemos el nodo Grid27. Invitamos al lector interesado a investigar sobre las configuraciones generales que puede realizar desde aquí. Por ejemplo, puede cambiar el aspecto general de las páginas, los nombres de las mismas, las imágenes para las acciones de Insert, Update, Delete y mucho más.

Reflexione el lector sobre lo que significa “trabajar con”. ¿Qué otras cosas podría pedir el infame usuario? (perdón lector, es que también somos humanos). Pregúntele a Julia y ella le podrá contar que en esa misma reunión, se le ocurrió a esa especie particular de homo sapiens que quería: A. Exportar la información de los países con los que estaba trabajando, a una

planilla Excel. ¿Puede usted creer? Además dijeron que no querían: B. Que se pudiera eliminar países desde el Work With Countries. Y por si esto

no fuera poco, que querían: C. Agregar otra acción más sobre los países (además de las de Insert y

Update ), cuyo efecto fuera mostrar en otra pantalla cada país con sus ciudades, ¡todos juntos!, y en un formato más agradable a la vista. (Un poco ambiguo para nuestro gusto… y el de Julia)

Julia disfrutó con sorna: ante cada requerimiento que el usuario pedía con

ojos maliciosos, hacía un par de clics y listo. Salvo para el último, donde el usuario logró su aparente objetivo: complicarle la vida. Pero ni tanto. No olvidemos que Julia no es técnica, luego en la oficina pediría ayuda a Diego.

27 Si observa en su KB la propiedad Page del nodo Grid, encontrará que tiene el valor “Page.Rows”. ¿Qué es Page? Pues un dominio enumerado creado automáticamente, que contiene un solo valor, Rows, cuyo valor por defecto es 10 y que nosotros hemos cambiado a 3.

Page 95: Libro Genexus

95

Acciones en el Work With Countries

Aquí se muestran las acciones que el pattern Work With tiene disponibles para implementar sobre los datos del WWCountry con los que trabaja. En particular obsérvese que existe la opción Export. ¡Otra vez los valores “default”! Otra vez, sí, estos estarán configurados en el objeto WorkWith que vimos antes que centraliza todo esto. En nuestro caso, los valores “default” para Delete y para Export, eran “true” y “false”, respectivamente. Para deshabilitar la acción de eliminación de un país y habilitar la acción de exportación, Julia sólo tuvo que modificar los combos que mostramos, pasándolos a “false” y “true” respectivamente. ¡Requerimientos A y B listos!

Ahora queda el último requerimiento, el C: agregar una acción que no es una de las predefinidas. Pero el objeto al que hay que invocar como resultado de la acción, no es creado por el pattern Work With; se quiere hacer “a medida”, para estos datos en particular. Se aparta de lo que es común a

Exportación

Page 96: Libro Genexus

96

cualquier “trabajar con”. Aquí es donde deberá intervenir Diego, implementando en GeneXus ese objeto. Julia escribe en el selector Documentation del WorkWithCountry que esto queda pendiente (¿qué selector Documentation? Observe el tab que se abre cuando usted presiona doble clic sobre el nodo WorkWithCountry del Folder View, bajo la transacción Country -vea página 84 y la siguiente página- … todos los caminos conducen…). Lo hizo bajo un título “To-Do Diego” para que sea implementado por él cuando ponga manos a la obra28. Los insaciables de siempre: ¡quiero más!

Tras volver de la reunión con los usuarios, Julia se encuentra con un e-mail

de… ¡los usuarios!... solicitándole que en la lista de países del “Trabajar con” se pudiera ver aunque más no fuere un trozo de los detalles del país, CountryDetails, cuestión de poder comentar a sus clientes algo muy breve de cada uno. También querían poder ver para cada país su cantidad de atracciones.

Necesitaba agregar dos nuevas columnas al grid del Work With Countries. Una para la descripción corta, y otra para la cantidad de atracciones. Pero no sabía cómo lograrlo, ya que esa información no estaba almacenada en atributos de la base de datos, sino que se tenía que obtener mediante algún procesamiento. Ya tenía en mente llamar a Diego para que le enseñara cómo implementar el requerimiento que había quedado pendiente, el C, por lo que tomó el teléfono inmediatamente.

Diego creó el dominio ShortDetails de tipo Character(70). Luego abrió el Work With Pattern Instance de Country y mediante el comando “Add” agregó una variable al grid.

28 Como verá un poco más adelante, a Diego no le resultará nada complicado ubicar esta nota de Julia entre toda la documentación que se supone “desperdigada” entre tantos objetos de la KB, y será gracias a las Categorías Dinámicas, de las que él mismo hablará en el capítulo 6.

Page 97: Libro Genexus

97

Obsérvese que en el diálogo de propiedades se le debe necesariamente dar un nombre y asignar un dominio a la variable que se está insertando (es lo que indica el ícono ). Diego le asigna como nombre ShortDetails y como dominio el recién creado de igual nombre. Solamente resta decirle el valor que deberá tomar para cada línea del grid.

- Como bien intuyes, Julia, cuando ponías los atributos bajo este nodo no era preciso decir más: GeneXus recorrería la tabla COUNTRY, y para cada registro que cumpliera con las condiciones de los filtros especificados (en este caso veo que son por CountryName y CountryWorldRegion), cargaría una línea nueva en el grid del “Trabajar con” con los valores de esos atributos en cada columna. Si los usuarios quisieran la descripción completa del país, bastaría con incluir el atributo CountryDetails, pero lo que quieren es acortar la descripción a 70 caracteres. Por lo que, lo único que tendremos que hacer es especificar en la propiedad LoadCode de la variable que ingresamos, el valor que tomará para cada línea: será la operación de quedarnos con los primeros 70 caracteres del string29, es decir: &ShortDetails = Substr(CountryDetails,1,70). Sí, CountryDetails es el atributo. Así de sencillo. Ya puedes hacerlo sola.

- Entiendo, y entonces para el otro requerimiento, el de mostrar el número de atracciones que tiene cada país…

29 GeneXus cuenta con múltiples funciones para trabajar con los distintos tipos de datos. En particular, tiene la función substr, que permite recuperar un substring de un string dado.

Page 98: Libro Genexus

98

- Bueno, en principio ahí el procesamiento que debes hacer para cargar la variable que contendrá esa información es un poco más complejo. Sólo en principio. Fíjate que para el país que se esté cargando en una línea del grid en cada oportunidad, se deberán recorrer todas sus atracciones para contarlas. Tienes tres caminos: uno es programar ese código que te permite recorrer información30, otra vez en el LoadCode; otro, es agregar un atributo a la transacción Country, que contenga esa información, suponte un CountryTotalAttractions. De esta manera sólo tienes que agregar este atributo al nodo Attributes de la instancia, y ¡listo!

- Pero, espera, no entiendo… En ese caso, cuando el usuario ingrese la información de un país mediante la transacción, ¿va a digitar el número de atracciones que contendrá? ¿Y si después resulta que no coincide con las atracciones reales que ingresó? Eso no les va a gustar a los usuarios.

- ¡Julia, en cualquier momento me dejas en la calle! Exactamente, no tiene sentido que el usuario tenga que digitar una cantidad que es resultado de un cálculo entre valores que están en la base de datos. Este atributo, CountryTotalAttractions será de un tipo especial, que en GeneXus se denomina fórmula. Verás, ingresaré ese atributo nuevo en la estructura de la transacción Country, y le diré a GeneXus que tengo más información, que sé cómo debe calcularlo, y que por tanto no lo almacene en la base de datos, sino que siempre que se le pida su valor, la calcule como yo le digo:

30 Será el comando For Each, del que hablaremos más adelante, en el Capítulo 5.

Page 99: Libro Genexus

99

- ¿Count(AttractionName)? Ajá, con eso le dices a GeneXus que quieres que para ese país cuente la cantidad de valores de AttractionName asociados31, ¿no? Ahora, ¿cómo sabe que tiene que contar las atracciones de “ese” país y no de “todos” los países?

- Bueno, recuerda que GeneXus conoce las relaciones entre los datos y sabe que una atracción pertenece a un país y ciudad determinado. Y cada país-ciudad pertenece en particular a un país. Con eso basta.

- ¿Y qué ventaja tiene esta solución para nosotros, frente a la otra, la de agregar una variable en la instancia del pattern y decirle cómo hacer el cálculo con ese comando for each que dijiste -cosa que, por otra parte, no sé hacer-?

- Pues que aquí has implementado una solución general. Si en otros lados, en otros objetos, cuando recuperas un país necesitas saber su cantidad de atracciones, sólo con nombrar el atributo fórmula ya se dispara el cálculo, y no tienes que programarlo a mano. Ahora sólo tienes que agregar al nodo Attributes de la instancia este atributo fórmula. ¡Ya están listos los dos requerimientos del e-mail!

31 Para usted que sí es informático: para cada país contar la cantidad de registros asociados de la tabla donde se encuentra AttractionName, que en este momento es ATTRACTION.

Page 100: Libro Genexus

100

- Pero espera… habías dicho que teníamos tres caminos posibles para implementar esto último y sólo me has explicado dos.

- Qué suerte, Julia. Estaba esperando que te dieras cuenta. De hecho, el tercer camino es el que yo hubiera utilizado desde un principio en este caso. Si no lo hice fue para aprovechar a explicarte esto de las fórmulas globales. Lo que hicimos sería lo más recomendable si creyéramos que muchas veces, y en lugares distintos, vamos a tener que realizar esa misma cuenta de las atracciones. Pero si por el contrario, será en pocos lugares, tal vez lo mejor hubiese sido directamente definir la variable como antes en el nodo Attributes de la instancia del Work With, por ejemplo &CountryTotalAttractions, y en la propiedad LoadCode de la variable, especificar &CountryTotalAttractions = count( AttractionName ).

- ¿Pero no es la misma fórmula que antes? - Sí y no. Aquí no has definido un atributo fórmula, sino que estás

utilizando la fórmula localmente, como si fuera una función. Por el contexto en el que está especificada -en nuestro caso estará en un grid que recorre la tabla de países- sabrá que deberá contar solamente las atracciones del país que se está cargando en cada caso.

Así mostró a Julia, tras el <F5> cómo luce ahora el “trabajar con países”:

Existen diversos tipos de fórmulas en GeneXus (para sumar, contar, buscar, maximizar, minimizar, sacar promedios). Es importante destacar que se pueden definir tanto en forma global, como atributos, así como localmente donde se necesite. Cuando se definen globales serán atributos virtuales: no estarán almacenados en la base de datos.

Page 101: Libro Genexus

101

Quod Pattern non dat, GeneXus praestat32

En lo que Julia demoró en ir a servirse un café, saludar a un compañero y volver con la taza humeante, Diego ya había implementado el último requerimiento (el C pendiente). Como no estaban seguros si implementaba lo que deseaban los clientes, capturaron la siguiente pantalla y se la enviaron por e-mail:

32 N. de A. Frase latina, “Quod Natura non dat, Salamantica non praestat” para referir a que si la naturaleza no da inteligencia, ni la prestigiosa Universidad de Salamanca puede darla.

Page 102: Libro Genexus

102

- ¿Cómo lo hiciste tan rápido, Diego? ¿Me enseñas? No puede ser complicado.

- No lo es; lo que hice fue crear un nuevo objeto en GeneXus, de tipo Web Panel. Es el que estás viendo en la pantalla en ejecución. Fíjate que lo que deseas es mostrar información de países y de ciudades (las suyas). Olvídate por un momento de los países, y supón que sólo quieres listar información de las ciudades, en un grid. Lo único que debes hacer es insertar un control de este tipo en el form del web panel y decir qué atributos compondrán sus columnas. Sólo con hacer esto, cuando presiones <F5> verás en ejecución listadas todas las ciudades existentes. GeneXus observa los atributos presentes y de ellos infiere la tabla que deseas navegar (le llama tabla base). Fíjate –creó un web

Page 103: Libro Genexus

103

panel “Cities” en un segundo, insertó el grid mediante la toolbox que ofrece todos los controles que pueden insertarse en un form, eligió el atributo CityName, y quedó el form del objeto como puede ver a continuación- en nuestro caso: los atributos que colocamos en el grid (CityName) son de la tabla COUNTRYCITY y esa será la que navegará GeneXus.

- La grilla que hemos insertado se conoce como Grid, a secas, grid estándar, ya que permite mostrar la información de un registro de una tabla -y de su extendida-, con un elemento de información por columna -observa que nuestro grid tiene una sola columna, corresponde al atributo CityName, pero si tuviéramos otro, lo colocaría en otra columna-. Ahora olvídate por un instante de las ciudades, y piensa que algo muy parecido quieres hacer con los países, sólo que quieres mostrar la información de cada país no en columnas rígidas, sino de forma más flexible, como puedes ver en la pantalla que le enviamos a los clientes. Observa el web panel que hice mientras ibas por tu taza de café:

Page 104: Libro Genexus

104

- Observa que ahora aparece el grid de ciudades, junto con los atributos CountryFlag y CountryName dentro de un rectángulo. ¿Qué es? Otro tipo de grid, uno llamado Free Style, pues como ves, la información del país que quieres listar se muestra no por columna, sino toda junta, con el diseño que tú decidas. Pero también es un grid. Mostrará por tanto, tantos países como existan en la tabla de países. Observa que en sus propiedades aparece una Columns, a la que modifiqué el valor de “1” a “3”, y es por ello que puedes ver en la imagen que le enviamos a los usuarios tres países por fila. Rows “0” significa que no realice paginado, sino que muestre todos los países existentes. Luego observa que dentro del Free Style, podemos colocar cualquier control, incluyendo otro grid. Y al hacerlo, GeneXus encuentra las

Page 105: Libro Genexus

105

relaciones entre las tablas, y es por esta razón que está listando por cada país, sus ciudades en el grid, sin tener que indicarle nada, lo hace solo. Si no te convence, míralo tú misma en el listado de navegación que aparece luego de la especificación, tras el <F5>. A ver si lo entiendes…

- Lo que sí entiendo es por qué lo implementaste tan rápido… no es que “Natura te dio”, es que “GeneXus te prestó”. ¡No tuviste que hacer casi nada!, Diego… Cambia esa cara, estaba bromeando. ¿No vendrá tu amiga hoy a verte?

- … además podríamos hacer otro web panel, invocado vía un Tab del View de países, que dado el país muestre las imágenes de las atracciones de

El país que se está cargando en el

id

Page 106: Libro Genexus

106

ese país, como una galería de imágenes que se deslizan. Quisiera hacer algo que impresione… – quedó pensativo.

- ¿A los usuarios?… Bueno, Diego, veo que hoy no estás para bromas. Antes de que sigas, quedó pendiente poder llamar desde el Work With Countries a este web panel que acabas de implementar.

- Es verdad, pero eso sabes hacerlo. Simplemente debes agregar una nueva acción a las ya predeterminadas (insert, update, etc.) en la instancia del Work With de Country: la asociaremos a un botón fuera del grid y le indicaremos que en esa acción queremos invocar al web panel “CitiesPerCountry” que acabo de crear.

Sobre el nodo Selection (Work With Countries), con botón derecho, seleccionar “Add actions” y luego posicionarse sobre el nuevo nodo Action y otra vez repetir la operación. Vea lo que hicieron nuestros amigos, y la repercusión en ejecución:

Page 107: Libro Genexus

107

¡Requerimiento C listo! Nuevo Tab en el View Country

Diego quería impresionar a alguien, no sabemos a quién (quizás a usted, estimado lector) con un nuevo requerimiento: en el ViewCountry, agregar un nuevo control Tab que muestre en una galería de imágenes las atracciones de ese país. Es decir, algo que en ejecución se verá:

¿Botón dentro del grid?

¿Se quiere imagen para la acción en lugar de botón?

el web panel creado…

Page 108: Libro Genexus

108

En todo momento se mostrarán 3 imágenes, siendo la del centro la activa. Cuando se pulsa sobre la de la derecha, la del medio pasa a la izquierda, la de la derecha al centro (pasa a ser la activa) y aparece una nueva a la derecha.

Para lograr esto, Diego agrega un nodo Tab en la instancia del pattern Work With correspondiente al View, que se cargará con el contenido de otro objeto GeneXus que luego deberá programar. Podría ser un web panel, pero en realidad tiene que ser un objeto que se pueda ejecutar dentro de otro (en nuestro caso del ViewCountry). Se tratará de un Web Component. A los demás efectos será igual que un web panel. Le llamará CountryAttractions, y deberá indicar en el nuevo Tab que se cargará con el mismo:

Page 109: Libro Genexus

109

¿Cómo se implementa? Otra cara de la extensibilidad: User controls

Además de los controles estándares que usted suele insertar en sus paneles (attribute, grid, text block, etc.), GeneXus X le permite crear sus propios controles personalizados: user controls. Una vez que un control de usuario es instalado, puede ser utilizado como cualquier control estándar. GeneXus X viene con algunos controles de usuario ya instalados (vea la toolbox en el IDE; ellos se encuentran agrupados bajo el rótulo User Constrols): uno de ellos es el ImageGallery. Observemos en la foto composición que sigue que Diego insertó un control de ese tipo en el form del web component, y luego lo único que tuvo que hacer fue configurarle sus propiedades. Al insertar este control en el form, se crea automáticamente un tipo de datos estructurado, ImagesData, más conocido como SDT por su sigla en inglés, que en forma flexible permite construir listas de largo variable de records. En este caso, una lista de elementos en el que cada uno tendrá la información de la atracción a mostrar; esto es: su id, el lugar donde se encuentra la imagen (en general se almacena la

Se le deberá pasar el CountryId como parámetro al Web Component que implementa la galería de imágenes.

Page 110: Libro Genexus

110

imagen en tamaño grande, y la misma imagen en tamaño pequeño) y la descripción de la misma. Junto con el SDT se crea automáticamente la variable &imagesData basada en el mismo, justamente para cargar las imágenes a mostrar.

Por tanto, lo único que Diego debió hacer fue programar la carga de esa variable. ¿Cómo lo hizo? En el evento Start del Web Component escribió la invocación a un objeto que creó inmediatamente, que justamente tiene por objeto cargar estructuras jerárquicas de datos, como SDTs. Ese objeto es de un tipo que veremos más adelante (el Data Provider). No importa que ahora no entienda este código. Sólo queríamos mostrarle lo fácil que es lograr este requerimiento.

Page 111: Libro Genexus

111

¿Look & Feel consistente, sin transpirar?

“Tener un look&feel consistente es hoy en día un deber de toda aplicación Web. Crear y mantener cada página de una aplicación Web asegurando la consistencia con el resto del sitio toma gran tiempo de programación” leyó Julia en voz alta de una revista técnica que estaba sobre la mesa del escritorio de Diego.

Page 112: Libro Genexus

112

- No con las Master Pages –acotó Diego-. Como te conté, un web

panel puede implementar una página web común y corriente, como la que hicimos, CitiesPerCountry -con los grids Free Style y estándar-, es decir, una página web independiente que será invocada por otra -en nuestro caso por WWCountry-; o puede implementar una página web que será parte componente de otra, como la de la galería de imágenes que era un Web Component parte del ViewCountry; o por último, puede ser una página maestra, es decir, una página que contiene centralizado allí lo que será común a todas las páginas del sitio, como el encabezado y el pie de página que has visto por todos lados repetido; incluso suelen tener un menú vertical a la izquierda, de manera tal que sólo esté en un lugar, y que si se quiere modificar, se lo haga en un solo sitio.

- ¡Con razón! Cuando empezamos con el proyecto vi a Mike ejecutando las transacciones y no entendía cómo era que todas salían con el mismo encabezado y pie de página (que también veo ahora que tienen el Work With Countries, y el View Country) siendo que en el form de esos objetos esas partes no aparecían. Existe una master page que programa ese look común, ¿verdad?

- Efectivamente. Mira esto- dijo Diego a la vez que seleccionaba la transacción Country y accedía a sus propiedades-. Verás lo mismo en cada transacción y en los web panels creados por patterns: AppMasterPage es una master page creada automáticamente con la KB -observa el contenido de la carpeta GeneralWeb- que es la que contiene ese encabezado y pie de página que se repite por

Page 113: Libro Genexus

113

doquier. Si abrimos este web panel, mira sus propiedades. ¿Qué te dice esto?

Y aquí no sólo se programa el form común: también se le puede programar el comportamiento común. Es más que habitual que las páginas sean restringidas a usuarios autorizados, que deberán loguearse al sistema antes de comenzar a navegar por las distintas páginas del sitio. Antiguamente la validación de que el usuario estuviera autorizado debía repetirse en cada página web a la que se accediera. Sin embargo, con las master pages, alcanza con escribir el código de validación solamente una vez, aquí, y ¡listo! Nos aseguramos que todas las páginas asociadas realizarán esa validación, y su código no estará desperdigado por todos lados.

- ¡Qué bueno! Digo, para ustedes, que tienen que trabajar menos para que les quede todo consistente. Me voy a almorzar y luego a impactar el pattern al resto de las transacciones que tenemos… ¿Ya almorzaste?

Page 114: Libro Genexus

114

Page 115: Libro Genexus

115

Capítulo 4 Vuelo al mundo de los subtipos

El proyecto avanzaba según los tiempos estipulados. Cada uno en lo suyo.

Dada la filosofía incremental de GeneXus, no temían a los cambios ni a lo nuevo. El modelo amparaba todo lo necesario. Sólo lo necesario.

Restantes Transacciones al vuelo

Diego no había almorzado, no tenía apetito. Debía comenzar a crear el resto de las transacciones, pero decidió esperar un rato. Quería lucirse frente a Mary, a quien había ofrecido mostrar más de GeneXus. En lugar de llevar su notebook al bar, le había propuesto encontrarse, esa misma tarde, en su oficina en ACME.

- Mary, te ves bien. ¿Cómo estás? - Mejor. Te agradezco que me hubieras invitado a vernos de nuevo, aún

cuando estemos en empresas competidoras. Es un lindo gesto de tu parte. - Mmm… bueno, no es tan así – dijo pensativo, mientras en la pizarra a

sus espaldas escribía: “la Comunidad” –. No, por favor, no me malinterpretes, no quise decir… Me refería a esto de la rivalidad, de la competencia. Mira, entre los miles de desarrolladores GeneXus existe en forma “etérea” algo que se conoce como “la Comunidad”. Es muy estimulante. “Sinérgico”, escucharás decir. Tú me preguntarás si es un club; pues no desde el punto de vista de actividades sociales o deportivas, al menos por ahora –agregó sonriendo, intentando disimular el temblor en la voz –, pero sí respecto a la interacción. Es como un tejido del que todos formamos parte, compartiendo conocimiento y soluciones a través de GeneXus y de las demás herramientas que se han desarrollado alrededor. Existen muchas extensiones creadas por la Comunidad para satisfacer distintas necesidades. Hay muchos sitios Web de donde descargarlas, y a donde subirlas también. La Comunidad es muy activa. Existen decenas de foros donde se intercambian soluciones de todo

Page 116: Libro Genexus

116

tipo. Yo estoy en las listas de varios de ellos, y me considero un miembro activo. - Suena lindo. Pero, ¿funciona? O sea, ¿te dan una mano desinteresada

cuando la necesitas, o es puro cuento? - Es que es hoy por mí y mañana por ti. Todos necesitamos una mano

de vez en cuando. Si en vez de vernos como competidores nos sentimos cooperadores, trabajamos mejor, más animados, llegamos a mejores soluciones, para todos. Tiramos para un mismo lado: hacer crecer a la Comunidad, en beneficio de todos. Puede resultarte un poco ingenuo, pero la prueba está a la vista: funciona. Como evidencia de ello, el propio GeneXus está compuesto por extensiones de GeneXus. Es decir, la Comunidad puede seguir haciendo crecer a GeneXus con sus propias extensiones. De hecho, GeneXus puede considerarse en sí mismo una extensión.

Tras reparar en esa luz antigua, conocida, que adquiría su mirada, continuó más seguro:

- Mira – invitó Diego al tiempo que abría la ventana Extensions Manager a través de la opción Tools del menú de GeneXus –. Esta es la lista de extensiones que componen GeneXus X en este momento. La mayoría han sido desarrolladas por Artech y las otras por personas de la Comunidad que las publican para que diferentes miembros puedan hacer uso de ellas.

- Veo que en la Start Page aparece una sección que dice “Extensions”, con un abstract escrito por el autor, y un link para instalarla.

- Sí, y si te fijas, aparecen unas cuantas que no dicen By Artech. Son extensiones realizadas por terceros, miembros de la Comunidad.

En cuanto al proyecto en sí y al punto en que habían llegado, la realidad reclamaba otras transacciones. Tras una breve ojeada a lo ya hecho, Diego pasó a crear Airline. Su finalidad:

Page 117: Libro Genexus

117

almacenar el nombre de las compañías aéreas involucradas con Travel Agency & Co. Transacción Flight: vuelo al mundo de los Subtipos

En cuanto Diego miró sus papeles, agradeció por la complejidad asociada a la próxima transacción. A Mary solían gustarle los desafíos.

- Mary, vamos a crear la siguiente transacción, pero va a hacer falta un poco de concentración de tu parte –advirtió tratando de predisponerla – Mira este caso. La transacción contendrá información relativa a los vuelos; cosas tales como la descripción, la aerolínea, la hora de despegue, la cantidad máxima de pasajeros, fáciles de entender y de inferir; y por otro lado, tendremos la ciudad de donde se sale y la ciudad a donde se llega33, información que debemos conocer y almacenar porque está comprendida en la realidad. ¿Recuerdas que te comenté en el bar que dos atributos con igual concepto deben tener el mismo nombre?

- Sí, fue cuando me explicaste cómo crear la transacción Country. Recuerdo perfectamente que mencionaste que había una excepción…

- Llegó la hora de aclarar eso. Hay momentos en los que no hay más remedio que llamar distinto a dos atributos que son básicamente lo mismo. Mira, en la transacción Flight se nos presenta el problema que da lugar a la excepción que lo resuelve: en cada vuelo existen dos países y dos ciudades involucradas, que cumplen roles diferentes: un país y una ciudad son de partida y otro país y ciudad son de destino. Es decir, tenemos dos referencias a una misma tabla, COUNTRYCITY. Observa, si sólo escribiéramos esto que dibujo en el papel:

33 Recuerde que estamos simplificando la realidad: supondremos que no habrá nunca más de un aeropuerto por ciudad.

Page 118: Libro Genexus

118

…podríamos decir que los atributos CountryId y CityId en la transacción Flight representan una ciudad de un país,

pues se llaman igual que los identificadores de la transacción Country. Es decir, serán una clave foránea a la tabla COUNTRYCITY. Hasta allí nada nuevo bajo el sol, pero ahora debemos agregar otra ciudad34, en otro rol, el de “ciudad de destino”. Es decir, tiene que aparecer una doble referencia a una ciudad: en un caso en un rol de ciudad origen, y en el otro de ciudad destino. ¿Cómo hacemos esto? GeneXus no nos permitirá agregar la misma pareja de atributos en la misma transacción, eso es obvio porque ¡¿cómo se discriminaría entre un atributo y otro?! Pero podríamos hacer algo así –tomó el lápiz y escribió dos atributos nuevos, luego de lo cual antecedió a los dos primeros con la D de “departure” y a los dos últimos con A de “arrival”.

- Hmm… pero si GeneXus entiende que si se llaman distinto son otra cosa… ya no habría conexión entre vuelos y ciudades, ¿verdad?...

- Verdad. ¿Solución? Poder decirle a GeneXus que si bien se les ha cambiado el nombre a los atributos, por la necesidad de diferenciarlos, de todos modos corresponden al mismo concepto. Es decir, que no se confunda: que por más que el atributo DCountryId no se llame

34 Cuando hablamos de “ciudad”, nos referimos a una ciudad real, como París. Para identificar una ciudad, necesariamente debemos dar el par {CountryId, CityId}. Está sobreentendido. Por el diseño de las transacciones, una ciudad no tiene existencia independiente del país al que pertenece. Así, existe ciudad Rosario de Argentina y ciudad Rosario de Uruguay. Pero no existe Rosario, independiente del país del que se trate.

Page 119: Libro Genexus

119

CountryId, a todos los efectos es como si se llamase así, de modo que siga estableciendo las relaciones de integridad referencial igual que antes. Decirle esto a GeneXus es decirle que DCountryId es un subtipo de CountryId35. La solución completa, por tanto, es agrupar estas parejas en dos grupos de subtipos, ¿ves? – inquiría mientras rearmaba el croquis.

- ¡Ah, comprendo! Gracias a ello, DCountryId y DCityId serán una clave foránea en FLIGHT a la tabla COUNTRYCITY, al igual que ACountryId y

ACityId, ¿verdad? Tienes dos claves foráneas a la misma tabla. - Así es, y se deben realizar todos

los controles de integridad referencial automáticamente –contestó al tiempo que descubría un entusiasmo nuevo en Mary. ¿Podría luego quitarla del teclado?

- De todos modos el problema no se terminó aquí – continuó Diego –. En esta transacción, además de representar que un vuelo tiene un país-ciudad de origen y uno de destino, cosa que ya hicimos, queremos poder inferir el nombre de cada país y ciudad, para verlos en el form. Es decir, queremos inferir para mostrar atributos que pertenecen a la tabla extendida de FLIGHT, pero ¿cómo los inferimos si tenemos una doble referencia a la tabla?, es decir, no podemos poner directamente CityName, porque ¿de cuál se trataría? ¿Del atributo que se debe inferir de la clave foránea {DCountryId, DCityId}, o del que se debe inferir de la otra clave foránea {ACountryId, ACityId}? ¡Hay una ambigüedad!

“Y bueno, le tendrás que cambiar el nombre también a esos atributos que necesitas inferir (CountryName, CityName) y colocarlos en el grupo de

35 Recíprocamente, decimos que CountryId es supertipo de DCountryId

Page 120: Libro Genexus

120

subtipos adecuado” – pensó Mary, pero no emitió palabra –. Muéstrame cómo se definen estos grupos en GeneXus, anda; así lo termino de entender.

Diego seleccionó el icono Subtype Group de la ventana New Object, y le asignó por nombre “Departure”. Mary mantenía un silencio cómplice…

- Te había dicho grupos, y realmente es así. Creas uno, le das un nombre y le incluyes los “nuevos atributos”36 apuntando a sus respectivos supertipos. Entonces, expresado así, GeneXus sabrá que el atributo FlightDepartureCountryName será inferido a través del atributo FlightDepartureCountryId y no otro. Por pertenecer ambos al mismo grupo, “Departure”. ¿Comprendes la conexión?

- Comprendo –respondió ella–. Esto quiere decir que cuando el usuario digite un valor sobre la clave foránea {FlightDepartureCountryId, FlightDepartureCityId} no sólo se va a disparar el control de integridad referencial sobre COUNTRYCITY sino que se va a inferir en el atributo FlightDepartureCityName el nombre correspondiente a ese código de país-ciudad y también, por tabla extendida, se inferirá el CountryName de ese país

36 Obsérvese cómo Diego ha establecido el nombre de los subtipos respetando la nomenclatura GIK y comienzan con “Flight”, el nombre de la transacción, y continúan con el nombre del grupo, “Departure”. De esta manera se asegura una semántica clara, estableciendo el rol que juegan en la transacción.

Page 121: Libro Genexus

121

en FlightDepartureCountryName. Ahora habría que crear el otro grupo de subtipos, el de los arribos, ¿verdad?

- Bueno, no necesariamente, pues ya están claramente identificados los “dos” grupos; uno es el explícito, el que acabo de crear. Y el otro está implícito si yo coloco los atributos CountryId, CityId en la estructura de la transacción37. Pero por claridad, solemos en casos de este tipo mantener todas las ocurrencias en grupos, porque si no uno de los roles, en este caso el de ciudad de arribo, no quedaría claramente identificado. Te invito a que tú sola armes el otro grupo, ¿te animas? –preguntó Diego, señalando con su mano el laptop.

- Bueno, veamos… – dijo ella controlando su impulso de abalanzarse sobre el teclado. Tipeó, tipeó y tipeó, hasta que…

¡Perfecto! Diego ya tenía todo para crear la transacción Flight en GeneXus. Esta transacción será de dos niveles dado que para cada vuelo existen tres clases (“Business”, “Standard” y “Executive”) con tres precios diferentes, lo que en realidad la transforma en una lista de precios (relación 1-N). Todos los vuelos tienen hasta tres tarifas, no más.

Otra cosa importante: el identificador de vuelo no deberá numerarse automáticamente, dado que ese número lo determina un estándar internacional, que considera ruta y hora. Por ejemplo, el vuelo 0174, Montevideo-Buenos

37 Sólo con cambiarle el nombre a una de las dos ocurrencias de CountryId y de CityId alcanzaría para eliminar la ambigüedad.

Page 122: Libro Genexus

122

Aires, corresponde a un vuelo de la aerolínea Pluna de los sábados de mañana. Si ese vuelo cambiara para la tarde, también cambiaría su número de vuelo.

La siguiente imagen muestra la estructura final. Observe usted el ícono que indica que el atributo es un subtipo y además parte de clave foránea; y el ícono que indica que es un subtipo, por un lado, e inferido, por el otro.

Viendo que el atributo FlightClassType sólo puede tener hasta tres posibles valores fijos: “Business”, “Standard” y “Executive”, Diego creó un dominio enumerado para él. El atributo FlightDepartureTime almacena la hora del vuelo (no nos interesa la fecha). Obsérvese su tipo de datos DateTime. De él, sólo se utilizará la parte que representa Time, por lo que en sus propiedades, modificaremos bajo el grupo Picture la de nombre Date format. La pasaremos a ‘None’.

Antes y después

Para mostrarle a Mary en ejecución la nueva transacción, hizo <F5> tras lo cual tuvo que reorganizar la base de datos (tres nuevas tablas serán creadas).

Capacidad máxima de pasajeros en el vuelo

Page 123: Libro Genexus

123

Mary no pudo ocultar su cara de decepción. Diego, que estaba pendiente de todas sus reacciones y que también percibió un form un poco pesado de ver, agregó inmediatamente: “vamos a arreglar este form que está muy poco amigable”.

Para hacer el ingreso de país de partida y ciudad de partida más amigable para el usuario, podría utilizar combo boxes dinámicos, o disfrazar al Id del Nombre (recuerde lo que hizo en la página 69) y lo mismo con el país y ciudad de llegada, así como con la aerolínea.

Comencemos con los países y las ciudades; podría editar las propiedades del atributo FlightDepartureCountryId en la estructura y modificar su propiedad ControlType (y hacer lo mismo para los demás) a Dynamic Combo Box. - Ajá -se apuró Mary-,y en la propiedad

ItemDescriptions tendrías que poner FlightDepartureCountryName, que es donde quieres inferir el nombre del país, así saldrán como descripciones en el combo, los nombres de los países de la tabla COUNTRY…

Page 124: Libro Genexus

124

- Bueno, sí, eso es lo que quieres, pero por eso mismo, allí deberás colocar el atributo CountryName y no el subtipo, porque en verdad es así como se llama el atributo en la tabla COUNTRY y no FlightDepartureCountryName.

Así, Diego escribió CountryName en la propiedad ItemDescriptions y también modificó el valor de la propiedad ContextualTitle del atributo FlightDepartureCountryName que ahora dice “Departure country”. Hizo análogas acciones para FlightDepartureCityId, para FlightArrivalCountryId y para FlightArrivalCityId. Observe cómo luce ahora el form, sin que Diego tocara nada en él.

Hizo lo mismo para Airline, y además editó las propiedades del grid en el form, modificando el valor de Rows, pasándolo de 5 a 0. El valor de esta propiedad especifica la cantidad de líneas vacías que se presentan en ejecución para que el usuario ingrese los datos allí. Obsérvese que no es necesario tener ninguna línea vacía, dado que abajo del grid aparece [New Row] para

agregar una línea vacía al grid y poder llenarla. Nuestro amigo hizo <F5> para mostrarle a Mary en ejecución el “después”:

Page 125: Libro Genexus

125

...cuando ésta sorpresivamente anunció que debía irse.

- Mil gracias, Diego. Ahora discúlpame pero debo irme, te llamo en estos días”.

Un vuelo real: transacción FlightInstance

La transacción Flight pretende manejar datos generales de un vuelo, por ejemplo, el 0174 de la aerolínea Pluna, Montevideo-Buenos Aires, que sale todos los días a las 11:15 AM. Pero por cada vuelo 0174, tendremos un avión real, que realizará en un día determinado, ese viaje, con determinados pasajeros.

Lo que consigue la siguiente transacción es representar el vuelo real, con información como la fecha y hora de partida, la cantidad de pasajeros, sus

Page 126: Libro Genexus

126

reservas con sus nombres y números de asiento. Aquí el identificador sí será numérico y autonumerado.

Con este diseño, FlightNumber será una clave foránea. ¿Cuántos FlightInstance podrán haber para el mismo vuelo, FlightNumber? ¿Corresponde a lo deseado?

Existen algunos atributos aquí que merecen especial atención. Por ejemplo,

FlightDepartureTime38 es la hora de partida estipulada del vuelo (es inferido de FlightNumber), mientras que FlightInstanceDate y FlightInstanceTime son la fecha y hora reales, del vuelo real. La hora del vuelo real, se inicializa en forma predeterminada con el valor de FlightDepartureTime, pudiendo ser cambiada por el usuario, para lo que Diego declaró en las Rules: default(FlightInstanceTime, FlightDepartureTime);

Esta regla sólo se ejecuta cuando el modo es Insert. Por otro lado, observe

la otra regla que ha agregado Diego: error('Number of passengers exceeded') if FlightInstanceNumberOfPassengers > FlightTotalCapacity;

38 Si bien los atributos FlightDepartureTime y FlightInstanceTime son de tipo DateTime (permiten almacenar una fecha+hora), sólo nos interesa la representación de la hora.

count(FlightInstancePassengerSeatNumber)

Page 127: Libro Genexus

127

El atributo FlightTotalCapacity es inferido a través de FlightNumber. ¿Y FlightInstanceNumberOfPassengers? Es una fórmula que cuenta la cantidad de líneas que se han ingresado en el subnivel. Cada vez que se agregue un nuevo pasajero en el grid del form (que no hemos mostrado pero que usted imaginará sin problemas), se estará agregando un nuevo número de asiento para ese vuelo real, FlightInstancePassengerSeatNumber, por lo que la fórmula se actualizará, sumando 1, y si con ese pasajero se ha superado la capacidad, se disparará el error que impedirá que se inserte en la base de datos el pasajero para ese vuelo.

Por último, Diego declara la regla:

error('The flight departure cannot be advanced') if update and (FlightInstanceDate < FlightInstanceDate.GetOldValue() or (FlightInstanceDate = FlightInstanceDate.GetOldValue() and FlightInstanceTime < FlightInstanceTime.GetOldValue()));

Es decir, si se está actualizando un vuelo real, sólo se permitirá modificar su

día y/u hora de partida, siempre y cuando no se lo adelante. Obsérvese que se utiliza el método GetOldValue() de un atributo, que devuelve el valor que está almacenado en la base de datos, mientras que el atributo tiene ahora en memoria el valor que el usuario colocó en el form en este momento (aún no se ha grabado).

Por supuesto, se necesitará almacenar información de los pasajeros, por lo que Diego creará la última transacción de esta etapa y el atributo PassengerId pasará a ser clave foránea en FlightInstance, así como PassengerName inferido.

De carne y hueso: Transacción Passenger

Page 128: Libro Genexus

128

El siguiente Diagrama representa las relaciones entre las nuevas tablas y las viejas.

Diego pulsó <F5>. Todo parecía bien, así que reorganizó la base de datos. Luego accedió al Wiki, documentó la terminación de esta etapa y dejó a Mike la tarea de probar lo hecho y a Julia la tarea de aplicar los Work With al resto de las transacciones.

Page 129: Libro Genexus

129

Capítulo 5 Cuando el “qué” no alcanza

¿Sólo declarar?... ¿y las descripciones procedurales?

Fue lo que Mary cuestionó un par de citas después, en el bar de siempre. Se habían estado viendo con mayor frecuencia, en su oficina o en el bar, lo que había dado oportunidad a intercambiar sobre varios asuntos, como patterns, web panels y demás. Llegó a este nuevo encuentro, reflexiva. Decía entender que las transacciones permiten de manera declarativa describir las visiones de datos de los usuarios. Pero, ¿es esto suficiente? ¿Qué pasa con los procesos “batch” o similares, que en toda aplicación se necesitan? Estos procesos no se pueden inferir a partir del conocimiento aportado por las visiones de datos. ¿Entonces?

- Sí, faltaría un lenguaje procedural, estamos de acuerdo –ratificó Diego-. Si piensas en la filosofía de GeneXus, evidentemente deberá ser un lenguaje de muy alto nivel. Más aún: deberá ser un lenguaje que no utilice jamás referencias al Modelo Relacional, sino sólo al Externo. Te mostraré un ejemplo tonto: suponte que necesitas, por la razón que fuere, por ejemplo por malas condiciones en la pista de un determinado aeropuerto en un determinado momento de un determinado día, posponer la hora de partida prevista de todos los vuelos grandes (que sobrepasan determinada capacidad de pasajeros). Suponte que deberán atrasarse. En definitiva, deberás acceder a todos los registros de FLIGHTINSTANCE, tales que el valor de FlightInstanceDate y FlightInstanceTime coincidan con la fecha y hora en la que no deben despegar los aviones, y tales que correspondan a un vuelo que parta del país y ciudad determinado (aeropuerto) y que tenga una capacidad de pasajeros mayor que un valor determinado. Para esos registros debes modificar el valor de FlightInstanceTime, y eventualmente también el de FlightInstanceDate por los nuevos. Podrías hacerlo filtrando en el “Work With FlightInstance” por esos

Page 130: Libro Genexus

130

valores, y para cada línea (un vuelo real) desplegada en el grid, llamar a la transacción FlightInstance en modo Update y modificar los valores manualmente, lo que seguramente será inaceptable para el usuario final. La otra alternativa: desde algún web panel en variables pedirle al usuario los valores de fecha y hora a modificar, país y ciudad, y capacidad, y el valor de la nueva fecha y hora de esos vuelos, y luego llamar mediante una acción (botón o imagen) a un procedimiento que recorra los registros y realice el cambio – al tiempo que lo decía, abría su notebook e implementaba el web panel.

- ¿Un procedimiento común y corriente? - Sí, será un nuevo tipo de objeto GeneXus. Queremos que cuando el

usuario presione sobre el botón “Change flights”, que está asociado al evento Enter del web panel, se invoque a este nuevo objeto.

Event Enter ChangeDepartureTime.Call( &FlightDate, &FlightTime, &DepartureCountry,&DepartureCity, &NewDate, &NewTime, &capacity )

EndEvent - Vamos a crearlo entonces.

Segundos después, Diego tenía creado el procedimiento que implementaba

la lógica anterior:

Page 131: Libro Genexus

131

donde las variables que aparecen fueron declaradas por Diego como parámetros de entrada del procedimiento, en el selector Rules: parm( in: &CurrentInstanceDate, in: &CurrentInstanceTime, in: &CountryId, in: &CityId, in: &NewFlightInstanceDate,

in: &NewFlightInstanceTime, in: &FlightTotalCapacity);

- Espera, Diego, que me he perdido. Deduzco que el For each es un

comando que permite acceder a la base de datos recorriendo una tabla. Pero, ¿dónde le dices que es FLIGHTINSTANCE la tabla que debe recorrer? Además estás utilizando atributos de otra tabla también. ¿No deberías recorrer primero FLIGHT, y para los registros que cumplan con las condiciones, hacer un join con la tabla FLIGHTINSTANCE?

- No es necesario –observó Diego-. Para empezar, la tabla no se la dices directamente. Ahí está la gracia. Mira lo que le estás diciendo con el For each que hemos escrito:39 para cada registro de “la tabla donde se encuentren FlightInstanceDate, FlightInstanceTime, FlightDepartureCountryId, FlightDepartureCityId, y FlightTotalCapacity mencionados, tales que los

39 Respire profundo, amigo lector, no se nos vaya a quedar sin aire a la mitad…

Page 132: Libro Genexus

132

valores de los primeros cuatro coincidan con los de las variables recibidas por parámetro, y el valor de FlightTotalCapacity sea mayor o igual que el de la variable también recibida por parámetro”, cámbiale el valor que contengan FlightInstanceDate y a FlightInstanceTime por los indicados.

- Sigo sin entender. Esos atributos están en tablas distintas. FlightInstanceDate y FlightInstanceTime están en FLIGHTINSTANCE, sí, pero los demás están todos en FLIGHT… ¿No deberías recorrer…?

- Cierto –interrumpió Diego sin dejarla terminar-. Pero FLIGHT está en la tabla extendida de FLIGHTINSTANCE. Es decir, por cada vuelo real, tienes un FlightNumber (clave foránea) que corresponde a un y solo un registro de FLIGHT, que tiene un FlightDepartureCountryId, un FlightDepartureCityId, y un FlightTotalCapacity. Hay una relación N a 1 entre FLIGHTINSTANCE y FLIGHT. Con eso no necesita más: sabrá que debe recorrer la tabla FLIGHTINSTANCE, accediendo para cada registro a su correspondiente en FLIGHT, es decir, haciendo un join automático para saber el valor de los atributos FlightDepartureCountryId, FlightDepartureCityId y FlightTotalCapacity y con eso decidir si se queda o no con el registro para procesarlo (por supuesto, siempre y cuando el propio registro de FLIGHTINSTANCE cumpla con ser del día y hora indicados por las variables &CurrentInstanceDate y &CurrentInstanceTime). La condición que impone sobre un registro para quedarse con él y procesarlo, es la concatenación con And de todas las condiciones lógicas que ves en las cláusulas Where del For each. Como antes hablamos de tabla base, aquí también: para el For each es la tabla que recorre. Observa, entonces, cómo conociendo las relaciones entre las tablas y dónde están los atributos en el momento del análisis, GeneXus puede inferir el acceso a los datos sin que le tengas que nombrar tabla alguna. Esto tiene una consecuencia importante: permite que se puedan cambiar de tabla esos atributos, sin que la descripción del procedimiento pierda validez40.

40 Por supuesto: siempre y cuando los atributos pertenezcan a una misma tabla extendida. En caso contrario GeneXus arrojará un error, informando que no fue posible relacionar los atributos.

Page 133: Libro Genexus

133

- ¿Por qué? Hmmm… parece un poco mágico, no me sentiría segura programando así.

- No Mary, créeme, nada es mágico. Miremos el listado de navegación para aclarar tus dudas (y las mías; siempre antes de ejecutar un procedimiento para probarlo, el primer test ya está hecho: es el listado de navegación, que informa sobre gran parte de lo que te da inseguridad. Por eso, nunca ejecuto sin antes mirar ese listado. Bueno, está bien, a ti no te voy a mentir. A veces no lo miro, de apurado nomás, pero luego me arrepiento). Mirémoslo juntos… (si sólo quiere verse el listado de navegación, en lugar de hacer <F5> que además construirá todos los programas, se puede hacer botón derecho sobre el tab del procedimiento, y elegir la opción View Navigation)

- ¿Ves? Te informa la tabla base del For each, el orden por el que

procesará la información41 y el índice que propone para satisfacer ese orden (si es que existe). Además te informa los filtros de navegación, es decir, desde dónde empezará a recorrer la tabla y hasta dónde (en nuestro caso, recorrerá

41 El orden puede indicarse mediante la cláusula “Order” del comando For each. Como no se ha indicado, y no existe ningún índice en la base de datos que permita optimizar la búsqueda de los registros que cumplan estas condiciones, asume clave primaria de la tabla a recorrer.

Tabla base

Page 134: Libro Genexus

134

toda la tabla42). Luego se observan las Constraints (filtros sobre los registros a recorrer). Más abajo te informa que por cada registro de FLIGHTINSTANCE deberá hacerse un join con la tabla FLIGHT de su extendida (para realizar los filtros por FlightDepartureCountryId, FlightDepartureCityId y FlightTotalCapacity) y que se actualizarán de la tabla FLIGHTINSTANCE los atributos FlightInstanceDate y FlightInstanceTime. Sé franca, Mary, y dime cuánto más te costará implementar esto mismo utilizando las herramientas que usas. Habrías implementado tú misma este join. Lo mejor de todo, en verdad, es el hecho de seguir hablando en alto nivel. Piensa qué pasaría en tu implementación, que seguramente requerirá nombrar las tablas, si mueves por ejemplo, el atributo FlightTotalCapacity de FLIGHT a FLIGHTINSTANCE. Aquí no tendrás que tocar absolutamente nada. Simplemente volver a generar el objeto, para que GeneXus infiera la nueva navegación y ¡listo!

- Sí, comprendo. ¿Y cómo insertas y eliminas registros de la base de datos?

- Bueno, el comando For each te permite recorrer una tabla de la base de datos, y acceder a su extendida, tanto para consultar solamente, como para actualizar atributos. Por este motivo, es un comando importantísimo, que podrá ser utilizado no sólo en procedimientos sino en todo objeto GeneXus donde se admita código procedural (como los eventos de transacciones y web panels, por ejemplo). Para las inserciones y eliminaciones se han creado dos comandos específicos: New y Delete43.

42 Si existiera un índice compuesto por {FlightInstanceDate, FlightInstanceTime, FlightDepartureCountryId, FlightDepartureCityId, FlightTotalCapacity}, lo propondría sin dudar, optimizando la consulta, de modo que ya no tendría que recorrer toda la tabla y esto lo informaría en los “Navigation filters” del listado de Navegación. Aun si no existiera este índice, pero sí uno compuesto por un subconjunto de esos atributos, lo propondría. Es decir, siempre que exista un índice que le permita optimizar la consulta, lo propondrá. (No lo dijimos antes, pero en cualquier momento usted puede pedirle a GeneXus la creación de un índice de usuario) ¿Por qué decimos “lo propondrá”? Pues para los generadores SQL si bien esta información es útil e influyente, en realidad es el propio DBMS quien resuelve el plan de acceso más apropiado. Para los no SQL (RPG, COBOL, VB-Access, VFP-DBF) en cambio, es vital. Existe mucho para conversar sobre este tema, estimado lector, pero no viene a cuento en esta historia. 43 En ninguno de los casos se hacen controles de integridad referencial, permitiendo así insertar registros que apuntan a un registro inexistente, o eliminar registros que son apuntados por otros, dejando a estos últimos huérfanos. El único control que se realiza es el de unicidad. Hablamos de controles programáticos, ya que en GeneXus se puede indicar si se desea activar la integridad referencial a nivel del DBMS, en cuyo caso siempre se controlará.

Page 135: Libro Genexus

135

- Ahora que pienso, no estás controlando en el procedimiento lo que sí controlas en la transacción FlightInstance: que no intenten cambiar la fecha y hora del vuelo, adelantándolo. Si mal no recuerdo, allí tenías una regla de error que controlaba eso.

- ¡Tienes razón! -. Pensativo, Diego rumiaba algo. Mary lo miraba entusiasmada. Tras unos segundos de meditación, rompió el silencio-. Tendría que hacer ese control en el procedimiento, preguntando con un comando If antes de apresurarme a actualizar así sin más. Toda vez que quiera modificar fecha y/u hora de un vuelo real, en cualquier objeto, tendré que volver a hacer el mismo control que hacía mediante la regla en la transacción. Repitiendo el código. Una y otra, y otra vez. Quizás en alguna me olvido…. o me equivoco.

A lo seguro y sin transpirar: el “qué” de los Business Components

- Ya que sabemos –continuó con una renovada vitalidad- que las transacciones son uno de los objetos más importantes, porque en ellas se recogen las visiones de los usuarios, las reglas del negocio, las fórmulas, y se ejecutan los controles de unicidad y de integridad referencial, ¿qué me dices sobre reutilizarlas desde cualquier otro objeto GeneXus?

- ¡¿Ehh?! –la expresión de su rostro provocó una sonrisa en nuestro amigo.

- Sí, reutilizarlas, aprovechando todo ese conocimiento que ya tienen incorporado. Por ejemplo, si yo tuviera una forma de encapsular toda la lógica de la transacción FlightInstance, dejando de lado la interfaz… es decir, si pudiera tener algún buffer o parecido, alguna estructura temporal, por ejemplo una variable compuesta, estructurada, donde cargar los datos de un vuelo real (cabezal y líneas)… podría ingresarlo en la base de datos desde cualquier objeto… Si realmente tuviera encapsulada la lógica de la transacción de esa forma, entonces se permitirá la inserción solamente si se cumple todo lo que se debe cumplir para ingresar esa misma información vía la transacción… Quiero decir: si quisiera insertar una instancia de vuelo asociándole un vuelo

Page 136: Libro Genexus

136

(FlightNumber) inexistente, o pretendiendo agregar algún pasajero inexistente, fallará y no me dejará hacer la inserción, tal como ocurre con la transacción. O, por ejemplo, si quiero agregar un pasajero existente pero con el que se excede la capacidad del vuelo, no me dejará realizar esa inserción, tal como sucede cuando quiero hacerlo vía la transacción, dado que tengo una regla de error en ella que lo impide –. Diego abrió la transacción FlightInstance y mostró a Mary la sección Rules. Usted, estimado lector, puede buscarla en el capítulo anterior-. Encapsular la lógica de la transacción, quedándose con la estructura pero dejando de lado el form es posible: se llama Business Component. Es un tipo de datos generado por GeneXus a partir de una transacción44.

- ¿Y cómo haces para encapsular todo eso? ¿y cómo lo usas? - A la transacción que quieres utilizar de esta otra forma “silenciosa”,

alcanza con cambiarle el valor de su propiedad Business Component a “True”. A partir del momento en que guardas este cambio, GeneXus creará un nuevo tipo de datos Business Component, cuyo nombre será el de la transacción45.

- ¿Y cómo lo utilizas? –volvió a preguntar, intrigada. - A través de variables basadas en ese tipo de datos. Siempre que

quieras actualizar, insertar o eliminar una instancia de vuelo, en vez de utilizar For each, New y Delete, podrás utilizar toda la potencia de la transacción FlightInstance a través de las propiedades y los métodos de una variable basada en el tipo de datos FlightInstance, business component, que GeneXus generó. Modifiquemos el procedimiento anterior para hacer la actualización mediante un Business Component –prosiguió Diego su monólogo; Mary observaba expectante sin hablar-. Necesitamos una variable, &FlightInstance de este tipo de datos (generado

44 Los Business Components se obtienen de objetos transacción, pero pueden ser utilizados desde cualquier otro objeto GeneXus, para hacer inserciones, eliminaciones y modificaciones en sus tablas. 45 Si la transacción tiene 2 niveles, creará un business component por nivel.

Page 137: Libro Genexus

137

cuando pasamos a “True” la propiedad Business Component de la transacción). En ella alojaremos todo el contenido de la instancia de vuelo a cambiar. Esta variable será, lógicamente, estructurada, será el “buffer” donde temporalmente tener los datos del vuelo que queremos manipular en un momento dado. El código nos quedaría más o menos así…- y sustituyó en el objeto las dos asignaciones directas por las cuatro sentencias que se pueden observar en la siguiente imagen-. Observa que aquí sólo utilizamos el For each para recorrer la tabla base y filtrar, pero no actualizamos directamente:

¿Ves? Lo primero que hago aquí es cargar desde la base de datos la instancia de vuelo a modificar, dentro de la variable &FlightInstance, según el valor de FlightInstanceId que corresponde al registro que cumplió las condiciones de filtro dentro del For each (es el registro de esa iteración). Luego cambio solamente los datos que debemos modificar, y luego grabo, con el método Save. Observa la analogía: para editar un vuelo mediante la transacción, hubieses colocado el identificador en el campo FlightInstanceId del form, y salido del campo. Para hacer eso con el Business Component, utilizamos el método Load. En la transacción hubieses cambiado en el form los valores de los campos FlightInstanceDate

Page 138: Libro Genexus

138

y FlightInstanceTime y luego hubieras presionado [Confirm]. ¿Y aquí? Cambias lo valores en la variable, y luego ejecutas el método Save. Lo que pasará como consecuencia de esas acciones, es prácticamente lo mismo. Quiero decir, ¿qué pasaría si en la transacción modificaras fecha y hora de forma tal que adelantaras el vuelo? - Se dispararía el error ese que habías programado como regla, y no nos

dejaría grabar, o sea, no se realizaría ese cambio en el registro. Lo dejaría como estaba antes.

- Exactamente. ¿Y qué piensas que sucederá aquí, en el procedimiento? - ¿Lo… mismo? –preguntó y afirmó al mismo tiempo-. - ¡Lo mismo! Disparará la regla, y como su condición se satisfará, no te

dejará grabar el cambio. - ¿Y cómo me entero? - Bueno, en el caso de la transacción, como es un objeto interactivo, allí

no hay duda, te despliega mensaje. Sin embargo, un Business Component no lo es, razón por la cuál los posibles mensajes y errores producto del procesamiento (ejecución de métodos Load, Save o también Delete, el utilizado para eliminar) deben quedarte almacenados en algún lado. GeneXus los inserta en una colección (lista) de mensajes asociados a la variable Business Component de que se trate, y la puedes recuperar con un método (GetMessages). Luego tienes la posibilidad de recorrer esa lista de mensajes… Pero si sólo te interesa saber si se produjo algún error, tienes el método Fail que te devuelve “True” en caso de error: así, en nuestro caso, programarías “if &FlightInstance.Fail()…”

- Entiendo. ¿Y qué otros errores podrían producirse cuando manipulas la información con el Business Component?

- Los mismos que cuando intentas insertar mediante la transacción FlightInstance. Es decir: los de integridad referencial (si quisieras cambiar por ejemplo el vuelo, FlightNumber, que es clave foránea, por uno inexistente), el de unicidad (por ejemplo, si quisieras insertar una instancia de vuelo nueva, y le asignas un FlightInstanceId que ya existe para otro registro), las validaciones

Page 139: Libro Genexus

139

de tipos de datos, las reglas de error declaradas en la transacción, y cuyas condiciones de disparo se satisficieran…Observa cómo en ese sentido es más seguro hacer un procedimiento utilizando Business Component donde sabes que se van a disparar todas las reglas del negocio, sin tener que repetirlas.

- ¿Todas? Si no te quedas con la interfaz visual, ¿qué ocurre si en la transacción hubiera una regla que disparara una llamada a un web panel, por ejemplo?

- Bueno, no todas… GeneXus ignora en el Business Component las reglas que hacen uso de interfaces de usuario. Esa no será incluida.

- Interesante… -dijo pensativa.

¿A dónde quieres llegar? Atracciones que invitan… en pdf

El lector observador habrá notado lo que Mary en su momento (y que prefirió dejar en el tintero esperando la oportunidad propicia):

- Entre los selectores del objeto de tipo Procedimiento aparece uno “Layout”. ¿Para qué? ¿Acaso un procedimiento te permite efectuar una salida visual?

- Pues sí. Casualmente los usuarios nos han pedido como requerimiento que el sitio de la agencia de viajes tenga una página que muestre las atracciones por ciudad, y un link del estilo “Ver como pdf”. Para implementar esto último, creé un procedimiento al que llamé AttractionsPerCity. Mira –dijo Diego mientras mostraba el objeto en su notebook, seleccionando “Layout”-:

Page 140: Libro Genexus

140

Le tendré que decir a este procedimiento que tendrá una salida como pdf. La idea del Layout es declarar qué es lo que quiero mostrar en la salida. Como resultado de la ejecución, quiero mostrar para cada ciudad, todas sus atracciones. Evidentemente, el usuario final tendrá que tener Acrobat Reader instalado para que desde el Browser se pueda abrir correctamente el archivo pdf generado. Para indicar qué es lo que se quiere mostrar en la salida y su formato, el Layout contiene áreas de impresión, que son controles llamados printblock. Coloreados en la imagen, puedes ver donde empiezan dos de esos controles: City_Block y Attraction_Block. Esos nombres se los di yo luego de insertar cada control. Dentro de cada printblock insertas la información (atributos, variables, imágenes, líneas, recuadros, etc.) que deseas desplegar en esa área de datos, cuando sea invocada desde el código. Por ejemplo, observa que dentro de City_Block he insertado los controles atributo CityId y CityName, porque esa es la información de cada ciudad que querré mostrar en el listado. - ¿Y sólo con eso GeneXus ya sabe qué listar y cómo? - Mary, espera. El amigo no es mago, sólo es inteligente. Con eso solo

no puede saber cómo quieres listar la información. Falta el código –dijo enarcando las cejas, con expresión risueña, viendo aparecer un rubor...

Page 141: Libro Genexus

141

- Bueno, lo presentas como tan maravilloso… bien podría adivinar tus intenciones, ¿no? –sonrieron ambos.

- ¿Tú las adivinas? –avanzó Diego. - ¿Y si las explicitas? –contestó Mary sin retroceder, mirándolo

fijamente a los ojos, con una expresión que a él se le antojó encantadora. - ¡Sus deseos son órdenes!...–. Y tras un breve silencio que a ambos

pareció eterno, él continuó-. Quiero recorrer las ciudades, y para cada ciudad, sus atracciones. Esto implica la utilización de dos For eachs anidados.

Mary aproximó su silla a la de él, tomó el mouse sin preguntar, siempre sonriendo, y se posicionó en el selector Source. La ventana se transformó, mostrando el siguiente código:

Diego continuó: - Observa que no necesitas establecer el filtro por ciudad para el

segundo For each: es que existe una relación 1-N directa entre las tablas que se recorren, siendo uno de los casos más comunes de For eachs anidados. La tabla base del For each externo es COUNTRYCITY, y la del interno es ATTRACTION (por los atributos de los printblocks involucrados), y como GeneXus conoce las relaciones entre las tablas, él implícitamente aplica la

Page 142: Libro Genexus

142

restricción sobre los registros a recuperar. Pero es más potente que eso, encuentra también relaciones 1-N indirectas. Por ejemplo, si quisieras listar para cada aerolínea, la cantidad de instancias de vuelo46.

Mary, muda, no emitía palabra. Miraba la pantalla. Una sonrisa seguía estampada en su rostro. Posicionó el mouse sobre el Tab del procedimiento donde se encontraba su nombre, y con botón derecho seleccionó “View Navigation” del menú contextual. Diego la miraba. Apareció un nuevo Tab en la pantalla, conteniendo la siguiente ventana:

- ¿Ves lo que digo? Está restringiendo la búsqueda- explicó a esta mujer que tenía tan cerca y que sólo observaba y sonreía-. Te lo indican los filtros en la navegación de la tabla ATTRACTION en el segundo For each. Observa el “@”. Está indicando que toma esos valores del contexto. ¿Cuáles son @CountryId y @CityId? Los del registro del primer For each con el que se encuentra trabajando en esa iteración. Esas intenciones GeneXus sí las descubre.

Diego continuó, llenando el silencio que Mary no llenaba:

- Para convertir este listado en ejecutable, deberás cambiar 2 propiedades: Main Program = “True”, Call Protocol = “HTTP” para que se

46 Entre las tablas AIRLINE y FLIGHTINSTANCE hay una relación 1-N indirecta (está la tabla FLIGHT de intermediara). GeneXus encuentra en muchos otros casos relación 1-N indirecta y hace el join.

Page 143: Libro Genexus

143

ejecute en el Browser. Además deberás incluir una regla en el selector Rules “output_file("AttractionsPerCity", "pdf");” que es donde le dices que la salida será un archivo pdf, con el nombre que especifiques. También puedes decirle que quieres que envíe la salida a alguna impresora de las que estén bajo el alcance de la computadora desde la que se esté ejecutando el navegador con la aplicación.

Para ejecutarlo, Diego se posicionó sobre el tab con el nombre del procedimiento, y con botón derecho seleccionó la opción “Run with this only”, para que GeneXus construya y ejecute ese procedimiento únicamente. La cara de asco de Mary no se hizo esperar. Es que el listado sólo mostraba datos, sin ningún formato. Diego no esperó. Grabó el procedimiento con otro nombre, y le mejoró el diseño y la información mostrada…

…obteniendo ahora en ejecución:

Page 144: Libro Genexus

144

Por fin Mary rompió el silencio. - Me atrae París… ¿Por qué no vienes esta noche a cenar a casa? Y me

sigues contando…

Page 145: Libro Genexus

145

¿Y las consultas?

Otro de los grandes alcances de GeneXus X es el objeto Query que permite crear consultas a la base de datos, y mejorar la salida de la información recuperada. Ésta puede ser visualizada en diferentes formatos (barras, tortas, 3D o planas, área, etc.) lo que ayudará a enriquecer sus aplicaciones. Además permitirá al usuario realizar consultas dinámicas (qué measure ver) a través de pivot tables, etc.

Una consulta de este tipo es definida como una estructura. Supongamos que deseamos obtener una consulta de cantidad de despegues en una fecha dada (FlightInstanceDate) por aerolínea (AirlineName) para cada ciudad (CityName); en la siguiente imagen se resume esta intención:

Al objeto Query, creado desde la ventana New Object, le hemos asignado el

nombre QSoldSeatsByArrivalCities. En el nodo Attributes hemos insertado los atributos AirlineName y FlightArrivalCountryName, que serán los que queramos ver en tiempo de ejecución. Inmediatamente debajo, hemos

Page 146: Libro Genexus

146

declarado la función Count, pero según las necesidades, se pueden declarar también a Sum y Average47

Podemos también incluir parámetros, y en este caso en particular hemos incluido una variable basada en un atributo en particular.

Luego tenemos el nodo Filters donde, si fuera necesario, estableceremos las

condiciones para recuperar los registros a mostrar; en este caso, todos aquellos cuyo valor del atributo FlightInstanceDate (incluido en FLIGHTINSTANCE) sea igual al valor del parámetro que hemos nombrado como &DepartureDate.

Por último, tenemos el nodo OrderBy donde, de ser necesario, deberemos establecer el o los atributos que deberán ser considerados; en este caso, los datos se mostrarán en orden alfabético según el contenido del atributo AirlineName.

Ahora, ¿cómo ejecutamos esta consulta? Deberemos crear un web panel con el aspecto que deseemos, como cualquier otro, pero al cual le deberemos insertar el User Control Query Viewer que se encuentra en la Toolbox del IDE.

47 No todas las funciones de agregación aplican para todos los tipos de atributos. Sum y Average están solo disponibles para atributos numéricos.

Page 147: Libro Genexus

147

Luego accedemos a sus propiedades y seteamos aquellas que sean necesarias. En este caso se ha modificado la propiedad Query, a la cual se le asignó el nombre del objeto Query que definimos al inicio, QSoldSeatsByArrivalCities, y también se ha modificado la propiedad Type; esta tiene por finalidad establecer alguno de los tres tipos posibles de salidas: Pivot Table, Table, y Chart. Le recomendamos que pruebe para ver las diferencias entre ellos. Considere que para el valor Chart dispondrá además de la propiedad ChartType donde podrá escoger un estilo entre doce posibles. Y eso es todo para lograr una construcción básica de un Query.

En el selector SQL statement se puede observar el código que GeneXus ha generado y que será ejecutado sobre la base de datos para obtener la vista deseada en cuanto se ejecute el web panel diseñado para tal fin.

Page 148: Libro Genexus

148

Ahora <F5> y, ¡voilà!

Page 149: Libro Genexus

149

Capítulo 6 Más declaraciones

Nuevos protagonistas del conocimiento: Data Providers

La meta de GeneXus: que usted declare, declare y declare. Cuando uno declara dice el qué, y no se preocupa por el cómo, pues éste varía con el tiempo. Con frecuencia se encuentran nuevas formas de implementación, mejores, más eficientes. El qué es estable, la esencia. Conocimiento que permite automatizar. Las transacciones y los business components eran, así, las estrellas de GeneXus: declaraban la lógica del negocio. Declaraban conocimiento. GeneXus encontraba, en cada etapa de su evolución, un cómo. Ahora, un nuevo protagonista irrumpe en escena: el Data Provider. Para una gran variedad de casos, pasaremos de programar procedimientos, a declarar data providers.

¿Quedó usted intrigado? Mary también. Vea lo que pasó.

¿Declarándose?

Sin responder a la pregunta que ella dejara sonando en el aire, Diego abre torpemente su inbox de correo y encuentra un e-mail de la agencia donde le solicitaban un nuevo requerimiento: debía generar un archivo de interfase con formato XML para enviársela a cierta aerolínea donde figurara la cantidad de plazas vendidas por vuelo de esa aerolínea, en cierta fecha.

- Mira esto –susurró a Mary girando la notebook para que ella leyera. - Fácil –contestó ella demorando en leer. Su vista se había detenido

antes en otros mensajes de correo, y Diego lo percibió, pero no dijo nada–. Implementas un procedimiento parecido al que vimos. Pero ¿cómo se hace en GeneXus para generar la salida en formato XML? –si bien parecía estar

Page 150: Libro Genexus

150

pensando en otra cosa, su conversación era completamente coherente. ¿En qué pensaría sin decirlo?

- No es complicado, ya veremos. ¿Pero sabes una cosa? Esta vez no lo haremos con un procedimiento. Antes de la X no nos quedaba otra opción, pero ahora disponemos de algo más potente para este tipo de casos.

- ¿Más potente? - Sí, nada de codificación procedural. Fíjate que aquí necesitas

consignar información estructurada: imagínate cómo debería ser el XML resultante, por ejemplo… –. Creó un documento xml, escribió y mostró:

Los ojos de Mary, entrecerrados por el ceño contraído, habilitaron a

Diego a continuar la explicación: - Para esto usaremos un Data Provider, que es un objeto ideado

específicamente para devolver datos estructurados. Te voy a mostrar una cosa: si quisieras implementar este requerimiento como un procedimiento en GeneXus, programarías algo así:

Page 151: Libro Genexus

151

- ¿Ves cómo en este código están mezclados los elementos de la entrada, con los elementos de la salida? Dime la verdad, sin importar si entiendes o no el código, ¿no te resulta complicado aquí encontrar cuál será la estructura resultante y de qué modo se carga? Esto es porque los procedimientos privilegian el lenguaje de transformación del input para obtener el output. Por lo que el output queda oscurecido y no es fácil identificarlo. Sin embargo, en los Data Providers lo privilegiado será el Output. De hecho, declaras tu intención, mediante simples ecuaciones del estilo x = y para cada elemento de la estructura jerárquica que quieres devolver cargada.

- Muy interesante, Diego, pero se me hace un poco tarde, y veo que esto puede llevar un tiempo…

- ¡Cinco minutos! – acotó inmediatamente, ocupado en demorar el fin del encuentro. Mary dudó unos segundos y por fin continuó:

- Bueno, entonces anda, “declara”. Diego creó un nuevo objeto, al que llamó LoadNumberOfPassengers, de tipo Data Provider. Mary observó la imagen.

Page 152: Libro Genexus

152

- Ajá. Tiene casi los mismos selectores que los procedimientos. - Sí, lo que varía es lo que se escribe en el selector Source, pues no será

código procedural, sino declarativo. Pero vamos por partes. Según el requerimiento, algún otro objeto capturará del usuario el número de aerolínea y la fecha, y a ambos los recibiremos como parámetros. Así que definamos entonces las variables y declarémoslas en las Rules –tras lo cual lo hizo.

parm(in:&AirlineId, in:&DepartureDate);

- Bien, ahora vamos al Source –dijo Mary con confianza. - Bueno, todavía no. Debemos definir un tipo de datos estructurado que

será el tipo de datos de la información devuelta por el Data Provider. - ¿Tipo de datos estructurado? ¿Como si fuera un struct en C, o .Net, o

un record de Pascal? - Eso. Una estructura que puede contener tanto datos simples como

otras estructuras, así como colecciones (listas) de datos. Incluso puede ser él mismo una colección. En GeneXus defines esos tipos de datos, no en la ventana de Dominios, sino como objetos (algo parecido a como se hace en Java definiendo clases que luego se instancian). Mira, en nuestro caso definiremos un objeto de tipo “Structured Data Type”, o simplificando, SDT, y allí ingresaremos su estructura:

Page 153: Libro Genexus

153

- ¿Ves? –se apresuró a proseguir-. Este tipo de datos se compone de tres miembros, de nombres Name, DepartureDate y Flight. Los dos primeros, de tipos de datos simples, y el tercero, será una colección de ítems (observa el check box Is Collection), cada uno de los cuales es estructurado; les hemos llamado FlightItem. Los miembros de éste último estructurado también son tres. Observa que como he llamado a los dos primeros miembros igual que atributos de mi KB, infirió como su tipo de datos el mismo que el de los atributos homónimos. Les puse el mismo nombre que a los atributos porque en realidad voy a cargar este SDT con los valores de esos atributos, luego. Pero no nos detengamos en ello.

Ante la atenta mirada de Mary, Diego picó sobre el nombre del SDT que ahora aparecía en el árbol del Folder View, lo arrastró hacia el área Source del Data Provider y soltó el botón, tras lo cual apareció:

Page 154: Libro Genexus

154

De forma automática, GeneXus inicializó

en el Source la estructura que será devuelta por el Data Provider; es más: dio valor a la propiedad Output que indica cuál será la estructura devuelta por el Data Provider.

Ahora Diego sólo debe sustituir los comentarios del Source por los valores que correspondan a cada miembro.

Page 155: Libro Genexus

155

Vio cómo los rojos labios de Mary se unían para pronunciar lo que adivinó ser un “pero…”, aunque no llegó a saberlo, pues continuó: - Espera, no he terminado. Aún no estoy filtrando por los parámetros

recibidos. Mira cómo completo el Data Provider:

- ¿Cláusulas where, como las de los for eachs? –preguntó Mary

pensativa, haciendo la asociación clave.

Page 156: Libro Genexus

156

- Efectivamente. En el lenguaje de Data Providers tendrás grupos, estos son los que contienen elementos. En nuestro caso: SeatsSoldByFlight, Flight y FlightItem son grupos. Los grupos contienen elementos, variables y/u otros grupos. En cualquier caso, un grupo podrá presentarse en la salida una sola vez, o múltiples veces. GeneXus tiene la inteligencia para determinarlo.

- ¿Cómo? - Por lo que decías tú. La presencia de atributos a la derecha de las

asignaciones en los elementos, así como en las cláusulas where del grupo están determinando que esos grupos son como for eachs, que acceden a una tabla base.

- Si no hubiera cláusulas where, tal como estaba el Source antes de que las agregaras, y si en lugar de atributos a la derecha de las asignaciones tuvieras variables o literales…

- …en ese caso, en la salida tendrías cargado el SDT de forma tal que la colección Flight estaría compuesta únicamente por un ítem. En cambio, en nuestro caso, el grupo Flight contendrá muchos ítems, tantos como instancias de vuelos haya para esa fecha y aerolínea, y en cambio el grupo raíz SeatsSoldByFlight será único, puesto que estamos filtrando por AirlineId.

- Ah, entiendo –exclamó Mary–. Es como si tuvieras un par de For Each anidados. El primero, por los atributos que aparecen, recorrería la tabla AIRLINE, y el segundo la tabla FLIGHTINSTANCE.

- Perfectísimamente. Mira el listado de navegación:

Page 157: Libro Genexus

157

- Observa además cómo en el lado derecho de los elementos puedes

poner tanto atributos como cualquier función o fórmula que devuelva un resultado del mismo tipo de datos que el que definiste para ese elemento en el SDT. Por eso utilizamos una fórmula count para SoldSeats. En realidad, como esa fórmula la tenemos también global en el atributo FlightNumberOfPassengers, podríamos haber colocado directamente este atributo. No lo hice para mostrarte que es posible escribir cualquier cálculo.

- Entiendo ahora por qué dices que los Data Providers son declarativos. Lo que tienes que hacer es declarar cuál será la estructura resultante, y luego cargarla a través de ecuaciones x = y donde en y declaras cómo se calcula x. Ahora, ¿cómo devuelves el XML que necesitas?

- Bueno, en realidad deberás invocar a este Data Provider como si fuera un procedimiento común y corriente desde cualquier objeto, y hacer lo que quieras con la salida, por ejemplo, convertirla a XML –contestó Diego-. Creo que lo mejor será que lo haga, así lo vas a entender mejor.

Page 158: Libro Genexus

158

Creó un Web Panel y tres variables: &AirlineId, &DepartureDate y &SeatsSoldXML, esta última de tipo de datos LongVarChar y de 2 MB (será la que contendrá la información en formato xml):

A continuación se posicionó en el selector Events y escribió:

- Espera, espera… ¿y la variable &SeatsSold a la que le estás asignando

lo devuelto por el DataProvider? - Olvidé definirla –tras lo cual lo hizo. Su tipo de datos será,

lógicamente, el SDT devuelto, es decir: SeatsSoldByFlight. - Ajá, e infiero que el método ToXML aplicado a una variable SDT

tiene por efecto convertir el contenido del formato SDT al formato XML. ¿Cierto?

Page 159: Libro Genexus

159

- Cierto. El valor “true” del parámetro solamente está indicando que le agregue el header al xml; ahora no importa… entonces luego de ejecutado el método, la información está contenida en memoria dentro de esa variable &SeatsSold.

- Ahora lo único que te queda es generar, por ejemplo, un archivo con ese contenido y listo –agregó Mary mirando su reloj pulsera.

- Sí, y todavía quizá mejor: puedo enviarlo directamente por mail a la compañía aérea.

- Ajá… Diego, escúchame, no sigas. Te agradezco la explicación, no te enfades conmigo, pero me esperan, debo marcharme. Te espero esta noche en casa, y seguimos con las declaraciones… ¿te parece?

- Sí, pero omití decirte que un Data Provider también puede cargar y devolver un business component, porque la estructura de un business component es como la de un SDT. Luego puede ser insertado en la base de datos desde el objeto llamador, con el método Save, como siempre.

- Me lo cuentas luego. Nos vemos esta noche, ¿sí? Diego permaneció en el bar varios minutos más, con la vista perdida por

donde ella había partido minutos antes, dejando tras de sí su estela de perfume.

Page 160: Libro Genexus

160

Page 161: Libro Genexus

161

Capítulo 7 Soplan nuevos vientos

Poniendo la casa en orden: categorización

Esa noche Diego llevó el vino. Y la notebook. La música de fondo creaba un ambiente acogedor.

- Debes cumplir lo prometido –reclamó Mary sonriendo, mientras llenaba su copa-. Dime más.

- ¿Sobre GeneXus? Bueno, no sé qué más decirte…- Parecía nervioso, aunque pronto salió del paso-. Hoy por ejemplo estuve categorizando los objetos de la KB. No sé si recuerdas, pero el IDE tiene un panel rotulado Category View en el contenedor Knowledge Base Navigator que despliega, en árbol, todos los objetos que hayas clasificado dentro de sus correspondientes categorías. Espera que te muestro –dijo depositando la notebook en su falda y la copa sobre la mesita próxima al sofá.

- No veo clara la finalidad –respondió Mary sentándose a su lado. - Bueno -mientras la notebook terminaba

de iniciar, Diego llenaba el incómodo silencio-, por ejemplo, en mi compañía solemos tener categorías para identificar el estado de los objetos: “Operating”, “Under Review”, “Under Construction”. Así, puedes saber en un simple pestañeo, qué objetos están prontos para ser testeados, simplemente abriendo la categoría “Under Review”. En este caso, separamos nuestro universo de objetos en categorías disjuntas. De hecho, hasta puedes darle un nombre a este criterio de clasificación de tu universo, justamente mediante una categoría, “Status” por ejemplo, que contenga a estas tres

Page 162: Libro Genexus

162

subcategorías. Pero simultáneamente, podemos querer categorizar el mismo universo, de acuerdo a otros criterios. Lo que quiero decir: puedes incluir un mismo objeto en varias categorías distintas. Por ejemplo, suponte que quieres organizar además por sistema, subsistema, etc. Nada impide que un objeto esté en la categoría correspondiente a un subsistema, y además en la “Under Review” de la “Status”. O puedes categorizar los objetos según si se utilizarán en el Back-end de la aplicación, o en el Front-end. Las categorías nos permiten organizar y visualizar la información de nuestra KB de acuerdo con nuestras necesidades puntuales.

- Multicategorizar…–. A ella le brillaban los ojos. Al menos eso le pareció a él.

- Cuando seleccionas un objeto del Folder View, debajo de la ventana de propiedades, tienes un contenedor rotulado Categories que comienza con una pequeña toolbar en donde puedes tanto agregarle categorías al objeto como eliminárselas. Pero, como siempre, tienes varios caminos para llegar a Roma…

- Hoy estuviste categorizando… eso significa que tuviste que ir uno por uno a todos los objetos y asignarles las categorías correspondientes, ¿no? ¡Mucho trabajo!

- Sí –contestó un poco avergonzado-. Es que en esta versión aparecieron las categorías, y no me he acostumbrado a usarlas, si bien fue decisión de la empresa el uso de categorías de estado. A veces es difícil cambiar las costumbres. Tendríamos que haber creado estas categorías desde el vamos, y los objetos ya con la categoría “Under Construction” y luego ir moviéndolos de categoría, en el cambio de estado. Hoy tuve que poner la casa en orden, ya eran muchos los objetos de la KB. Pero sólo tuve que hacerlo para

Page 163: Libro Genexus

163

las subcategorías de “Status” porque la categoría “To-Do Diego” es dinámica, se mantiene sola.

- ¿Sola? ¿Cómo es eso? –. Mary estiró el brazo hasta la botella de vino. - En este caso, aprovechando que tenía que poner la casa en orden, puse

comentarios que contenían el texto “To-Do Diego” en algunos objetos que tenía pendientes de cerrar; también otros compañeros lo habían hecho. Luego creé una categoría dinámica. Las categorías dinámicas tienen asociada una condición de búsqueda (que podrá ser por palabras o por propiedades, como ya te mostraré). Todos los objetos resultantes de la búsqueda, pasan a formar parte de la categoría. Puse en la condición de búsqueda que tuviera el texto “To-Do Diego” y allí aparecieron todas las cosas que tengo por hacer, varias por cierto…

Veamos cómo lo hizo…

El que busca encuentra

GeneXus X tiene la capacidad de encontrar “cualquier cosa” que se halle en

la KB. Esta facilidad se presenta de la misma forma que experimentamos cuando hacemos uso de los motores de búsqueda en la Web. Basado en complejos algoritmos, el motor full-text search de GeneXus X localiza lo buscado entre lo que podrían ser toneladas de palabras contenidas en los objetos que componen la KB. Y al igual que la mayoría de los buscadores, nos permite utilizar sintaxis intuitivas para acotar las búsquedas. En la siguiente imagen puede ver la búsqueda por “To-Do Diego”…

Page 164: Libro Genexus

164

El motor full-text search exploró la KB y reportó todos los lugares donde estaba presente. Obsérvese además cómo desde esta consulta puede crearse la categoría dinámica de igual nombre, “To-Do Diego”, que Diego mostró antes a Mary. Para ello alcanza con hacer [Save as Category]. Tal vez sea lo que hizo Diego ese día…

Note que debajo se propone también una búsqueda, pero en este caso sólo de propiedades. Por ejemplo, si se quisieran ubicar todos los objetos de tipo procedimiento, agregaríamos una propiedad:

Page 165: Libro Genexus

165

Base, rimel, sombras… Maquillando la aplicación

Esa misma tarde, unas cuantas horas antes, Julia terminaba la documentación en la KB cuando Mike, que finalizaba los últimos testings, comentó:

- Me enteré por Diego que quieren cambiar el estilo de las páginas. Él estaba muy atareado, terminando el desarrollo de unos objetos, y me derivó este tema a mí. Me dijo que hablara contigo para pedirte los detalles.

- Sí, me enviaron un e-mail que describe más o menos el aspecto que desean y lo copié como Talk del Main Document; luego lo documento bien. Dejaron a nuestro gusto la composición de colores y demás. Yo ya había ingresado hace tiempo el logo de ellos a la KB, en el nodo Images. Quieren que quede situado arriba, en todas las páginas.

- Eso lo hago ahora mismo. Sólo tengo que abrir la master page AppMasterPage creada con la KB y utilizada por defecto por todos los objetos. Es donde se encuentra centralizado el encabezado y pie que tendrán todas las páginas del sitio; luego sustituyo el encabezado “Application Header” que sale por defecto, y listo. Para todo lo otro que piden, llamemos a Paul, el diseñador gráfico…

- Pero Paul no sabe nada de GeneXus, ¡¿y tú le vas a permitir que abra los objetos y modifique los controles en los forms, cambiándoles el color, el tamaño, y demás?!

Mike no pudo reprimir la carcajada. - No Julia, cada uno en lo suyo. ¡Ni loco le dejo tocar nuestros objetos!

Pero necesito que configure para cada control de los forms, su diseño. Por ejemplo, necesito que especifique para los botones de las transacciones su tamaño, color de fondo, tipo de letra, etc. Lo mismo para los grids y demás controles. Quisiera centralizar eso y que él no tenga que tocar los controles “en” los objetos. Para eso es que existe el objeto de tipo Theme. Observa que bajo el nodo Themes bajo el Customer del Folder View, aparece uno de nombre GeneXusX. Es el que asoció GeneXus por defecto a nuestros objetos. Si lo

Page 166: Libro Genexus

166

abro, verás que se trata de una lista, en árbol, de clases de controles (aunque no sólo).

- Cada tipo de control tiene varias clases posibles. Dentro de la clase Button hay una subclase que es BtnDelete, que por defecto hereda sus propiedades pero que te permite cambiarlas. Si ahora vamos a cualquier transacción y editamos el form, podremos ver en las propiedades del botón [Delete] que tiene asociada esta clase. Puedes cambiársela por otra. Con esto te queda perfectamente separado en tu aplicación el comportamiento del diseño. Te digo más: ahora lo que haré será exportar48 de nuestra KB el theme GeneXusX, enviárselo a Paul para que él configure los aspectos de las clases y me lo envíe nuevamente, momento en que

48 No habíamos hablado hasta el momento de exportación/importación en GeneXus. En este libro solamente diremos que se pueden exportar objetos GeneXus y otro tipo de conocimiento de una KB en archivos de extensión xpz, que luego pueden ser importados en otra o la misma KB. Para ello está la opción del menú Knowledge Manager.

Aquí se configuran los aspectos de diseño para esta clase.

Page 167: Libro Genexus

167

lo importaré y ¡listo! ¡Diseño aplicado! Paul no necesita tener GeneXus, sino solamente el Theme Editor, un utilitario que sólo permite trabajar con estos objetos. Es específico para el diseñador gráfico. Si no, también lo puedes hacer desde dentro de GeneXus.

“De hecho, ahora que veo, el logo de la empresa está en la línea de los naranjas. Así que mejor utilicemos el Theme de nombre Orange (para ello lo marcamos en el check box) y hagámosle un Save as… TravelAgencyTheme. Así usamos por entero uno propio. Enviémosle este a Paul. Para probar y mostrarte, mira, modifiquemos los colores de algunas clases del theme –tras lo cual, con el nuevo theme TravelAgencyTheme abierto, se puso a cambiar las propiedades de las clases Grid, FreestyleGrid y Table, y grabó-. Ahora lo que tengo que hacer es decirle a la KB49 que utilice en todos los objetos ese tema:”

Y habiendo cambiado también el web panel AppMasterPage, tras pedirle a

GeneXus que reconstruya todos los programas (Build/Rebuild All) para que

49 Estrictamente hablando a la versión de la KB que esté activa. Sobre versionado de KB, busque próximos episodios o en las distintas fuentes de documentación de GeneXus.

Page 168: Libro Genexus

168

tome en cuenta que los objetos tendrán ahora este theme, observe cómo luce en ejecución. Por ejemplo, veamos el Work With Airline, y luego la transacción para modificar una aerolínea:

Espejito, espejito…dime cuándo y te diré cómo eras

Otra interesante posibilidad que ofrece GeneXus X es la de poder consultar

la evolución de los objetos dentro de la KB a través del tiempo. Es decir, cada objeto tendrá asociada una historia que estará registrada con su fecha y hora y el usuario que hizo las modificaciones. Con esto se podrá seguir la evolución de un objeto desde su inicio mismo.

Y lo que es más: usted podrá volver a una versión anterior del objeto, seleccionando esa versión como activa, a través de botón derecho.

En la siguiente imagen…

Los tipos de objeto Master Page y Theme son los que más importan a la hora de diseñar la estética de la aplicación.

Page 169: Libro Genexus

169

…se observa, a la izquierda, el contenido del panel Latest Changes View y a la derecha la historia de la transacción Airline. Note que existieron seis modificaciones desde su fecha de creación, por lo tanto hay siete números de versión.

También podrá comparar dos versiones entre sí y obtener sus diferencias. Por ejemplo, puede seleccionar dos versiones y luego, mediante botón derecho del mouse, pulsar sobre “Compare Selected Revisions”. Como resultado de comparar las revisiones 12 versus 14 de la transacción Country, GeneXus mostrará las diferencias en dos ventanas adyacentes, como se muestra a continuación para el ejemplo (en la versión 12 no habían reglas definidas).

Page 170: Libro Genexus

170

¿Versionado de la KB? El tiempo hacía lo único que sabe hacer; Mary y Diego parecían no percatarse de ese avance inexorable.

- Diego… - ¿Sí? - ¿No crees que llegó el momento de… –se alisó la falda con las manos,

y carraspeó antes de continuar, segundos eternos para él-…hacer una copia de la KB?

- ¡Ah, copias de respaldo! Bueno, en realidad en GeneXus lo puedes hacer muy muy muy fácilmente. De hecho, dentro de la misma KB puedes contener “fotografías” de diversos estados de la misma en el tiempo. Y no solo eso, puedes hacer desarrollos paralelos de distintas versiones. Por ejemplo, quieres poner en producción una versión, y continuar desarrollando nuevos requerimientos, de tal manera que puedes ir luego haciendo correcciones a la versión de producción a medida que el usuario lo va requiriendo, que no estorben el desarrollo principal.

- Interesante, ¿me lo cuentas luego? –. Se levantó del sofá y parada delante de él le ofreció extendida la palma de su mano-. ¿Vienes?

Page 171: Libro Genexus

171

Situaciones que se repiten: para no decir siempre lo mismo, Data Selectors

Es común encontrarnos codificando las mismas “situaciones” (filtros y órdenes de acceso a la base de datos) en distintos lugares de nuestra aplicación donde recuperamos datos, desperdigando por variados lugares la misma lógica (o parte de ella).

Observando los siguientes trozos de código:

…y comparando entre ellos, es evidente que obtienen resultados distintos, aunque con un mismo trozo de lógica: ambos quieren filtrar por ciudades africanas (CountryWorldRegion=WorldRegion.Africa), ordenándolas por nombre. El primero despliega en la salida exactamente eso (supongamos que City_Block contiene atributos de ciudades), y el segundo las atracciones de categoría ‘Safaris’ de ciudades africanas. Obsérvese el bloque de código en común, compuesto por un orden y un filtro. Podría haber decenas de objetos GeneXus donde se necesitaran las ciudades africanas ordenadas por nombre.

Pero tiene más ventajas. Es muy común que la gente incorporada al staff de

desarrollo en un momento dado desconozca los criterios o filtros comunes de

Un Data Selector permite la centralización y el reuso de navegaciones, evitando la duplicación de código.

Page 172: Libro Genexus

172

una aplicación. Suponiendo que se quiere promocionar fuertemente el turismo africano, suele pasar, por ejemplo, que se le pida un listado de ciudades y salgan todas las ciudades en lugar de sólo las africanas. La centralización de estos criterios reduce enormemente la curva de aprendizaje haciendo productivo al personal rápidamente y reduciendo el número de errores.

En el ejemplo anterior, si le diéramos un nombre a esas cláusulas comunes como si fuera una macro que luego pudiera expandirse… ¡obtendríamos un Data Selector! Un nombre para un criterio de búsqueda de datos. En nuestro caso: AfricanCities.

Los procedimientos anteriores nos quedarán:

Page 173: Libro Genexus

173

A la derecha del comando For each se ha escrito la cláusula Using la cual dice que “se utilice el contenido” del Data Selector AfricanCities; y eso es todo… si luego modificamos el criterio, lo hacemos dentro del objeto Data Selector y ¡se cambia en todos lados automáticamente! El camino de los procesos

Los humanos somos buenos para muchas cosas, pero en tareas como buscar un documento entre cientos de otros, tener presente sus vencimientos, y asegurarse de que el trabajo terminado pase de un lugar a otro respetando una secuencia previamente definida... hmmm ¿lo somos?

GXflow, conocida herramienta de Artech que se relaciona con la automatización de los procesos en las empresas, determinando flujos de tareas, está también integrado a GeneXus X. ¿Por qué? Las aplicaciones cada vez más contienen flujos de procesos, que

requieren de componentes de Workflow. En efecto, usted podrá crear sus propios diagramas dentro del IDE de

GeneXus, simplificando mucho las tareas de integración y haciendo sencilla la vinculación de las actividades con los objetos de la KB.

En la imagen se ha diseñado un sencillo ejemplo que muestra el flujo del proceso de reserva de un vuelo.

Page 174: Libro Genexus

174

La reserva en sí misma, expresada por el primer bloque del esquema, no es ni más ni menos que la puesta en ejecución de la transacción a la que representa.

Luego se evalúa determinada condición, como podría ser que los datos están correctos y completos. A los efectos de exponer un ejemplo, vea las siguientes condiciones: Print Tickets IF FlightStatus = ‘AUTHORIZED’ Get in contact with Client IF FlightStatus = ‘CORRECT PERSONAL INFORMATION’

En caso de que así sea, se imprime el ticket de reserva y se envía a su destinatario. De lo contrario, el flujo se deriva hacia la puesta en contacto con el cliente, se actualiza la información (seguramente mediante la transacción correspondiente a los clientes), y se vuelve a retornar el flujo hacia la reserva, reiniciándose el ciclo. Back y Front End - Intra e Inter Net

En este libro nos hemos permitido inmiscuir en algunos momentos de la vida de nuestros personajes, lo que nos permitió observar cómo se han implementado algunas partes de la aplicación. El objetivo: ver un poco de cada cosa. Pero no hemos visto el todo. Por momentos los autores sentimos un poco de vergüenza de nuestro voyeurismo y quitamos la mirada, para luego, tentados, volver nuestros ojos a la historia. Por esta razón, hemos visto entremezclados objetos que serán utilizados en el back-end de la aplicación (por ejemplo los Work With, dado que la actualización de la información de

Estas condiciones son expresadas mediante un editor de condiciones. La idea es que se puedan expresar reglas, donde primero se indica el nombre de la tarea sucesora e inmediatamente la condición que se tiene que cumplir para que siga por ese camino. Sencillo, práctico y potente.

Page 175: Libro Genexus

175

aerolíneas, países, ciudades, vuelos, pasajeros, etc., sólo podrá ser efectuado por empleados de la empresa, con permisos, a través de la intranet), con otros que pueden utilizarse en el front-end, es decir, en el sitio Web que será consultado por personas de todo el mundo vía Internet.

Sin embargo, Diego, Mike y Julia han trajinado mucho más de lo que hemos podido apreciar. Han hecho la página principal de la Intranet, modificando un poco la página Home creada por el pattern Work With. Han agregado una sección de login a la master page… web panels con consultas variadas y distintos menús.

Esa misma tarde, sin ir más lejos, antes de la cita con Mary, mientras Paul recibía un theme para modificar, Julia se contactaba con los usuarios de Travel Agency y Mike terminaba los testeos. Diego observaba detenidamente cómo había quedado la página principal del sitio web de la empresa:

Page 176: Libro Genexus

176

Obsérvese que se brinda información de hoteles, de rentadoras de autos y del estado del clima. Estas son aplicaciones de terceros, publicadas como Web Services que se pueden integrar (consumir) fácilmente dentro de GeneXus, con el nuevo tipo de objeto External Object. A esta altura el lector tiene todos los elementos para hacer la radiografía de la página y sospechar qué puede haber detrás de la misma: un web panel, web components, master page, theme, imágenes, contoles…

Final… ¿o principio?

Hasta aquí hemos obtenido distintas piezas de un rompecabezas que usted

irá armando, valiéndose de la más importante: su imaginación. Con ella, las piezas faltantes se le irán descubriendo en una búsqueda

continua. Inacabable. El puzzle está siempre próximo a… pero nunca se completa. Allí radica su secreto: mantenernos eternamente en vilo, buscando, pensando, imaginando, cambiando.

“Amigos, con fe, con alegría, con generosidad, con la sinergia de esta Comunidad, vamos adelante, y preservemos la magia.”

Breogán Gonda en el cierre del XVI Encuentro Internacional GeneXus 20 de Setiembre de 2006

¿Qué pasó con nuestros personajes? ¿Fue exitoso el proyecto de desarrollo?

¿Cómo terminó esa cena con música? ¿Se habrá convencido Mary de ingresar a la Comunidad? Piezas que faltan, y que cada uno encontrará… como los secretos que aún gesta la X…

…¿O es que esta historia recién empieza?

Page 177: Libro Genexus

177