24 septiembre 2014

Uso de variables vinculadas (Bind) en sentencias SQL

Al ejecutarse (por defecto) cualquiera de los métodos disponibles para una instancia de tipo "Table", como por ejemplo $select() o $insert(), Omnis genera dinámicamente la sentencia SQL correspondiente, pero antes de su ejecución, compone (proceso conocido como “tokenize”) todos y cada uno de los elementos (variables, valores, etc) que conformaran la sentencia SQL que enviará al servidor.

Particularmente, esto significa que Omnis deberá localizar las referencias a las variables "bind" que hayamos utilizado, es decir las usadas mediante la notación @[variable]. Cuando Omnis "tokenize" la sentencia, tendrá en cuenta el ámbito en el que espera encontrar las variables referenciadas. En el caso de una instancia de tipo "Table", el ámbito de aplicación será, el de la propia instancia.

Llamamos "ámbito de aplicación" al medio en que podremos encontrar las variables declaradas, según sean "Task" (Nivel de Tarea), "Class" (Nivel de clase), "Local" (Nivel local), "Parameter" (Equivalente al nivel local) o "Hash" (Globales).

En el caso de que se trate de una lista creada en la instancia "Table" y definida mediante una clase "schema" o "query", se permitirá el uso de los siguientes ámbitos:

  • @[#...] - Una variable "hash", como por ejemplo @[#S1], o @[#10]. Con la excepción de #D, #T, #CT que son técnicamente "macro-hash" (evaluadas en el momento de ser referenciadas) y no pueden ser evaluadas durante el proceso "tokenize". Si necesitamos hacer uso de la fecha actual, podríamos optar por lo siguiente: Calculate lvCurrentDate as #D, para después, usar @[lCurrentDate].
  • @[taskvar] - una variable de tarea, siempre y cuando la tarea que ejecuta y define la lista sean la misma.
  • @[$cinst.column] - cuando se trata de una "row" definida con una clase SQL, "column", sería una de las columnas de la "row".
  • @[$cinst.N.column] - cuando se trata de una lista definida con una clase SQL, "column", sería una de las columnas de la lista, "N" sería un número de línea literal. Por ejemplo, @[$cinst.5.column] haría referencia al valor de "column" en la línea 5. No está permitido el uso de otras notaciones entre corchetes, dentro de éstas, es decir, la referencia @[$cinst.[lvRowNo].column] no es admitida como variable "bind". Una solución a esto, podría ser, hacer uso del comando Calculate $cinst.$line as lvRowNo, para después, usar @[$cinst.column].

A continuación mostramos un ejemplo sobre como hacer uso de una variable "bind" al obtener datos, en correspondencia con los almacenados en la columna "Col3" de una lista:

Begin Statement
   Sta: Select * from Table
   Sta: Where Col = @[iList.Col3]
End Statement

Do lStatementObj.$prepare() Returns lStatus
For iList.$line from 1 to iList.$linecount
   Do lStatementObj.$execute() return lStatus
   Do lStatementObj.$fetch(iRow)
   .....
End for

En el caso de que se trate de una lista creada en la instancia "Table" y definida mediante una clase "schema" o "query", también podría utilizar:

  • @[$cinst.iVar] donde "iVar" sería una variable de instancia en la clase "Table" y que podría haber sido configurada mediante la ejecución previa de un método, como por ejemplo MyList.$setbind('valor') antes de invocar al MyList.$select(), MyList.$insert(), etc

En el ejemplo siguiente, se invoca al método $setbind que contendría a su vez el parámetro "pVar" y un sola línea "Calculate iVar as pVar". Luego, desde fuera de la clase "Table" tendríamos lo siguiente:

Calculate lFruit as 'Oranges'
Do lList.$definefromsqlclass('MyTable')
Do lList.$setbind(lFruit)
Do lList.$sessionobject.$assign(iSessionObj)
Do lList.$select('WHERE Fruit=@[$cinst.iVar]')
Do lList.$fetch(kFetchAll)

La formula descrita en éste artículo nos permite el uso de variables que de otra manera no estarían a nuestro alcance desde la clase "Table".

17 septiembre 2014

Uso de "Item reference"

En éste artículo hablaremos un poco sobre las variables de tipo "Item reference", que no deben ser (en ningún caso) confundidas con las de tipo "Object reference" de las que ya hemos hablado en un anterior artículo, las variables "Item reference" puede referenciar a un punto concreto dentro del árbol de objetos y han sido creadas para contener cadenas de notación largas, tales como:

Set reference myRef to $root.$iwindows.wTest.$objs.Tabpane.$objs.Button
De éste modo, puedo simplificar la notación con la que hago referencia a las propiedades de un objeto, como (por ejemplo) para el caso expuesto mediante el comando de asignación anterior:

Do myRef.$text.$assign('Mi botón')

En el capítulo 3 del manual de Programación Omnis en castellano, podrá encontrar una descripción de cómo utilizar este tipo de variables, así como del comando utilizado para la asignación de su posibles valores, el comando "Set Reference".

 

Eliminación de un "Item reference"


Por supuesto las variables del tipo "Item reference" pueden ser re-asignadas, es decir modificar o alterar su contenido, mediante la instrucción:

Set reference myRef to $root.$iwindows.wTest.$objs.Tabpane.$objs.Button2

"myRef" ahora estaría apuntando a un botón diferente.

 

Pero, ¿cómo se elimina un "Item reference"?


El comando...

Calculate myRef as #NULL

...produciría un error en la notación resultante y...

Set reference myRef to #NULL

...generaría una referencia válida, pero, hacia la constante #NULL.

Así que la respuesta es:

Set reference myRef to

(Sin indicar notación alguna) lo cual creará una referencia vacía, pero sin errores.

 

Verificación de "Item reference"


Con frecuencia será necesario comprobar, si la notación contenida en una variable "Item reference" sigue siendo válida. En la mayoría de los casos bastará con utilizar el siguiente código:

If myRef
     OK message Comprobación de referencias {La referencia es correcta}
Else
     OK message Comprobación de referencias {La referencia no es correcta}
End If

La instrucción "If" no precisa la inclusión de cálculo alguno (excepto para el caso expuesto más abajo); "If isnull(myRef)" o "If not(myRef)" no siempre produce los resultados esperados. Si la cuestión es tratar el caso de que la referencia no es correcta, es mejor usar la condición "Else" y programar en ese punto lo que proceda.

En ocasiones, puede ser que lo que deseemos sea crear una referencia a una variable. En este caso, deberemos agregar la notación ".$ref' en el final del nombre de la variable y en el momento de su construcción. De modo que, el código para crear una referencia a una variable sería:

Set reference myRef to ivString.$ref

Pero en estos casos, se revela un pequeño problema:

En el caso de la consulta mostrada anteriormente "If myRef" no funcionará cuando se trata de referencias a variables, es decir, siempre deriva hacia la rama del "Else", aún cuando "myRef" esté apuntando hacia una variable totalmente correcta. De modo que para comprobar la validez de éste tipo de referencias, será necesario usar lo siguiente:

If myRef.$ident

Esto devolverá kTrue si la variable "ivString" existe realmente, incluso aunque contenga el valor #NULL. Por cierto, la consulta "If myRef.$ident" también funciona cuando se está haciendo referencia a una instancia.

Lamentablemente no es posible utilizar éste método de comprobación para ver si la referencia a un grupo de objetos se ha establecido correctamente. Por ejemplo, si se ha definido previamente myRef con:

Set reference myRef to $clib.$classes

En el caso de "If myRef.$ident" siempre derivará hacia la rama "Else". No obstante y para estos casos, la consulta "If myRef" funcionará sin ningún problema. Por lo tanto, si deseamos aunar ambas posibilidades (es decir referencias a variables y a otras notaciones) deberíamos realizar la consulta del siguiente modo:

If myRef | myRef.$ident

Sin embargo, normalmente conoceremos de antemano si la referencia en cuestión apunta hacia una instancia, hacia una variable o hacia un grupo.

10 septiembre 2014

Uso de las variables "Object Reference"

Un problema


A medida que nos hemos ido familiarizando con el uso de objetos y especialmente cuando los hacemos disponibles por medio de variables de instancia, empezamos a tropezar con situaciones en las que nos gustaría acceder a un objeto (variable) desde fuera de su ámbito normal. Por ejemplo, podríamos querer pasar un objeto que contiene datos importantes desde una instancia-ventana a una instancia-informe o incluso a otra instancia-ventana diferente. Pero si pasamos la variable de objeto como parámetro, obtendremos una copia nueva e independiente de ese mismo objeto y puesto que se trata de un parámetro, solo alcanzable desde un ambito local. Si posteriormente copiamos el objeto sobre una nueva variable de instancia, con el fin de hacerlo disponible en ese ambito, estaremos duplicando de nuevo el objeto original, al final podría resultar que terminásemos con un sin fin de copias del mismo objeto.

Naturalmente, cualquier cambio que posteriormente realicemos sobre estas copias no será reflejado en el objeto original. Podemos decir que las copias pasan a tener vida propia tan pronto como son creadas. Por supuesto podemos compartir un objeto común mediante (por ejemplo) el uso de una variable de clase, ya que estaría disponible para todas las instancias generadas a partir de esa misma clase, por ejemplo, entre las varias instancias de la misma clase-ventana. Pero en ese caso ya no tendríamos el objeto a nivel de instancia, sino que afectaría a todas las diferentes instancias de la clase.

Una solución


Las variables "Object Reference" nos permiten crear un puntero hacia un objeto, eliminando la necesidad de realizar una nueva copia del mismo. Esto resulta en un uso más eficiente de los recursos de memoria RAM en nuestra aplicación y al mismo tiempo dotamos de una mayor coherencia a las acciones requeridas para con ese Objeto. Como veremos, también disponemos de algunas "utilidades" con las que realizar un seguimiento de las referencias o punteros en uso.

Pero no podemos simplemente crear referencias para las variables-objeto que tengamos creadas. Debemos tener en cuenta, que las variables-objeto son en realidad contenedores de los objetos instanciados y las "Object Reference" generan punteros hacia dichas instancias por lo que sólo podemos usarlos con instancias de objetos que han sido generadas de un modo algo diferente al habitual. Puesto que no se ha acuñado término alguno para éste tipo de instancias, las llamaremos "referenciables" o también "instancias refrenciadas".

 

Creación de un "Object Reference"


Ya sabemos que podemos generar dinámicamente instancias de un objeto mediante su método $new(), el valor devuelto por éste, es la instancia propiamente dicha. Podemos hacer esto mediante el comando "Do" o mediante "Calculate", tal como se observa en el siguiente ejemplo:

Do $objects.demoObject.$new(5) Returns demoObj
Calculate demoObj as $objects.demoObject.$new(5)

El "5" en estos ejemplos es un valor enviado como parámetro al método $construct de la instancia y que (como sabemos) es ejecutado automáticamente en el momento de su creación.

Por supuesto ésta no es la única forma de generar una instancia, (por ejemplo) podríamos hacerlo mediante definir una variable objeto (en el panel de definición de variables) y asignarle un valor como sub-tipo desde una clase en nuestra librería u otras opciones similares, pero en éste artículo optamos por la creación dinámica de instancias, a fin de que pueda observarse con mayor facilidad los elementos que son objeto de discusión.

Esencialmente, las variables "Object Reference" son asignadas bajo el mismo criterio o modo que las variables "Object". De hecho, desde la versión 4.0, existe en Omnis Studio un método creado específicamente con este propósito. El método $newref(), su uso:

Do $objects.demoObject.$newref(5) Returns objref
Calculate objref as $objects.demoObject.$new(5)

Al ejecutarse, este método se crean ambas cosas, es decir, un objeto y una referencia, se crea una nueva instancia de ese objeto y un valor de referencia que apunta hacia esa misma instancia. La instancia es creada directamente a partir de la clase, Omnis añadirá un carácter de subrayado y un número entero al componer el nombre de la instancia con el fin de que sea único y al mismo tiempo guarda un puntero hacia esa misma instancia, en la variable de tipo "Object Reference" especificada.

Ambos tipos de variables (Object y Object Reference) son iguales desde el punto de vista operacional, pero con una sutil y gran diferencia...

Cuando hacemos uso de "Object reference" para crear instancias, el Objeto "referenciable" (término con el que hemos bautizado a las instancias referenciadas), es decir la instancia continuará existiendo aún después de que su puntero o variable "Object reference" haya sido destruida. De hecho, perdemos contacto con dicha instancia (excepto si es a través del grupo $inst) a menos que nos aseguremos de tenerlo a buen recaudo.

Paso de "Object References"


Si queremos pasar un valor "Object reference" como parámetro, tendremos simplemente que especificar el nombre de la variable de tipo "Object reference" (que contiene el valor) como parámetro durante la llamada al método, igual que lo haríamos con cualquier otro. La variable-parámetro en el método de recepción, también deberá ser de tipo "Object reference". En su ejecución, dicha variable-parámetro constituirá otro punto de acceso a la misma instancia.

Si lo deseamos, también podemos duplicar la referencia sobre otra variable mediante el comando "Calculate", el método "$assign()" o bien mediante asignar su valor de retorno, a continuación mostramos los tres posibles modos:

Calculate dataObjRef2 as dataObjRef1
Do dataObjRef1 Returns dataObjRef2
Do dataObjRef2.$assign(dataObjRef1)

Note que (en éste caso) no podemos hacer uso del comando "Set reference", ya que sólo funciona con variables de tipo "Item reference" y evidentemente las "Object reference" no son "Item reference". La transmisión de valores deberá hacerse entre variables del mismo tipo.

Dado que diferentes celdillas de una variable de tipo lista, también se comportan como variables en si mismas, podemos hacer uso de las mismas técnicas para la asignación de valores, así un "Object reference", puede ser guardado en una celdilla. Por ejemplo, si se definió "listVar" para incluir una columna de tipo "Object reference" con el nombre "dataObjRef2" y queremos copiar la referencia de "dataObjRef1" sobre la celda de la línea 3, usaríamos:

Calculate listVar.3.dataObjRef2 as dataObjRef1

Tanto "dataObjRef1", como la celda en la lista, apuntan ahora a la misma instancia.

Un mayor poder, conlleva una mayor responsabilidad


Si usamos esta herramienta, deberemos también asumir la responsabilidad de mantener aseados nuestros propios líos (La versión 6.1 de Omnis permitirá otorgar cierto automatismo a ésta tarea). En su uso habitual con una variable de tipo "Object", la instancia correspondiente es destruida automáticamente tras eliminarse su variable, lo cual ocurre cuando el elemento (método, instancia o tarea) que lo la contiene se cierra, en estos casos, Omnis Studio gestiona esta tarea de limpieza por nosotros.

Pero cuando usamos una variable "Object reference" y luego la destruimos, la instancia del objeto referenciado al que señalaba continúa existiendo. Sólo se destruye su referencia. La instancia del objeto sigue viva en algún lugar de la memoria RAM, ya que podrían existir otras variables de tipo "Object reference" que aún la necesiten. Nos corresponde a nosotros escribir el código necesario para destruir la instancia y mantener limpio el espacio RAM. El único momento en que la instancia de un objeto referenciado es destruido automáticamente, es cuando a su vez la tarea que lo contiene es destruida o bien al salir de Omnis Studio.

Entonces, ¿cómo eliminar las instancias "huérfanas"? Podremos hacerlo mediante el método $deleteref(). Este método nos permitirá eliminar la instancia del objeto al que apunta la variable "Object reference" indicada. Su ejecución causará la llamada del método $destruct de la instancia, como parte del proceso. Exactamente del mismo modo que sucede con cualquier otra instancia.

No podemos aplicar el método $deleteref() directamente sobre la instancia, pero si disponemos de un modo para localizar las instancias-referenciadas, se trata del método $listrefs(). (Se ha de tener en cuenta que mediante este método obtendremos todas instancias-referenciadas, sea que estén "huérfanas" o no) Podremos usarlo en cualquier lugar de nuestra aplicación. Se nos devolverá una lista de todas las instancias-referenciadas, según el nivel del árbol de notación en que lo apliquemos, si es $root, será a nivel global, es decir todas las existentes en nuestra aplicación.

La lista constará de una sola columna con los valores "Object reference", sobre las que se podrá aplicar el método $deleteref(), para hacerlo sobre todo el conjunto, usaríamos el método $sendall(). El Manual de programación Omnis en castellano, proporciona algunos ejemplos útiles sobre su uso.

Si lo que quiséramos fuera hacer una copia, disponemos del método $copyref(). En éste caso, el valor de retorno del método será un nuevo "Object reference", al igual que ocurría al aplicar el método $newref() y al igual que éste, $copyref() generará una nueva instancia del objeto referenciado, se trata (por tanto) de un duplicado exacto (a excepción de su nombre, por supuesto) de la instancia a la que apunta las variable "Object reference" original.

En el ejemplo siguiente usamos "dataObjRef1" para crear un duplicado de la instancia a la que hace referencia, guardando en "dataObjRef2" un puntero hacia la recién creada:

Calculate dataObjRef2 as dataObjRef1.$copyref()

También podemos utilizar el comando "Do", con o sin el método "$assign()", para realizar la misma tarea. Se ha de tener en cuenta que las instancias creadas son completamente independientes entre sí, pero estará bien, si eso es lo que queremos. El posterior uso de "$listrefs()" sobre la clase de objeto original, mostraría (al menos) dos instancias referenciadas en la lista.

Pero ¿qué pasa si por error destruimos una instancia, cuando resulta que aún existía una variable "Object reference" apuntando hacia ella? ¿Cómo podemos saber si el "Object reference" que deseamos utilizar sigue siendo válido? No nos alarmemos, el método "$validref()", acude en nuestra ayuda, nos permite probar si la referencia es aún utilizable. Este método devuelve un valor booleano, si es kTrue significará que la referencia es válida.

Aunque bien podría considerarse una exageración utilizar este método antes de realizar cualquier acción que implique el uso de una variable "Object reference", habrá ocasiones en las que sea razonable realizar una comprobación de este tipo. Considere (por ejemplo) el caso en que haya recibido la referencia desde una fuente externa, si es así, es muy posible que deseemos comprobar si aún sigue siendo válida, supongamos que hemos programado en una ventana la ejecución del método "$deleteref()" en su "$destruct", cuando es el caso que existen otras ventanas aún abiertas que requieren de su uso, ocurriría que la referencia pasada a esta segunda ventana no sería válida.

Llegados a éste punto, les propongo desde "Aula Omnis" un ejercicio sencillo para demostrar que una instancia referenciable puede continuar vinculada. Estos son los pasos:

  • En primer lugar, ejecutamos el método "$newref()" dentro de una tarea para generar una instancia referenciable y capturar su referencia en una variable de tipo "Object reference".
  • A continuación, pasamos el "Object reference" a otra tarea y la testamos ($validref) para asegurarnos de que nos estamos comunicando con la instancia original.
  • Luego destruimos la primera tarea, que a su vez destruirá la instancia referenciable que hemos generado desde dentro de ella.
  • Desde la tarea que nos queda, testemos de nuevo la variable "Object reference" con el método "$validref()". No será válida, lo que significa que la instancia original fue destruida.

Si lo desea también podría construir una lista de todas las instancias mediante "$listrefs()" antes y después de la destrucción de la tarea.

(basado de un articulo de David Swain)

05 septiembre 2014

Uso de $ref y $sendallref

Uso de $ref como sufijo


El uso de $ref como sufijo (es decir situado al final de la notación) es sin duda el modo más simple de uso. En éstos casos, $ref devuelve una referencia al elemento de notación al que se aplica. Puede resultar útil si (por ejemplo) lo que deseamos es pasar la referencia del elemento como parámetro durante la llamada a un método o para simplemente guardarla en una variable de tipo "Item reference" mediante el comando "Set reference". Naturalmente el uso de $ref no es estrictamente necesario para éste tipo de operaciones, pero hará que la notación se vea más completa y "correcta". De modo que aprovechamos la redacción de éste artículo para sugerirle que se habitue en su uso.

A modo de ejemplo, supongamos que queremos guardar en una variable de tipo "Item reference" la referencia a un campo de tipo lista, situado en la instancia actual de una ventana. Podríamos hacer lo siguiente:

Set reference listFieldRef to $cinst.$objs.listField.$ref

Usado de éste modo, $ref funciona como si se tratase de una propiedad del elemento al que se aplica. También actúa un poco a modo de método, ya que devuelve su referencia. De hecho, si prefiere pensar en ello como si de un método se tratase, puede hacer uso de $ref con paréntesis, (como lo haríamos con un método real) del modo siguiente:

notacion.$ref()

Podemos concluir que el uso de $ref como sufijo, se posiciona en algún lugar entre propiedad y método. Pero en éste artículo queremos centrarnos en otros modos de uso mucho más interesantes...

Uso de $ref como prefijo


Cuando usamos $ref como prefijo (es decir situado al principio de la notación) y a su vez dentro del parámetro en un método, $ref hará referencia al elemento en el que se aplica dicho método, es como un puntero reflexivo. Nótese la sutil diferencia entre éste modo y su uso como sufijo. En este caso, $ref usado como parámetro es aplicado a sí mismo. Si bien esto puede parecer un poco confuso al principio, es una característica ¡muy potente!

Para entenderlo mejor, pensemos en el método $assign(), que usamos habitualmente para asignar un valor a cualquier variable (incluidas las propiedades de los elementos). Podríamos hacer uso de $ref como parámetro del mismo, de éste modo actuará como referencia a la variable que precisamente va a recibir la asignación del valor. Por ejemplo, si tenemos una variable numérica llamada numberVar1 y deseamos incrementar su valor en 5, podríamos escribir...

Do numberVar1.$assign($ref+5)

...en lugar de...

Do numberVar1.$assign(numberVar1+5)

En ambos casos obtendremos el mismo resultado.

También podremos usar $ref de esta manera, dentro de las posibles funciones incluidas en el parámetro del método $assign(). Considere el caso en el que deseamos concatenar caracteres adicionales al contenido actual de una variable de tipo texto:

Do stringVar.$assign(con('Esta variable contenía "',$ref,'" antes de hacer clic en el botón.'))

Note como su inclusión en la función con() no interfiere en el uso de $ref.

Un ejemplo más. Supongamos que en el método perteneciente a un campo, queremos incrementar el valor de su variable asociada en 5 unidades. Queremos escribir el código de forma genérica para que podamos copiarlo y pegarlo en cualquier otro campo. La línea de código necesaria sería:

Do [$cfield.$dataname].$assign($ref+5)

Sea cual sea el nombre de la variable asociada, vera incrementado su valor por 5 unidades.

Pero aún existe otro modo más interesante de usar $ref...

Uso de $ref con métodos grupales


La notación $ref tiene un significado ligeramente diferente cuando es usado como prefijo de una notación, incluida a su vez dentro de un parámetro en un método grupal. Al usarse de éste modo $ref hará referencia al miembro actual del grupo, según cada una de sus iteraciones.

Supongamos que queremos construir una lista con las instancias de ventana abiertas. Queremos incluir en ella su nombre, su título y el nombre de cada instancia. Haremos uso del método $makelist() aplicándolo sobre el grupo $iwindows. Para obtener los valores de dichas propiedades sobre la lista resultante, haremos uso de $ref del modo siguiente:

$iwindows.$makelist($ref.$name,$ref.$title,$ref.$class().$name)

Nótese que debemos resolver (mediante el uso de los paréntesis) el $class del tercer parámetro. Si no lo hiciéramos, nos devolvería "$class".

En su ejecución, $ref ira referenciando a cada uno de los miembros del grupo $iwindows. Nosotros sólo tendremos que añadir a $ref la propiedad, el método o la variable que deseemos.

Llegados a éste punto, debemos tener en cuenta que no todos los métodos grupales pueden hacer uso de $ref, sino sólo aquellos usables de forma iterativa. Esto es debido a que algunos métodos de grupo, tales como $next(), ni siquiera admiten parámetros. Otros, como $remove(), requieren del uso de un puntero o referencia hacia un miembro específico del grupo y por tanto no puede ser aplicado de forma iterativa. Para cada método grupal, disponemos a su vez de cuatro métodos que se ajustan a estos criterios: $makelist(), $appendlist(), $insertlist() y $sendall(). Los tres primeros son simples variaciones sobre un mismo tema, por lo que en realidad sólo existen dos (el que permite obtener la lista de los miembros del grupo y el que permite el envío de mensajes a cada uno de ellos) mediante los cuales hacer uso de $ref.

Presupongo que la mayoría de los desarrolladores en Omnis Studio hacemos uso frecuente del método $sendall(). Es el método utilizado para enviar mensajes a los miembros de un grupo y que (como sabemos) dispone de dos parámetros, el primero conforma el propio mensaje y el segundo es una expresión de búsqueda que permite determinar el alcance del mensaje, es decir, permite determinar que miembros concretos del grupo podrán atender el mensaje. El segundo parámetro es opcional, si es omitido, todos los miembros atenderán el mensaje.

$ref puede ser usado con cualquiera de los parámetros. Observe el siguiente ejemplo:

Do $iwindows.$sendall($ref.$close(),$ref.$style=kTitle)

Aquí le estamos diciendo a todas las instancias de ventana que se cierren, pero con la condición de que se trate de ventanas de tipo "title". Hemos usado un método ($close()) y una propiedad ($style) con $ref. Por supuesto, cualquiera de estos parámetros podría contener construcciones más complejas. En éste artículo decubriremos algunas cuestiones a tener en cuanta cuando hacemos uso de $ref de modo aún más complejo...

Uso de $ref con métodos de listas


Doy por supuesto que usted ya conoce el uso de las variables de tipo lista, si es así, seguramente también sabrá que una variable de tipo lista también actúa como si se tratase de un método grupal o notación de grupo, donde las líneas contenidas serían los diferentes elementos del grupo. Sabiendo esto, se deduce que cualquier método aplicado a una variable de tipo lista, es realmente un método de grupo, al cual se le pueden aplicar métodos exclusivos de éste tipo de variables como $search() y $sort(), por tratarse en realidad de variables de tipo lista.

El primer parámetro del método para listas $search() contiene la especificación de búsqueda, es decir, la expresión utilizada para seleccionar las líneas de la lista. (por ahora haremos caso omiso de los otros cuatro parámetros de este método) $ref podrá ser usado para referenciar a la línea actual de esa lista, como parte del método que analiza las líneas de la lista en busca de las que cumplan con los criterios establecidos. Normalmente necesitaremos añadir a $ref el nombre de una columna de la lista, ya que muy probablemente queremos localizar líneas en base al contenido de sus celdas. Por ejemplo, si queremos seleccionar todas las líneas en las que el valor de la columna "quantitysold" sea mayor que 5, tendremos que utilizar:

Do listVar.$search($ref.quantitysold>5)

También pudiera suceder que deseáramos seleccionar las líneas en función de los valores contenidos en diferentes columnas de esa misma línea. Por ejemplo, si queremos seleccionar todas las líneas donde "quantityshipped" es inferior a "quantitysold", podríamos utilizar:

Do listVar.$search($ref.quantityshipped<$ref.quantitysold)

Esta comparación se va realizando línea a línea. El uso de $ref es seguramente la forma más sencilla de garantizar esto.

Con el método $sort() normalmente usaremos un par de parámetros. El primero para el criterio de clasificación y el segundo para el indicador boleano de dirección de ordenación (kTrue si es descendente, por defecto es kFalse - ascendente). Recordemos que también podemos hacer uso de $ref dentro de las funciones, por ejemplo:

Do listVar.$sort(upp($ref.lastname),kFalse,upp($ref.firstname),kFalse)

Por supuesto el método $sendall() también puede ser usado con una variable de tipo lista. En éstos casos, $ref se refiere a cada línea de la lista en su turno. Así que sólo podemos anexar propiedades, métodos y nombres de celdas, en correspondencia con una línea de la lista, es decir en el ambito de una línea simple. El primer parámetro de $sendall() contiene la expresión que será evaluada para cada línea. Este parámetro es necesario (ya que de no proporcionarse haría del método algo inútil). El segundo parámetro es el que contendrá los criterios de búsqueda, que deberán cumplir las líneas sobre las que actuará el primer parámetro, si se omitiese se aplicará a toda la lista. $ref puede utilizarse en  cualquiera de los parámetros.

Por ejemplo, si quisiéramos ejecutar un proceso incluido en la instancia de la ventana actual, pasándole como parámetros información obtenida de cada una de las líneas de la lista, según cumplan con los criterios establecidos, usaríamos lo siguiente:

Do listVar.$sendall($cinst.$processlineinfo($ref.C1),$ref.C2>20)

Este comando pasa el valor de la columna 1 al método $processlineinfo() ubicado en la instancia de la ventana, ejecutándose para cada línea cuyo valor en la columna 2 sea mayor de 20. Si bien se trata de un ejemplo sencillo, observe como $ref nos permitió pasar el valor como parámetro en la llamada al método incluido dentro del primer parámetro de $sendall(). Los parámetros para $sendall() pueden llegar a ser bastante complejos, en la medida que nos familiarizamos con su uso. De momento ya hemos aprendido a ejecutar métodos incluidos. El uso de $ref se complica un poco cuando lo usamos de forma anidada, es decir cuando incluimos métodos dentro de métodos.

Uso de $ref con métodos anidados


Si bien el uso de $ref con métodos anidados puede darse en otras estructuras, lo más común es que lo encontremos asociado al método $sendall() y por tanto, es bajo su uso que queremos llamar la atención al problema que resolvemos a continuación. Las expresiones dentro de los parámetros de $sendall() pueden realmente llegar a ser muy complejas, e implicar el uso de métodos anidados, de hasta un máximo de cinco niveles de profundidad. ¡Piense en uno o más métodos $sendall() anidados dentro de otros! Esto crea un pequeño problema (aunque no sin solución) con el uso de $ref en los niveles más profundos, sobre todo si lo que queremos en que se haga referencia al elemento más exterior y no al correspondiente a su nivel. Por definición, cualquier método que hace uso de $ref lo hace según el nivel en el que se encuentre. No obstante, decir que Omnis Studio no proporcionaba una alternativa para solventar éste problema, hasta la versión 4.2, hasta entonces no hubo otro remedio que escribir notaciones más específicas o simples.

Considere este caso: Queremos pasar el valor contenido en una determinada columna y para cada línea de una lista utilizando $sendall(). Para nuestro primer ejemplo, supongamos que simplemente queremos cambiar el valor de una columna booleana, haríamos lo siguiente:

Do listVar.$sendall($ref.booleanColumn.$assign(not($ref)))

Observe que en el primer uso de $ref hace referencia a la línea en curso, pero en su segundo uso (anidado dentro del método $assign) hace referencia a la celda específica sobre la que se va aplicando el método $assign en cada iteración. Esto causa que cambie el significado de $ref. En nuestro primer ejemplo, el cambio resulta beneficioso, ya que realmente queremos hacer uso del valor actual de la celda para simplemente asignarle un nuevo valor. Pero... ¿y si lo que quisiéramos fuera asignar un nuevo valor a la celda, en función de lo contenido en otras de la misma línea? Observe el siguiente ejemplo:

Do listVar.$sendall($ref.sumColumn.$assign($ref.columnA+$ref.columnB)) 

Esto definitivamente no va a funcionar. La notación no es válida, a menos que "sumColumn" fuese en realidad una lista, una row u otro objeto que contuviese a su vez las variables "ColumnA" y "ColumnB", pero definitivamente no es ese nuestro caso. Claro que podríamos solucionarlo utilizando el nombre de la variable de lista en lugar de $ref. Pero lo que queremos es algo que nos funcione en todos los casos, sin tener que modificar su sintaxis.

Entonces... ¿qué podemos hacer?

Afortunadamente $sendallref acude a nuestro rescate.

La razón, es que $sendallref permite mantener la referencia al elemento actual durante la ejecución del bucle $sendall(), incluso cuando está dentro de un parámetro. Así que ahora podemos reformular el problema anterior del siguiente modo:

Do listVar.$sendall($ref.sumColumn.$assign($sendallref.columnA+$sendallref.columnB))

De éste modo conseguimos referenciar correctamente a las columnas dentro de la línea objetivo del método $assign(). Ocurriría exactamente lo mismo en situaciones similares a esta, cuando estemos usando $sendall() con otras notaciones grupales.

Tener en cuanta que $sendallref no es el sustituto definitivo de $ref, podemos seguir usando éste último para los casos más sencillos donde si es posible su uso, pero realmente podemos utilizar uno u otro indistintamente, la única diferencia es que requiere el triple de pulsaciones de teclado para escribirse. Aunque (si se me permite una licencia) me parecería coherente usar $sendallref, siempre que se use con $sendall.

(artículo original de David Swain)