20 diciembre 2014

Aplicaciones para Android (requerimientos, instalación y configuración de Eclipse)

Requerimientos Android


Para construir y desplegar aplicaciones para Android, necesitará lo siguiente:

  • Windows, OS X o Linux
  • Eclipse con ADT Plugin
  • Android SDK
  • $0 - $25

En cuanto a los requisitos del sistema operativo, según los diferentes modos de uso, necesitará:

  • Para el modo “On-line”: Android 2.2+ (API 8)
  • Para el modo “Off-line”: Android 2.3+ (API 9)
  • Para soporte de bases de datos locales: Android 2.2+ (API 8)

Configuración del entorno Android


1) Instalar herramientas de desarrollo Android

El entorno de desarrollo Android comprende Eclipse, el ADT Plugin para Eclipse y el Android SDK.

Google ahora ofrece un paquete ADT, que contiene Eclipse, el plugin ADT y el SDK para Android, todo ya configurado y listo para su uso.

  • Descargue el paquete ADT desde: http://developer.android.com/sdk.
  • Se trata de un archivo zip, que deberá extraer y colocar en el lugar que desee.
  • A continuación lanzaremos la herramienta, mediante el ejecutable Eclipse que podremos encontrar bajo la carpeta de instalación ADT.
2) Importar el proyecto
  • Primero descargue la última versión del “wrapper” Android. desde la página web Omnis.
Para importar el proyecto Omnis, deberá abrir Eclipse y hacer lo siguiente:
  • Seleccione la opción “Import” del menú “File”.
  • Una vez abierto el asistente, seleccione “General ≥ Existing Projects Into Workspace”.
  • Desde la siguiente ventana escoja “select archive file” y el archivo “wrapper” zip que descargó anteriormente, después pulse “Finish”.
3) Uso del proyecto
Al usar proyectos “wrapper” Android, intentaremos siempre hacer uso del último SDK disponible. Sin embargo, deberá tenerse en cuenta que se actualizan muy a menudo, por lo que podría suceder que no disponga de una versión en particular instalada del SDK.
  • Pulse botón-derecho sobre el nodo raíz del proyecto y seleccione “Properties”.
  • Bajo la sección “Android” de las propiedades, asegúrese de tener marcado “Project Build Target”. Si dispone de varios SDK instalados, seleccione el más reciente. 



Si no dispone de un SDK reciente, le recomendamos que lo instale y lo marque como el “Build Target” a utilizar. El último “wrapper” para Android requiere cómo mínimo el API 18, pero le sugerimos que utilice siempre el más reciente. Para instalar otras versiones del SDK, use el “Android SDK Manager” accesible desde la barra de herramientas de Eclipse, icono:


Personalización de aplicaciones Android


Una vez importado el “wrapper” en Eclipse, deberá personalizarlo para su aplicación particular. Este proceso comprende los pasos siguientes:

  1. Renombrar el proyecto
  2. Editar el fichero “config.xml”
  3. Cambiar el nombre del paquete
  4. Cambiar el nombre visible de la aplicación (App Name)
  5. Añadir iconos personalizados
  6. Añadir pantallas de transición personalizadas
  7. Eliminar elementos no requeridos
  8. Localizar su aplicación
  9. Agregar SCAFs (sólo para aplicaciones “off-line”)
  10. Agregar bases de datos para funcionamiento “off-line”
  11. Editar la ventana “Acerca de..”  
¡paso a paso! iremos viendo todo ésto en sucesivas entregas...

17 diciembre 2014

Aplicaciones para iOS (despliegue) (Parte 4 de 4)

A través de la tienda Apple (App Store)


DESCARGO DE RESPONSABILIDAD: Antes de llevar acabo el proceso que se describe a continuación, usted deberá leer los requisitos y las directrices de Apple sobre la publicación de aplicaciones. TigerLogic no se responsabiliza en cuanto a los contenidos de su aplicación.

Este proceso requiere de un “App Store Provisioning Profile”. Si no dispone de uno, consulte la sección “Configuración del entorno iOS” de la entrada "Aplicaciones para iOS (requerimientos, instalación y configuración de xCode)" en éste mismo blog.

El primer paso será crear un repositorio para sus aplicaciones en “iTunes Connect”.

  • Conéctese a “iTunes Connect” (https://itunesconnect.apple.com) y seleccione “Manage Your Apps”.
  • Pulse el botón “Add New App”, y siga los pasos del asistente para crear su repositorio. Tendrá que aportar datos descriptivos de su tienda, imágenes, etc. El asistente le proporcionará todas las instrucciones que necesite.
  • Al terminar, habrá obtenido un repositorio de aplicaciones en estado de “Prepare For Upload”.
  • Si está satisfecho con las descripciones, imágenes y etc. aportados, pulse “View Details” para ver su nuevo repositorio de aplicaciones y a continuación, pulse “Ready to Upload Binary” finalmente, siga los pasos del breve asistente que se abrirá a continuación.

  • Su repositorio de aplicaciones deberá estar en estado de “Waiting For Upload”. Si es así, podrá subir su aplicación desde xCode.
  • Abra el “Organizer” de xCode (Pulse: Cmd-Shift-2) y sitúese en la sección “Archives”.
  • Seleccione el archivo que construyó en la sección anterior (Construcción de una aplicación iOS) y a continuación, pulse el botón “Validate”.
    • Siga los pasos del asistente, indicando su repositorio de aplicaciones y su “App Store Provisioning Profile” cuando se le solicite.
    • Se realizarán una serie de controles sobre su aplicación, tratando de localizar cualquier cuestión que pueda causar su rechazo.
    • Si se encuentra algún problema, asegúrese de corregirlo (si es así deberá crear un nuevo archivo y re-validarlo) antes de seguir adelante. 



  • Si todo ha ido bien, abra el “Organizer” y seleccione de nuevo “Archive”, pero esta vez pulse el botón “Distribute”.
  • Al igual que antes, siga los pasos del asistente, indicando su repositorio de aplicaciones y su “App Store Provisioning Profile” cuando se le solicite.
  • Al final de este proceso, su aplicación se habrá subido y presentado, pasando a estar a la espera de ser revisada por Apple. Podrá comprobar el progreso de esta revisión en “iTunes Connect”. Conviene que después de subir su aplicación compruebe como se ve en su tienda, puede que no se vea como usted esperaba. (por ejemplo, tal vez quiera revisar alguna captura de pantalla)

Si la evaluación termina con éxito (lo cual puede llegar a tardar varias semanas), se convertirá en disponible desde la App Store y abierta a millones de potenciales usuarios.

16 diciembre 2014

Aplicaciones para iOS (despliegue) (Parte 3 de 4)

Desde un servicio web propio


Con sólo un poco más de trabajo que el descrito en el proceso anterior, podrá dar a sus usuarios un toque de profesionalidad, automatizado el proceso de instalación desde su sitio web.

Este proceso requiere de un “Ad-Hoc Provisioning Profiles”. Si no dispone de uno, consulte la sección “Configuración del entorno iOS” de la entrada "Aplicaciones para iOS (requerimientos, instalación y configuración de xCode)" en éste mismo blog.

  • Abra el “Organizer” de xCode (Pulse: Cmd-Shift-2) y sitúese en la sección “Archives”.
  • Seleccione el archivo que construyó en la sección anterior (Construcción de una aplicación iOS) y a continuación, pulse el botón “Distribute”. 


  • Sobre la ventana que se abrirá, seleccione “Save for Enterprise or Ad-Hoc Deployment” y después pulse “Next”.
  • Seleccione el “Distribution Provisioning Profile” que desee usar para firmar la aplicación y pulse “Next”.
  • Desde el cuadro de diálogo “Save As”, marque la casilla “Save for Enterprise Distribution”.

Esto ampliará el diálogo con varios campos nuevos que deberá rellenar del modo siguiente:

  • Application URL - Esta es la URL donde se podrá encontrar la aplicación dentro del servidor. Se tratará de un archivo .ipa por lo que tendrá el formato: http://www.miservidor.com/Aplicaciones_iOS/miAplicacion.ipa.
  • Title - Es el nombre que se mostrará para la aplicación mientras se descarga e instalación en el dispositivo. Una vez instalada, sólo se verá al nombre definido en su proyecto XCode.
  • Small Image URL - Esta es la URL de una imagen PNG con tamaño 57x57 ubicada en un servidor web, será el icono que se muestre de fondo mientras la aplicación se descarga e instala en el dispositivo. Una vez instalada, la aplicación mostrará el icono ya definido en su proyecto XCode.
El fichero imagen “Icon2.png”, (ya usado en su proyecto) puede ser el adecuado.


  • Tome nota de las ubicaciones indicadas para sus ficheros .ipa y .png, después pulse “Save”.
  • Esto creará en el lugar indicado, dos ficheros, uno con extensión .ipa y otro con extensión .plist. Cópielos sobre el servidor web y en el espacio indicado en “Application URL”.
Si con el paso del tiempo olvida cuales fueron los lugares indicados (URL’s), podrá consultar el fichero .plist, ya que contiene su configuración.
  • Copie también al servidor el fichero de imagen y en el espacio indicado en “Small Image URL”.
  • Cree un enlace para su página web, con el siguiente formato:
    itms-services://?action=download-manifest&url=≤URL hacia su fichero .plist≥
  • Es posible que deba configurar el servidor web para transmitir adecuadamente este tipo de archivos. Podrá hacerlo mediante ajustar los “MIME Types” para que reconozca los ficheros “.ipa” como “application/octet-stream” y los “.plist” como “text/xml”.
Ahora, si un dispositivo iOS sigue este enlace (y siempre que el dispositivo haya sido incluido en su “provisioning profile”), su aplicación se descargará e instalará.

10 diciembre 2014

Aplicaciones para iOS (despliegue) (Parte 2 de 4)

Despliegue manual


Este proceso requiere de un “Ad-Hoc Provisioning Profiles”. Si no dispone de uno, consulte la sección “Configuración del entorno iOS” de la entrada "Aplicaciones para iOS (requerimientos, instalación y configuración de xCode)" en éste mismo blog.

  • Abra el “Organizer” de xCode (Pulse: Cmd-Shift-2) y sitúese en la sección “Archives”.
  • Seleccione el archivo que construyó en la sección anterior (Construcción de una aplicación iOS) y a continuación, pulse el botón “Distribute”. 


  • Sobre la ventana que se abrirá, seleccione “Save for Enterprise or Ad-Hoc Deployment” y después pulse “Next”.
  • Seleccione el “Distribution Provisioning Profile” que desee usar para firmar la aplicación y pulse “Next”.
  • Seleccione el lugar de destino en el cuadro de diálogo “Save As” y pulse “Save”.

Esto creará un archivo .ipa en el lugar indicado.

Lo que ha obtenido es una aplicación iOS firmada y lista para ser instalada en los dispositivos de los usuarios. Podrán hacerlo importando el archivo .ipa a iTunes, sincronizando después sus dispositivos.

05 diciembre 2014

Aplicaciones para iOS (despliegue) (Parte 1 de 4)

Despliegue de aplicaciones iOS

Una vez construida la aplicación, estará lista para su instalación en los diferentes dispositivos. A continuación describimos tres diferentes opciones en cuanto a cómo hacerla disponible a sus potenciales usuarios:

  1. Despliegue manual

  2. Desde un servicio web propio

  3. A través de la tienda Apple (App Store)

Despliegue manual: Consiste simplemente en distribuir su archivo “App Ad-Hoc” a los usuarios de forma manual. Después deberán instalarlo en sus dispositivos por medio de de iTunes.

Desde un servicio web propio: Consiste en alojar su “App Ad-Hoc” en un servidor web, a fin de que sus usuarios puedan instalar la aplicación en sus dispositivos mediante un enlace.

A través de la tienda Apple (App Store): Para que su aplicación pueda figurar en la tienda Apple o “App Store”, el proceso es un poco más complicado, pero los beneficios hacen que merezca la pena. El proceso requerirá que su aplicación sea verificada manualmente por parte de Apple, esto hace que su implantación pueda dilatarse en el tiempo, tal vez hasta un par de semanas y sin garantía alguna de que será aceptada.

03 diciembre 2014

Aplicaciones para iOS (el "wrapper") (Parte 9 de 9)

Agregar bases de datos para funcionamiento “Off-line”.


Es posible añadir una base de datos SQLite preparada para su uso con la aplicación (naturalmente sólo si es construida mediante el “Target” para SQLite). La cual podrá ser usada desde los “form’s” mediante conexiones $sqlobject, e incorporada del siguiente modo:

  • Arrastre el fichero .db de SQLite desde el sistema de archivos, a la carpeta “Resources” de su proyecto xCode.
    • Asegúrese de seleccionar la opción “Copy items into destination group's folder”.
    • Asegúrese también de marcar la casilla para que quede agregado al “SQLite target”.
  • Edite el archivo “config.xml” de su proyecto e indique en su propiedad “≤ServerLocalDatabaseName≥” el nombre de la base de datos (incluyendo la extensión .db).
Tenga en cuenta que usted está creando una aplicación para un dispositivo móvil, por lo que no deberá pensar en una gran cantidad de datos a almacenar. Si necesita acceder a grandes bases de datos, seguramente deberá conectarse a un servidor Omnis y utilizar la funcionalidad de sincronización para actualizar datos con el dispositivo. Puede encontrar toda la información relacionada con Sync Server en la web de Omnis.

Construcción de una aplicación iOS


Una vez personalizado el proyecto para su aplicación, la creación de una versión distribuible es un proceso muy simple.

  • Seleccione el código fuente apropiado (dependiendo de si su aplicación soportará o no bases de datos locales) de la lista desplegable y que se encuentra en la parte superior de la ventana xCode. También seleccione el dispositivo iOS que necesite.

  • Seleccione “Archive” en el menú “Product” de xCode.
  • De éste modo guardará el fichero de su proyecto en su estado actual. Con el podrá poner su aplicación a disposición de los usuarios, ya sea manualmente o a través de la tienda Apple “App Store”. Dicho proceso se describe con detalle en la sección siguiente “Despliegue de aplicaciones iOS”, ya que el proceso es diferente en función del modo de distribución elegido.

02 diciembre 2014

Aplicaciones para iOS (el "wrapper") (Parte 8 de 9)

Agregar SCAFs (sólo para aplicaciones “off-line”)


Si su aplicación debe incluir soporte para modo “off-line”, deberá decidir si incluirá o no los ficheros “SCAF” ya pre-instalados en la aplicación. Si lo hace, la aplicación será más grande, pero podrá ejecutarse en modo sin conexión desde el mismo principio, es decir, sin necesidad de conexión con el servidor Omnis para su actualización.

Para incluir los ficheros “SCAF” en la aplicación, deberemos hacer lo siguiente:

  • Busque la carpeta html/sc localizable bajo el raíz de la instalación Omnis Studio.
    • En caso de Windows, será bajo el directorio AppData, por ejemplo en: C:\Users\≤usuario≥\AppData\Local\TigerLogic\OS6.X\
  • Localice el “App SCAF” de su aplicación. (Un archivo con extensión .db dentro del directorio “sc” y con el mismo nombre que tiene su librería)
  • Localice también el “Omnis SCAF”. (Un archivo omnis.db situado en sc/omnis/)
  • Importe ambos archivos SCAF a su proyecto en xCode, situándolos dentro del directorio “OmnisJSWrapper/Resources”.
    • La forma más sencilla de hacerlo es arrastrándolos desde el Finder a su proyecto xCode. Asegúrese de seleccionar la opción “Copy Files” y de marcar las casillas “add to all of the targets”.

28 noviembre 2014

Aplicaciones para iOS (el "wrapper") (Parte 7 de 9)

Eliminar elementos no requeridos


Cuando construimos una determinada aplicación, es probable que no queramos incorporar todos los posibles ajustes de conexión. Para lograrlo, necesitaremos desactivar lo que no necesitemos del “Settings.bundle”.

  • Abra mediante xCode el proyecto y seleccione “Settings.bundle”.
  • Quite la marca de los “target’s” no requeridos, de entre los mostrados en “Target Membership”. 


26 noviembre 2014

Aplicaciones para iOS (el "wrapper") (Parte 6 de 9)

Localizar su aplicación


La localización, sólo está soportada en versiones 1.1.3 o superiores del “wrapper” iOS.

Si desea traducir los textos utilizados por la aplicación “wrapper”, podrá hacerlo tal y como describimos a continuación. Si el usuario configura su dispositivo para su uso con alguno de los idiomas soportados, se utilizarán los textos según la traducción elegida.

  • Sitúese sobre la raíz del proyecto en el “Project Navigator” y seleccione la pestaña “Info” del proyecto.
     
  • Sitúese bajo la sección “Localizations” y añada un nuevo lenguaje.


  • Sobre la ventana que aparece a continuación, asegúrese de seleccionar los dos archivos “Localizable.strings” e “InfoPlist.strings”. 
  • Si ahora desde nuestro “Project Navigator” nos situamos sobre nodo “Localizable.strings”, observaremos que puede ser ampliado. Si así lo hacemos, veremos que se ha creado una copia del archivo para el nuevo idioma.
  • Este archivo contiene grupos emparejados “key-value”. Las “keys” (situadas a la izquierda del signo igual) deberán dejarse como están, sin embargo, los “values” (situados a la derecha del signo igual) deberán traducirse al idioma elegido.
  • Asegúrese de que cada línea termina siempre con un punto y coma.

Si deseáramos cambiar algunos de los atributos de nuestro “application package” (como por ejemplo, el nombre de la aplicación, el icono, etc.), podríamos hacerlo del siguiente modo:

  • Busque el archivo “InfoPlist.strings” de su proyecto, localizable bajo el nodo “Supporting Files” amplíelo y escoja el correspondiente al idioma que desee. 
  • Cómo en el caso anterior, puede ser construido utilizando pares “key-value”, es decir con el formato: "Key Name" = "Key Value";
  • En este caso los nombres “key” deben coincidir con los definidos en el fichero “≤Nombre Proyecto≥-Info.plist. (por ejemplo “OmnisJSWrapper-Info.plist) La acción predeterminada no muestra éstos valores, por lo que deberá pulsar clic derecho sobre el nombre del archivo y escoger la opción “Open As ≥ Source Code” para verlos.
  • Recuerde que cada línea debe terminar con un punto y coma.


19 noviembre 2014

Aplicaciones para iOS (el "wrapper") (Parte 5 de 9)

Añadir iconos y pantallas de transición personalizadas


Las imágenes utilizadas para el proyecto como iconos y pantallas iniciales, se guardan en la carpeta: OmnisJSWrapper/Resources. Deberá sustituirlos (manteniendo sus mismos nombres) por los de su propia versión.

Sus tamaños y usos son los siguientes:

Iconos de Aplicación:


  • Icon.png (57x57) - iPhone/iPod No-retina. 
  • Icon@2x.png (114x114) - iPhone/iPod Retina. 
  • Icon-72.png (72x72) - iPad No-retina. 
  • Icon-72@2x.png (144x144) - iPad Retina.

Iconos de búsqueda y parámetros:


  • Icon-Small.png (29x29) - iPhone/iPod No-retina. 
  • Icon-Small@2x.png (58x58) - iPhone/iPod Retina. 
  • Icon-Small-50.png (50x50) - iPad No-retina. 
  • Icon-Small-50@2x.png (100x100) - iPad Retina.

Pantallas de transición:


  • Default.png (320x480) - iPhone/iPod No-retina. 
  • Default@2x.png (640x960) - iPhone/iPod Retina. 
  • Default-568h@2x.png (640x1136) - iPhone 5. 
  • Default-Portrait.png (768x1004) - iPad No-retina (orientación vertical). 
  • Default-Portrait@2x.png (1536x2008) - iPad Retina (orientación vertical). 
  • Default-Landscape.png (1024x748) - iPad No-retina (orientación horizontal). 
  • Default-Landscape@2x.png (2048x1496) - iPad Retina (orientación horizontal).

Imágenes para el “App Store” e “iTunes”:


  • iTunesArtwork (512x512) – Dispositivos No-retina. 
  • iTunesArtwork@2x (1024x1024) – Dispositivos Retina.

Si desea obtener más información sobre el uso de estas imágenes, consulte la documentación que proporciona Apple al respecto.

Aplicaciones para iOS (el "wrapper") (Parte 4 de 9)

Cambiar el nombre visible de la aplicación (App Name)


Para cambiar el nombre visible de su aplicación:

  • Sitúese sobre la raíz de su proyecto en el “Project Navigator”. 
  • Seleccione uno de los “Targets” y abra su pestaña “Build Settings”. 
  • Localice el “Product Name” de entre la lista de parámetros y modifique su valor.
    Repita éstos pasos para cada uno de los “targets”.


    12 noviembre 2014

    Aplicaciones para iOS (el "wrapper") (Parte 3 de 9)

    Cambiar el identificador (Identifier)


    El “Identifier” identifica a su aplicación y debe ser único entre todas las aplicaciones del dispositivo. Dos aplicaciones con el mismo identificador serían vistas por el dispositivo como si se tratase de la misma aplicación, por lo que este es un paso muy importante. Se recomienda el uso de una sintaxis de nombre de dominio inverso. Por ejemplo: com.miempresa.omnis.miaplicacion.

    • Sitúese sobre la raíz de su proyecto en el “Project Navigator”.
    • Seleccione cualquiera de los “Targets” y abra su pestaña “Summary”.
    • Modifique su “Bundle Identifier” consignado un valor diferente.



    05 noviembre 2014

    Aplicaciones para iOS (el "wrapper") (Parte 2 de 9)

    Editar del fichero “config.xml”


    El fichero "config.xml" contiene la configuración que utilizará su aplicación para conectarse con el servidor Omnis, además de otras opciones que determinarán su comportamiento.

    Localice el fichero "config.xml" situado sobre el directorio raíz del proyecto y modifique sus valores según sea necesario, tal y como se describe a continuación.

    • AppTitle – Mostrará o no, la barra de estado de iOS. 1 para cierto, 0 para falso.
    • SettingsFloatControls - Permite o no, que el “form” pueda cambiar su tamaño/posición, de acuerdo a su propiedad $edgefloat y en relación con la diferencia entre el tamaño de la pantalla del dispositivo y lo consignado en $screensize. 1 para cierto, 0 para falso. Funcionará sólo si el parámetro “SettingsScaleForm” contiene el valor 0.
    • SettingsScaleForm - De ser cierto (1), el “form” será escalado hacia arriba o hacia abajo, hasta ajustarse al tamaño de pantalla del dispositivo del cliente. 1 para cierto, 0 para falso.
    • SettingsAllowHScroll y SettingsAllowVScroll - Si es cierto, se permitirá el desplazamiento horizontal o vertical del “form”. 1 para cierto, 0 para falso.
    • SettingsMaintainAspectRatio - Si el parámetro “SettingsScaleForm” contiene el valor 1, el “form” se escalará de acuerdo a su aspecto original. 1 para cierto, 0 para falso.
    • SettingsOnlineMode - Determina si la aplicación se abrirá en modo “On-line” (1) o en modo “Off-line” (0).
    • TestModeEnabled - Determina si el modo de pruebas (opción Ctrl-M para testar “forms” en dispositivos) estará habilitado para su aplicación. 1 para cierto, 0 para falso.
    • TestModeServerAndPort – Es la ≤direccionip≥:≤puerto≥ usada con la versión Omnis Studio Developer durante el modo de pruebas. 
     
      • ServerOmnisWebUrl - URL del servidor Omnis o servidor Web. En caso de un servidor Omnis su formato debería ser: ≤direccionip≥:≤puerto≥ y si se trata de un servidor web, debería ser la raíz de su servidor Web: http://miservidor.com.
      • ServerOnlineFormName - Ruta hacia el fichero .htm correspondiente al “form” inicial, alojado en el servidor “ServerOmnisWebUrl”. Si se esta desarrollando la aplicación, es decir usando el “Omnis Studio Developer”, su formato será /jschtml/miform. Si se está utilizando un servidor web, será el resto de la URL necesaria para llegar hasta el fichero, por ejemplo, /omnisapps/miform. (¡Sin añadir la extensión .htm
     
      ServerOmnisWebUrl y ServerOnlineFormName, sólo son necesarios si se usarán “forms” en modo “On-line”. El resto de parámetros “Server...” se necesitarán si la aplicación también será ejecutada en modo “Off-line”. 
       
      • ServerOmnisPlugin - Si se está usando un plugin para establecer la comunicación entre el servidor web y el servidor Omnis, éste parámetro deberá indicar la ruta donde éste se encuentra, partiendo de “ServerOmnisWebUrl”. Por ejemplo /cgi-bin/omnisapi.dll.
      • ServerOmnisServer – Indica el camino hacia el Servidor Omnis, su formato es ≤direccionip≥:≤puerto≥. Sólo es necesario si se está usando un servidor web con el “Omnis Web Server Plugin” instalado. Si el servidor Omnis, se está ejecutando en la misma máquina que el servidor web, sólo necesitará indicar el puerto en uso. Ejemplo 194.168.1.49:5912.
      • ServerOfflineFormName - Nombre del “form” para uso en modo “off-line”. (¡No añada .htm!), Por ejemplo rfOffline.
      • ServerAppScafName - Nombre de la aplicación “SCAF”. Deberá coincidir con el nombre de su librería. Por ejemplo: milibreria. Nota: el nombre es sensible al uso de letras mayúsculas y minúsculas, por lo que le recomendamos usar sólo minúsculas).

    28 octubre 2014

    Aplicaciones para iOS (el "wrapper") (Parte 1 de 9)

    Primeros pasos


    • Primero, descargue la última versión del proyecto “iOS Wrapper Project”, desde la web de Omnis Studio.
    • Descomprima el fichero zip del “wrapper” sobre una carpeta y asegúrese de no dejar espacios intermedios en la ruta hacia la carpeta extraída.
    • Haga doble-clic sobre el fichero “OmnisJSWrapper.xcodeproj”, para abrir el proyecto con xCode. 

    Personalización de aplicaciones iOS


    Una vez importado el “wrapper project” sobre xCode, deberá personalizarlo para su aplicación en particular. Este proceso comprende los siguientes pasos:

    1. Renombrar el proyecto
    2. Editar el fichero “config.xml”
    3. Cambiar el identificador (Identifier)
    4. Cambiar el nombre visible de la aplicación (App Name)
    5. Añadir iconos y pantallas de transición personalizadas
    6. Localizar su aplicación
    7. Eliminar elementos no requeridos
    8. Agregar SCAFs (sólo para aplicaciones “off-line”)
    9. Agregar bases de datos para funcionamiento “off-line”

    Renombrar el proyecto


    Una vez abierto el proyecto “wrapper” en xCode, es probable que desee cambiar su nombre por otro, que designe de modo más apropiado a su aplicación en particular.

    La modificación del nombre del proyecto no ejerce efecto alguno sobre la aplicación resultante, pero si le permitirá registrar sus proyectos del modo más apropiado, especialmente si pretende crear diferentes aplicaciones ya que deberá usar proyectos independientes para cada una de ellas.

    • Escoja la vista “Project Navigator”, desde la barra lateral del proyecto (podrá hacerlo mediante hacer clic sobre el icono en forma de carpeta situado sobre la barra de herramientas).
    • Seleccione el nombre del proyecto, situándose sobre el nivel superior de la vista, y luego pulse la tecla “Entre” para cambiar su nombre.
    • Cambie el nombre y presione de nuevo “Enter”. Se le preguntará si también desea cambiar el resto de lugares donde también es usado. Deberá seleccionar la opción “rename all” para cambiar todas sus apariciones.
    • La versión actual de xCode (en el momento de escribir esto) parece omitir el cambio del los “Prefix Header” en los “targets”. De modo que deberá hacerlo manualmente:
      • Sitúese sobre el nivel raíz del proyecto en el “Project Navigator” – para que pueda ver la configuración del proyecto sobre el panel principal.
      • Seleccione uno de los “Targets”, para ver sus “Build Settings”.
      • Localice el ajuste “Prefix Header” (si lo desea puede usar para ello la caja de búsqueda) y asegúrese de que el nombre del archivo con extensión .pch figure con el formato -Prefix.pch.
      • Repita el paso anterior para los 3 “Targets”.

    Aplicaciones para iOS (requerimientos, instalación y configuración de xCode)

    Requerimientos iOS


    Para construir y desplegar aplicaciones iOS necesitará lo siguiente:

    • Mac OS X 10.7 o superior
    • xCode 4.5 o superior
    • $99 - $299 (cuota anual)

    Instalación de xCode


    Para la construcción de aplicaciones iOS, es imprescindible instalar xCode. Si lo desea puede descargarlo (para OS X 10.7 o posterior) a través de Mac App Store.

    Configuración como desarrollador autorizado


    Para la correcta creación de aplicaciones iOS, es necesario que su código esté firmado y hacerlo en el momento de su compilación. El primer paso del proceso será inscribirse en uno de los programas para desarrolladores de Apple iOS.

    Apple ofrece 3 diferentes opciones de inscripción en el “iOS Developer Program”:

    • Free - Permite descargar el SDK de iOS y probar las aplicaciones sobre el simulador, pero no podrá instalarlas sobre un dispositivo real.
    • Standard ($99 anuales) - Permite subir aplicaciones en la tienda “AppStore”, he impone un máximo de 100 dispositivos "Ad-Hoc" (modo test). 
    • Enterprise ($299 anuales) - Esta es la opción para compañías grandes, las cuales dispondrán de un “Dan & Broadstreet Number” (DUNS). Al igual que en el caso anterior, se permite la distribución de aplicaciones a través de la AppStore, y no impone límite en el número de dispositivos "Ad-Hoc".

    Seguramente la gran mayoría de los desarrolladores Omnis se inscribirán en el programa “Standard”, las indicaciones incluidas a continuación suponen el uso de ésta modalidad.

    Independientemente de la modalidad elegida, podrá registrarse como empresa o como individuo. La inscripción como empresa le ofrece la posibilidad de agregar miembros al equipo, mientras que la inscripción como individuo no lo permite.

    Una vez registrado como desarrollador iOS, deberá iniciar una sesión en el “iOS De Center” y seguir los pasos que le mostramos a continuación:

    Certificados


    • Abra la sección “Certificates” de su cuenta “iOS Dev Center” y seleccione el tipo “Production”.
    • Pulse sobre el botón + para abrir el asistente que le guiará durante la creación del certificado.

    • Cuando se le pida que seleccione el tipo de certificado, deberá elegir dentro de “Production” el tipo “App Store and Ad Hoc”.
      • En caso de que no tenerlo ya instalado, use el enlace que aparece en esta misma página para descargar e instalar el “Intermediate Certificates” (Worldwide Developer Relations Certificate Authority).
    • El asistente le guiará a través del resto del proceso de creación.
    • Una vez creado el certificado (y asociada su clave privada), es importante que guarde una copia del mismo en un lugar seguro.
      • Abra el “Keychain Access” localizado en “Applications/Utilities/”.
      • Pulse sobre la categoría “Certificates” localizada en la barra lateral y después sobre el certificado que acaba de crear.
      • Pulse botón-derecho sobre el certificado y escoja la opción “Export”.
      • Manténgalo en lugar seguro - si cambia de hardware o por cualquier otra razón pierde el certificado, podrá importarlo de nuevo desde esta copia de seguridad. Cualquier actualización posterior de sus aplicaciones, deberá ser firmada con el mismo certificado, de modo que esto es muy importante.

    Identificadores


    Un “App ID” (identificador de aplicación) determina los que podrán ser usados para realizar firmas mediante su perfil.
    • Abra la sección “Identifiers” de su cuenta “iOS Dev Center” y seleccione “App IDs”.
    • Pulse sobre el botón + para abrir el asistente que le guiará durante la creación del “App ID”.
    • Puede optar por crear un “Explicit App ID” (permite firmar un único identificador de aplicación) o un “Wildcard App ID” (permite firmar cualquier aplicación cuyo identificador coincida con el formato especificado).
      El formato para los identificadores de aplicación consiste en un nombre de dominio inverso. Por ejemplo “com.entidad.aplicacion”. De modo que deberá crear el identificador siguiendo éste patrón, tanto si está creando un “Explicit App ID”, como un “Wildcard App ID”. Está permitido el uso del carácter “*”, para indicar que se usará cualquier nombre, por ejemplo “com.entidad.*”.
      Sea cual sea el nombre elegido, téngalo siempre a mano, ya que tendrá que hacer uso de él más tarde, cuando tenga que asignar un identificador a su aplicación.
    • Las aplicaciones o “wrapper’s” Omnis, no requieren “App Services”.

    Dispositivos


    Si va a desplegar sus aplicaciones directamente (Ad-Hoc) y no a través de la “App Store”, deberá registrar cada dispositivo susceptible de ejecutar su aplicación.

    • Abra la sección “Divices” de su cuenta “iOS Dev Center”.
    • Pulse sobre el botón + para abrir la página que le permitirá añadir el dispositivo.

    • Los diferentes dispositivos deberán ser añadidos con su UDID (Ónique Device Identifier). Podrá localizar el UDID del dispositivo mediante conectarlo a iTunes y hacer clic sobre su número de serie. En la dirección http://whatsmyudid.com/ podrá encontrar una práctica explicación sobre como hacer esto.

    Aprovisionamiento de perfiles (Provisioning Profile)


    Un “Provisioning Profile” posibilita la unión de un Certificado (Certificate) con un “App ID” (también la de un grupo de dispositivos (Devices) con un “Ad Hoc Provisioning Profiles”). Es mediante el “Profile” resultante, con el que podrá firmar su aplicación (combinado con el “certificate/private key” almacenada en su llavero).

    • Abra la sección “Provisioning Profiles” de su cuenta “iOS Dev Center” y seleccione “Distribution”.
    • Pulse sobre el botón + para abrir el asistente que le guiará durante la creación de un nuevo “Provisioning Pofile”.

    • Podrá crear un “Distribution Provisioning Profile” de tipo “Ad Hoc” (despliegue directo) o bien de tipo “App store”.
      Deberá seleccionar en cada momento el que coincida con el modo de distribución elegido. Asegúrese de crear perfiles de tipo “Distribution”  y no del tipo “Development”.
      Es posible la creación de varios “Provisioning Profiles”, a fin de poder hacer uso de uno o más tipos, si así lo desea.
    • Siga los pasos del asistente. Una vez completado, podrá descargar el “Provisioning Profile”, para después hacer doble-clic desde el Finder sobre el archivo descargado, haciendo posible su incorporación a xCode.


    09 octubre 2014

    Construcción de aplicaciones para iOS, Android y BlackBerry con Omnis Studio

    Además de utilizar la nueva tecnología “JavaScript Client” en la construcción de aplicaciones ejecutables sobre el navegador de cualquier ordenador, tablet o dispositivo móvil, podemos usarla en la creación de aplicaciones totalmente terminadas para dispositivos iOS, Android y BlackBerry. Dichas aplicaciones podrán incluso operar completamente “off-line”, es decir, sin necesidad de conexión a ningún tipo de servidor, en éste caso tan sólo requerirá de un número de serie especial instalado en su SDK Omnis, el “Serverless Client Serial”.

    Para la construcción de dichas aplicaciones terminadas, disponemos de tres aplicaciones "esqueleto" construidas a medida de cada sistema, denominadas “wrapper” y presentadas en la forma de proyectos JavaScript: una para iOS, una para BlackBerry10 y otra para Android.

    Dichos proyectos posibilitarán la creación de aplicaciones personalizadas, mediante una sencilla integración de clases “remote-form”, que permitirán a su vez el acceso a gran parte de las funcionalidades implementadas de modo nativo en el dispositivo, tales como la lista de contactos, función GPS y cámara de fotos.

    Mediante las sucesivas entregas que iré publicando en éste blog, comprenderemos los diferentes pasos necesarios para crear y desplegar aplicaciones personalizadas para cada una de las plataformas móviles mencionadas. Estudiaremos paso a paso, todo lo que necesitaremos saber, tanto para la creación de la aplicación, como para su despliegue a los usuarios finales, ya sea que se haga a través de la tienda de aplicaciones del propio dispositivo o de manera autónoma.

    Les sugiero que se suscriban a "Aula Omnis" a  través de cualquiera de las redes disponibles Facebook, Twitter, Linkedin, Google+, RSS FeedBurner o como seguidores de éste blog, a fin de no perderse las sucesivas entregas.

    Reciban un cordial saludo.

    08 octubre 2014

    Creación automática de tablas en el servidor

    En algunos casos, puede tener sentido la ejecución de un proceso, que cree las tablas que falten en el servidor automáticamente tras iniciar la sesión con la base de datos, la intención sería, causar que las tablas necesarias para el correcto funcionamiento de la aplicación existan. Dicho proceso podría lanzarse cuando la aplicación es ejecutada por primera vez, cuando se entrega una nueva versión de la base de datos, cuando se cambie a un nuevo servidor de base de datos o a una base de datos diferente.

    Puesto que estamos haciendo uso de clases "schema" durante el desarrollo de nuestra aplicación, es posible compararlas con las tablas correspondientes en el servidor, para saber si existen realmente y en caso necesario crearlas.

    Partiendo de la base de que tenemos una variable "task" denominada "tSessObj" con una conexión válida a una base de datos. Crearemos un "statementobject" que más tarde utilizaremos para enviar las sentencias SQL necesarias a la base de datos, lo haremos sobre la variable local "lStatObj" (de tipo "Object" y sin subtipo).

    Do tSessObj.$newstatement(#CT) Returns lStatObj

    La variable "Object" "lStatObj" ahora dispone de los métodos y propiedades necesarios y que pueden ser localizados mediante el "Interface Manager" (opción disponible, mediante pulsar el botón derecho del ratón sobre la variable) si lo deseamos.

    A continuación, utilizaríamos el código que mostramos más abajo para obtener las tablas alojadas en el servidor de base de datos, recogiéndolas sobre la variable de tipo lista "lDBTableList", tal como sigue:

    Do lStatObj.$tables(kStatementServerTable)

    Tenga en cuenta que sólo podrá ejecutar éste código mientras disponga de una instancia activa, es decir, mientras exista una conexión válida con la base de datos. Por lo que, si desea depurar el código, deberá marcar un punto de interrupción en el lugar que desee de la "Startup_Task", para cargar sobre la lista el resultado del método anterior, escriba:

    Do lStatObj.$fetch(lDBTableList,kFetchAll)

    El segundo paso será crear otra lista, que contenga las clases "schema" existentes en nuestra librería. Para ello usaremos también dos variables locales que la definirán, tal y como sigue:

    Do lTablesForAppList.$define(lTableName,lSchemaClassName)
    Do $schemas.$appendlist(lTablesForAppList,$ref.$servertablename,$ref.$name)

    Ahora tendremos que ejecutar un bucle "For" que ira recorriendo la lista de clases "schema" y creando las nuevas tablas en el servidor según sea necesario:


    For lTablesForAppList.$line from 1 to lTablesForAppList.$linecount() step 1
       Do lTablesForAppList.$loadcols()
       ; carga la línea actual en los campos correspondientes lTableName y lSchemaClassName
       Do lDBTableList.$search(lDBTableList.TableOrViewName=lTableName,kTrue,kFalse,kFalse,kFalse)
       If not(lDBTableList.$line) ;; cuando no existe la tabla
          Do lRow.$definefromsqlclass(lSchemaClassName)
          ; define una variable local de tipo "row" con el nombre del "schema" que se acaba de cargar
          Do lRow.$sessionobject.$assign(tSessObj) ;; ¡importante! hace que $createnames funcionen correctamente
          ; crea la nueva tabla en el servidor:
          Do lStatObj.$execdirect(con('CREATE TABLE ',lRow.$servertablenames(),'
    (',lRow.$createnames(),') ')) Returns lStatus
          If not(lStatus)
             Breakpoint -> implementar aquí el controlador de errores
          End If
       End If
    End For

    Naturalmente este código deberá ejecutarse después de abrir la sesión con la base de datos sobre el método $construct de la "Startup_Task", cómo puede observar, se comprueba si existe la tabla en el servidor en correspondencia con lo definido en su clase "schema" y si no existe será automáticamente creada.

    01 octubre 2014

    Uso de las clases "table"

    Los desarrolladores a menudo nos preguntamos si no sería mejor utilizar clases "object" en lugar de clases "table" a la hora de construir código SQL, ya que al fin y al cabo, es posible programar en modo orientado a objetos, aún sin utilizar clases "table".

    A primera vista, esto parece una propuesta razonable, ya que al utilizar las clases "object", disponemos de acceso directo al código y no será necesario escribir métodos con el código SQL sobre la clase "table", sino que podremos hacerlo en cualquier lugar.

    De hecho, (según me consta) es un práctica extendida entre los programadores el hacer uso de las clases "schema" directamente, en lugar usar clases "table" conectadas a clases "schema". Seguramente debido a que con las clases "table", el programador debe decidir si introducir el código SQL a ejecutar dentro de la propia clase o simplemente hacer uso de los métodos y propiedades predefinidos, además de que, en cierto modo, las clases "table" son similares a clases "object".

    Esencialmente la diferencia está, en que las clases "table" tienen propiedades adicionales. Por ejemplo; cuando una instancia, de una clases "table" ha sido definida con las columnas de una variable de tipo "list" o "row" y añadido a ella los métodos SQL que deseemos. Debemos tener en cuenta que dicha instancia es también un objeto de datos, es decir, una variable "list" o "row". Esto significa que en realidad estamos incluyendo métodos dentro de objetos de datos. El ejemplo siguiente, muestra un método en una clase "table", del cual obtenemos su valor de retorno:

    If myRow.$checkFields()
       Do myRow.$insert()
    Else
       OK message "El campo name esta vacío"
    Endif


    El método $checkFields() en la clase "table" podría ser:

    If len($cinst.name) = 0 ;;name está vacío
       Quit method kFalse
    Else
       Quit method kTrue
    Endif

    En nuestro ejemplo $cinst hace referencia a la fila actual de la variable "list" utiliza al definir la clase "table".

    Algunos podrían argumentar que puede lograrse el mismo resultado usando una clase "object". Pero, esto no sería estrictamente cierto ya que requeriría del uso de otra capa de código, además del necesario uso de la clase "object" para acceder a la variable "list" o "row", mientras que en la clase "table" se aúnan ambas cosas.

    Además (como en el caso de las clases "object") se puede hacer uso de los métodos predefinidos a la hora de gestionar la "list" o "row" de datos ya que, como tal, las clases "table" son autónomas. Esto nos permite además hacer uso de las propiedades y métodos de uso general, al mismo tiempo que decidir si haremos uso o no, de los definidos por nosotros, al actuar como super-clase de la clase "table". Atendiendo a nuestro ejemplo, colocaríamos el método $checkFields() en la super-clase, con lo que tendríamos la opción de anular este método desde la clase "table" derivada en caso necesario, para ello simplemente colocaríamos el comando siguiente en el método en la super-clase:

    Quit method kTrue

    En otras palabras, si no se hereda el método checkFields() de la clase "table", siempre devolverá kTrue.

    Por supuesto usted es libre de usar clases "object" o "table", o ambas a la vez según lo considere oportuno, a la hora de incorporar el código SQL a su aplicación. La única cosa que deberá tener siempre en cuenta es, que la definición que hagamos mediante el método $definefromsqlclass() sobre la variable "list" o "row", será quien determine, donde deberá añadirse el código SQL. Por ejemplo:

    Do myList.$definefromsqlclass($tables.T_Address,'Name','Firstname','City','ADDRESS_ID')

    En éste ejemplo, "myList" es definida mediante la clase "table" "T_Address" y con las columnas; "Name", "Firstname", "City" y "ADDRESS_ID".

    Cuando desee escribir su propio código SQL con estos nombres de columna, deberá hacerlo mediante la implementación de un método dentro de la propia clase "table" y que hará uso de su propio "statementobject" al ejecutarse. Tenga en cuenta que para que esto funcione, el objeto de sesión (sessionobject) deberá haber sido asignado a la clase "table", cosa que (dicho sea de paso) habremos hecho en el método $construct() de la super-clase.

    Método: $loadExample()

    Begin Statement
        STA: SELECT [$cinst.$selectnames()]
        STA FROM ... (su propio SQL)
    End Statement
    Do $cinst.$statementobject().$prepare()
    Do $cinst.$statementobject().$execute() Returns Ok
    If not(Ok)
        Do $cinst.$sqlerror()
        Quit method kFalse
    Else
        Do $cinst.$fetch(kFetchAll) ;; Cargas todos los registros en el propio objeto
        Quit method kTrue
    Endif

    Note que $cinst.$selectnames() devolverá los datos de las columnas de la variable "list" o "row" utilizada en el momento de definir el instancia para la clase "table".

    El método sería invocado desde la clase "table", del modo siguiente:

    If myList.$loadExample()
       Do $cinst.$redraw()
    Else
       Ok message "No se encontraron datos: [myList.$getErrorText()]
    Endif

    Utilizando este enfoque, el objeto cargará sus datos de forma independiente. al mismo tiempo que nos permite obtener el mensaje de error que pudiera producirse, lo cual también podría ser implementado en la super-clase si optáramos por un control de errores más generalizado, algo así como:

    Método: $getErrorText()

    Quit method $cinst.$statementobject().$nativeerrortext()

    Esto completaría el círculo, ya que ahora estaríamos accediendo directamente las propiedades del "statementobject" ubicado dentro del propio objeto de datos. El método $getErrorText() devuelve el texto nativo del error, para ser mostrado el mensaje.

    Pensemos ahora en otra ventaja al usar las clases "table", cuando hacemos uso de nuestros propios métodos para insertar datos solemos hacer uso de variables "bind" (ya hemos hablado de ellas en un artículo anterior de éste blog). Las variables "bind" hacen uso de caracteres especiales, con el fin de ahorrarnos la necesidad de poner comillas para encerrar textos. Pero en el caso de las clases "table", y si hacemos uso del método $insert() por defecto, (es decir no personalizado) el objeto utilizará automáticamente las variables tomándolas directamente de la variable "list" o "row" por lo que no tendremos que preocuparnos de nada en absoluto.

    Además, el uso de clases "table", no impide el control sobre el código SQL que enviamos al servidor. Para ejemplo:

    Calculate mySQLText as $cinst.$statementobject().$sqltext

    Si decidiéramos no pasar ciertas columnas, podríamos excluirlas de los $insert() desde el método $construct() de la super-clase "table", del modo siguiente:

    Do $cinst.$cols.ident.$excludefrominsert.$assign(kTrue)

    En este ejemplo, la columna "ident" sería excluida de la inserción.

    Por cierto, el método integrado $sqlerror() en una clase "table" es ejecutado automáticamente, cuando cualquiera de los métodos incluidos tales como $insert(), $update(), $delete() o $select() falla por cualquier razón. Por lo que, podría tener sentido anular este método también de la super-clase "table", creando un controlador de errores SQL más genérico en nuestra aplicación, en el manual Omnis Studio en Castellano podrá encontrar más información al respecto.

    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)