Etiqueta

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)
 

No hay comentarios: