VISITAS:

lunes, 17 de noviembre de 2014

Notificaciones push para Android con Corona SDK y Gamethrive


NOTA: Utilizaré el servicio GameThrive, el cuál tiene la ventaja de ser gratuito, ser compatible con notificaciones Android e iOS, y además tiene un plugin gratuito para Corona SDK.


1. Registrarse en Gamethrive (si no estamos registrados previamente).

https://gamethrive.com/

2. Crear un projecto con notificaciones en Google

https://console.developers.google.com/project

Pulsar en "Crear proyecto"

Dar un nombre al proyecto.
El ID del proyecto da igual.
Pulsar el botón "Crear" (puede tardar casi un minuto en crear el proyecto).

En el frame de la izquierda, pulsar en "Descripción general".
Arriba aparece el número del proyecto (#PN). Este dato será necesario para enviar notificaciones a la aplicación.

En el  frame de la izquierda, pulsar en APIs y autenticación. Después pulsar en APIs
Buscar "Google Cloud Messaging for Android" y pulsar en el botón a la derecha de esta línea para activar el servicio.
Una vez activado, aparecerá arriba en la pantalla con los demás servicios activados.

En el frame de la izquierda, pulsar en APIs y autenticación. Después pulsar en Credenciales.
Pulsar el botón "Crear clave nueva".
Pulsar en "Clave de servidor"
No escribir nada en el textbox y pulsar el botón "Crear".
La clave de la API (#KEY) la necesitaremos después.

3. Crear una aplicación en Gamethrive

Entrar en la cuenta de Gamethrive que hemos creado en el apartado 1.

Ir al panel de aplicaciones.

Pulsar el botón "Add a new app"
Dar un nombre a la aplicación (se recomienda poner el mismo nombre que se puso en Google, para evitar confusiones).
Pulsar el botón "Submit".

En el frame izquierdo, pulsar "Application Settings"
Se muestran App ID/Key (#APPID) y REST API Key (#REST) que se utilizarán después.
En la sección "Platforms", pulsar el botón "Configure" en la línea de "Google Play".
Copiar la clave que se generó en Google al final del apartado 2 (#KEY).
Pulsar el botón "Save".
Aparecerá la línea "Google Play" como "Active"

4. Corona build.settings

Dentro de settings, añadir el siguiente elemento, por ejemplo, después del elemento orientation:

plugins =
{
    ["plugin.GameThrivePushNotifications"] =
    {
        publisherId = "com.gamethrive",
    },
    ["plugin.google.play.services"] =
    {
            publisherId = "com.coronalabs",
            supportedPlatforms = { android=true },
    },
},

También dentro de settings, hay que poner la siguiente sección:

        android =
        {
            permissions =
            {
                { name = ".permission.C2D_MESSAGE", protectionLevel = "signature" },
            },
            usesPermissions =
            {
                "android.permission.INTERNET",
                "android.permission.GET_ACCOUNTS",
                "android.permission.RECEIVE_BOOT_COMPLETED",
                "com.google.android.c2dm.permission.RECEIVE",
                ".permission.C2D_MESSAGE",
            },
        },


4. Corona config.lua

Dentro de application, añadir el siguiente elemento, por ejemplo, después del elemento content:

        notification = {
            google = { projectNumber = "#PN" },
        }, 

5. Corona main.lua

Al principio del fichero main.lua:


-- Esta función es llamada cuando el usuario abre una notificación o cuando la aplicación está abierta y activa.
function DidReceiveRemoteNotification(message, additionalData, isActive)
    if (additionalData) then
        if (additionalData.discount) then
            native.showAlert( "Discount!", message, { "OK" } )
            -- 
        elseif(additionalData.bonusCredits) then
            native.showAlert( "Bonus Credits!", message, { "OK" } )
            -- 
        elseif(additionalData.actionSelected) then -- Interactive notification button pressed
            native.showAlert("Button Pressed!", "ButtonID:" .. additionalData.actionSelected, { "OK"} )
        end
    end
end

local GameThrive = require("plugin.GameThrivePushNotifications")
GameThrive.Init("poner aquí #APPID de gamethrive", "poner aquí #PN de google", DidReceiveRemoteNotification)

La función DidReceiveRemoteNotification se ejecutará cuando el usuario abra una notificación.
Casos:
  • El móvil está apagado: cuando se encienda el móvil aparecerá la notificación y al pulsarla, se abrirá la aplicación y se ejecutará esta función.
  • La aplicación está abierta y activa (foreground): se invocará esta función, aunque no aparecerá el icono de la notificación
  • Si la aplicación no está activa: aparecerá el icono de la notificación y cuando el usuario lo pulse, se abrirá la aplicación y se ejecutará esta función.
Parámetros de esta función:
  • message: es el mensaje de la notificación.
  • additionalData: son datos adicionales que podemos añadir a la notificación (ya se envíe desde la web de gamethrive o bien a través del API de gamethrive).
  • isActive: indica (true o false) si la aplicación estaba activa.

6. Icono de notificación

El icono de notificación debe ser un fichero en formato PNG, con fondo transparente y color blanco.
Se debe crear un icono para cada una de las distintas densidades de pantalla y otro conjunto de iconos para Android API v11. En resumen, hay que crear los siguientes iconos:


  • IconNotificationDefault-ldpi.png ---> 18x18 px
  • IconNotificationDefault-ldpi-v11.png ---> 18x18 px
  • IconNotificationDefault-mdpi.png ---> 24x24 px
  • IconNotificationDefault-mdpi-v11.png ---> 24x24 px
  • IconNotificationDefault-hdpi.png ---> 36x36 px
  • IconNotificationDefault-hdpi-v11.png ---> 36x36px
  • IconNotificationDefault-xhdpi.png ---> 48x48 px
  • IconNotificationDefault-xhdpi-v11.png ---> 48x48 px

Hay que tener cuidado de dejar al menos un pixel en ldpi, un pixel en mdpi, un pixel en hdpi y dos pixel en xhdpi, como borde entre la imagen y el icono.




jueves, 28 de agosto de 2014

Estados de una Activity en Android

ESTADOS

Los estados en los que puede estar una Activity en Android son:

  • NON-EXIST
  • STOPPED: no visible
  • PAUSED: visible
  • RUNNING: visible y en foreground
Las transiciones entre estados y las funciones de la Activity invocadas son las siguientes:

  • NON-EXIST --> STOPPED: onCreate()
  • STOPPED --> PAUSED: onStart()
  • PAUSED --> RUNNING: onResume()
  • RUNNING --> PAUSED: onPause() y onSaveInstanceState()
  • PAUSED --> STOPPED: onStop()
  • STOPPED --> NON-EXIST: onDestroy()


EVENTOS QUE CAUSAN CAMBIOS DE ESTADO

Lanzar la Activity

Pasa de estado NON-EXIST a estado RUNNIG, invocándose las siguientes funciones:
  • onCreate()
  • onStart()
  • onResume()

Pulsar Back

Pasa de estado RUNNIG a estado NON-EXIST, invocándose las siguientes funciones:
    • onPause()
    • onSaveInstanceState()
    • onStop()
    • onDestroy()

    Pulsar Home

    Pasa de estado RUNNIG a estado STOP, invocándose las siguientes funciones:
        • onPause()
        • onSaveInstanceState()
        • onStop()

        Abrir la aplicación en recientes después de Home

        Pasa de estado STOP a estado RUNNING, invocándose las siguientes funciones:
            • onStart()
            • onResume()

            Abrir la aplicación en recientes después de Back

            Pasa de estado NON-EXIST a estado RUNNING, invocándose las siguientes funciones:
                • onCreate()
                • onStart()
                • onResume()

                Rotar dispositivo

                Pasa de estado RUNNIG a estado RUNNING, invocándose las siguientes funciones:
                    • onPause()
                    • onSaveInstanceState()
                    • onStop()
                    • onDestroy()
                    • onCreate()
                    • onStart()
                    • onResume()

                    Lanzar otra Activity desde la Activity

                    Pasa de estado RUNNIG a estado STOP, invocándose las siguientes funciones:
                        • onPause()
                        • onSaveInstanceState()
                        • onStop()

                        Pulsar Back en la otra Activity

                        Pasa de estado STOP a estado RUNNING, invocándose las siguientes funciones:
                            • onStart()
                            • onResume()


                            CÓMO VE EL S.O. ANDROID LAS ACTIVITIES

                            Cuando el usuario pulsa sobre una aplicación en su móvil o tablet Android, el sistema operativo no arranca la aplicación, sino que arranca una Activity y la pone en estado RUNNING.
                            Normalmente, las aplicaciones contienen múltiples activities, ¿cuál de ellas se arranca? Esto viene definido en el fichero AndroidManifest.xml. Una activity tendrá definido:

                               

                            dentro de

                            Si desde la Activity inicial se arranca otra Activity, el sistema arrancará la nueva Activity y la pondrá en estado RUNNING (encima de la anterior). Estas dos actividades están en el stack de actividades de Android.
                            Si se pulsa Back, la actividad actual se destruye y vuelve la actividad inicial a estado RUNNING (el mismo efecto se consigue invocando el método finish() ).

                            Si se pulsa Back en la actividad inicial, se destruye la Activity inicial y volveremos donde estábamos antes de arrancar la aplicación.

                            El responsable de realizar todas estas tareas es el ActivityManager de Android, el cual mantiene un stack de actividades compartido entre todas las aplicaciones.

                            martes, 15 de julio de 2014

                            Unity 3D: Publicación para iOS en Apple App Store

                            Introducción

                            Desarrollar un juego 3D ó 2D en Unity no es demasiado difícil, aunque requiere una capacidad de programación (en C# o Unity script) relativamente importante.
                            Cuando se desarrolla con Unity, las pruebas se realizan en el mismo entorno Unity, ya sea Windows o Mac OS X.
                            Sin embargo, al final, queremos publicar nuestro juego en una plataforma móvil tal como Android o iOS. La publicación en Android la trataré dentro de poco, pero hoy quiero explicar cómo publicar para iOS (por cierto, bastante más complejo que para Android).

                            Requisitos previos

                            Para poder publicar en la Apple AppStore es necesario cumplir unos requisitos previos:
                            • Tener el Apple developer. Para ello hay que registrarse en http://developer.apple.com. Apple cobra 99$ al año.
                            • Tener un ordenador Mac. Apple no permite generar aplicaciones para iOS desde Windows u otros sistemas operativos
                            • Tener instalado XCode
                            • Tener un juego desarrollado y probado en Unity (esto es fundamental)

                            Pasos


                            1. Probar con el simulador en XCode
                            2. Probar en un dispositivo
                            3. Subir app al store
                            4. Actualizar la app

                            Esta guía es muy sencilla y describe paso a paso cómo publicar un juego en la tienda de aplicaciones de Apple. Los pasos que aquí se describen están realizados en julio de 2014. Si cuando leas este post, ha pasado mucho tiempo desde entonces, puede ser que algunos pasos hayan cambiado ligeramente, aunque los cambios no suelen ser muy importantes.

                            PROBAR CON EL SIMULADOR

                            UNITY


                            1. File > Build Settings…

                            2. En la sección Platform, seleccionar iOS

                            3. Si el botón Switch Platform está activo, pulsarlo para adaptar los assets del proyecto a iOS (esto puede tardar un rato)

                            4. Pulsar en Player Settings

                            5. Preparar icono de la aplicación para App Store

                                 - Preparar el icono en PNG 1024x1024 (también vale JPG, pero no tiene transparencia)
                                 - Este icono debe utilizarse como icono de la aplicación en Player Settings, si no, Apple puede rechazarlo


                            6. En el Inspector:
                                 - En la sección Resolución and Presentation, seleccionar Default Orientation (normalmente se pone Auto Rotation, para que pueda girar). Marcar Use Animated Rotation para que los giros sean animados. Seleccionar Allowed Orientations (si debe ser landscape, seleccionar sólo Landscape Left y Landscape Right)
                                 - En la sección Icon, seleccionar un icono de 1024x1024 para la aplicación
                                 - En la sección Other Settings, lo más importante se encuentra en Identification, donde hay que poner el bundle id, por ej, com.joseanquiles.laberinto. También hay que especificar bundle versión, que en la primera versión será 1.0. El resto de valores se pueden dejar por defecto. En Optimization es muy importante elegir SDK Version. Las dos opciones son: Simulator SDK, Device SDK. Como ahora vamos a probar en el simulador de XCode, seleccionamos Simulator SDK.

                            7. Pulsar el botón Build para generar el proyecto de XCode. Si ya se ha generado antes, es mejor reemplazar. Se genera un proyecto XCode (por defecto dentro del directorio del proyecto Unity). Se recomienda añadir como sufijo al directorio _iOS o similar, para reconocer rápidamente el contenido de este directorio

                            XCODE


                            1. Con el Finder entrar en el directorio del proyecto Unity/Proyecto XCode generado en el paso anterior

                            2. Si XCode está abierto, cerrarlo.

                            3. Doble click en el fichero Unity-iPhone.xcodeproj Se abrirá XCode

                            4. En la parte superior izquierda, seleccionar el dispositivo y después la versión de iOS (no elegir iOS 6.1, porque no funciona, no sé porqué)

                            5. Pulsar el botón Run (similar a Play) en la parte superior izquierda

                            6. Para capturar screenshots con el simulador, compilar la app para Simulator SDK (tal y como se explicó anteriormente), ejecutar en XCode y en el menú del simulador: Archivo > Guardar captura de pantalla. La captura se guarda en el escritorio

                                 - Screenshots para iPhone 4: PNG 960x640 -> utilizar el device iPhone 3.5"

                                 - Screenshots para iPhone 5: PNG 1136x640 -> utilizar el device iPhone 4”

                                 - Screenshots para iPad: PNG 1024x768 -> utilizar el device iPad



                            PROBAR EN UN DISPOSITIVO


                            Requisitos: Estar registrado en iOS Developer Program de Apple

                            Portal del desarrollador de Apple: http://developer.apple.com/membercenter/index.action


                            PORTAL DE DESARROLLO


                            1. Registrar un dispositivo de test (si ya se ha registrado antes, esto no es necesario)

                                 - Portal del desarrollador > Certificates, Identifiers & Profiles
                                 - iOS Apps > Devices > All
                                 - Pulsar el botón + para añadir un nuevo dispositivo
                                 - Name: dar un nombre al dispositivo
                                 - UDID -> para descubrir el UDID del dispositivo, pasar al siguiente punto

                            2. Descubrir el UDID del dispositivo

                                 - Conectar el dispositivo por USB
                                 - Abrir XCode
                                 - Window > Organizer
                                 - En DEVICES, seleccionar el dispositivo
                                 - En el apartado Identifier se muestra el UDID (copiarlo al clipboard)

                            3. Volver al portal del desarrollador para continuar el registro del dispositivo:

                                 - UDID: copiar el UDID cogido en el punto anterior
                                 - Pulsar en Continue
                                 - Pulsar en Register

                            4. Registrar el ID de la aplicación

                                 - Portal del desarrollador > Certificates, Identifiers & Profiles
                                 - iOS Apps > Identifiers > App IDs
                                 - Pulsar el botón +
                                 - App ID description: Patri Labyrinth
                                 - App ID prefix: generado automáticamente
                                 - App ID suffix:
                                           seleccionar Explicit App ID
                                           Bundle ID: com.joseanquiles.laberinto
                                 - App services: no cambiar nada
                                 - Pulsar el botón Continue
                                 - Confirm your App ID
                                 - Pulsar el botón Confirm
                                 - Registration complete
                                 - Pulsar el botón Done

                            5. Crear un perfil de provisión para desarrollo

                            Es necesario que XCode y el dispositivo tengan un perfil de provisión de desarrollo de la aplicación para poder testear en el dispositivo

                                 - Portal del desarrollador > Certificates, Identifiers & Profiles
                                 - iOS Apps > Provisioning profiles > All
                                 - Pulsar el botón +
                                 - Seleccionar iOS App Development
                                 - Pulsar el botón Continue
                                 - App ID: Patri Labyrinth
                                 - Pulsar el botón Continue
                                 - Select Certificate: José Antonio Quiles (iOS development)
                                 - Pulsar el botón Continue
                                 - Devices: Select All
                                 - Pulsar el botón Continue
                                 - Profile Name: Patri Labyrinth Development
                                 - Pulsar el botón Generate
                                 - Pulsar el botón Download
                                 - El fichero con el perfil de provisión se guarda en Descargas (se puede mover a otro directorio)


                            XCODE


                            1. Cargar el perfil de provisión de desarrollo en XCode

                                 - Conectar el dispositivo por USB
                                 - Si XCode está abierto, cerrarlo.
                                 - Doble click en el fichero Unity-iPhone.xcodeproj Se abrirá XCode
                                 - Window > Organizer
                                 - Pulsar sobre el dispositivo
                                 - Si está activo, pulsar Use for development
                                 - En el frame izquierdo seleccionar Provisioning profiles en el dispositivo conectado
                                 - Pulsar el botón + Add
                                 - Seleccionar el fichero del perfil de provisión de desarrollo que hemos generado y descargado anteriormente

                            2. Instalar el perfil de provisión de desarrollo en el dispositivo

                                 - Arrastrar el perfil de provisión al dispositivo conectado en la sección Provisioning profiles
                                 - Para confirmar que el dispositivo tiene el perfil instalado: en el dispositivo entrar en Ajustes > General > Perfil y comprobar que está el perfil que hemos instalado.

                            UNITY

                            1. Si XCode está abierto, cerrarlo

                            2. File > Build Settings…

                            3. Pulsar el botón Player Settings

                            4. En el Inspector, en la sección Other Settings, elegir SDK Version: Device SDK

                            5. Pulsar el botón Build. Elegir Replace

                            XCODE


                                 - Conectar el dispositivo por USB
                                 - Si XCode está abierto, cerrarlo.
                                 - Doble click en el fichero Unity-iPhone.xcodeproj Se abrirá XCode
                                 - Asegurarse que el dispositivo no está locked
                                 - Pulsar en Run (parecido a Play)
                                 - El programa se ejecutará en el dispositivo




                            SUBIR APLICACIÓN AL APP STORE


                            iTUNES CONNECT


                            http://itunesconnect.apple.com

                            1. Añadir la aplicación a iTunes Connect

                                 - Entrar en iTunes connect
                                 - Pinchar en Manage your Apps
                                 - Pulsar el botón Add New App
                                 - Idioma por defecto: English
                                 - App Name: Patri’s Labyrinth
                                 - SKU Number: Patri_Labyrinth_01
                                 - Bundle ID: seleccionar el de la app que generamos en Unity
                                 - Pulsar el botón continue
                                 - Elegir el precio (Free si es gratis)
                                 - Desmarcar Discount for educational institutions
                                 - Pulsar el botón Continue
                                 - Version number: 1.0
                                 - Copyright: @Quilart
                                 - Category: Games
                                 - Subcategory: elegir una
                                 - Rating: marcar None en todos
                                 - Description: …..
                                 - Keywords: …. separadas por comas
                                 - Support URL: http://joseanquiles.blogspot.com
                                 - Meter los datos en Contact Information
                                 - Sección Uploads:
                                      - Large App icon: elegir el PNG 1024x1024
                                      - 3.5 inch retina: elegir los screenshots de 3.5”
                                      - 4 inch retina: elegir los screenshots de 4”
                                      - iPad screenshots: elegir los screenshots de iPad
                                 - Pulsar el botón Save
                                 - Pulsar el botón View Details
                                 - Pulsar el botón Ready for Upload Binary
                                 - Responder No a todas las preguntas
                                 - Pulsar el botón Save
                                 - Pulsar el botón Continue

                            PORTAL DEL DESARROLLADOR


                            1. Crear un perfil de provisión para distribución

                            Portal del desarrollador de Apple: http://developer.apple.com/membercenter/index.action

                                 - Portal del desarrollador > Certificates, Identifiers & Profiles
                                 - iOS Apps > Provisioning profiles > All
                                 - Pulsar el botón +
                                 - Seleccionar App Store
                                 - Pulsar el botón Continue
                                 - App ID: Patri Labyrinth
                                 - Pulsar el botón Continue
                                 - Select Certificate: José Antonio Quiles (iOS development)
                                 - Pulsar el botón Continue
                                 - Profile Name: Patri Labyrinth Distribution
                                 - Pulsar el botón Generate
                                 - Pulsar el botón Download
                                 - El fichero con el perfil de provisión se guarda en Descargas (se puede mover a otro directorio)

                            XCODE


                            1. Cambiar settings

                                 - En el frame izquierdo seleccionar el proyecto
                                 - En la pestaña capabilities, en la sección Game Center, si aparece algún aviso, pulsar Fix issues
                                 - En la pestaña Build Settings, pulsar el botón All para ver todas las opciones
                                 - Sección code signing, Provisioning profile, elegir el perfil adecuado

                            2. Recompilar el proyecto

                                 - Product > Clean
                                 - Product > Archive (este proceso es largo)
                                 - Al final se abrirá el Organizer
                                 - Pulsar el botón Validate (esto validará la app antes de subirla) NOTA: Si diera un error "no identities were available”, entrar en el portal y descargar de nuevo el perfil de provisión de distribución, luego darle doble click para que lo coja XCode.
                                 - Pulsar el botón Distribute
                                 - Elegir Submit to the iOS App Store
                                 - Pulsar el botón Next
                                 - Elegir el perfil de distribución
                                 - Pulsar el botón Next
                                 - La aplicación comenzará a subirse al App Store

                            3. Con este termina el proceso. Ahora habrá que esperar unos 7 días a que Apple acepte la aplicación y la haga disponible en el AppStore.

                            4. En iTunes Connect se puede ver el estado de la aplicación. El primer estado será: Upload Received


                                 

                            SUBIR ACTUALIZACIÓN AL APP STORE     


                            iTUNES CONNECT


                            http://itunesconnect.apple.com

                            Nota: no actualizar una app si todavía no está en estado Ready for Sale. Es posible hacerlo, pero retrasará el proceso.

                            1. Preparar la actualización en iTunes Connect

                                 - Entrar en iTunes Connect
                                 - Manage your Apps
                                 - Pulsar sobre la app que se desea actualizar
                                 - En el apartado Versions, apuntar el número de versión actual, para generar una nueva. Por ejemplo, si la actual es la 1.0, la siguiente puede ser la 2.0
                                 - En el apartado Versions, pulsar el botón Add Version
                                 - Version number: la nueva versión (por ejemplo, 2.0)
                                 - What’s new: cambios en esta versión (en inglés)
                                 - Pulsar el botón Save
                                 - Ahora el status debería ser: Prepare for Upload
                                 - Pulsar el botón Ready for Upload Binary
                                 - Responder a todas las preguntas y pulsar el botón Continue
                                 - Seleccionar: Automatically release “App” once it has been approved
                                 - Pulsar el botón Save
                                 - Ahora el status debería ser: Waiting for Upload

                            2. Generar la nueva versión en Unity

                                 - File > Build Settings
                                 - Seleccionar plataforma iOS
                                 - Pulsar Switch Platform
                                 - Pulsar el botón Player Settings
                                 - En la sección Other Settings, poner Bundle Version = nueva versión
                                 - Pulsar el botón Build para generar el proyecto XCode
                                 - Marcar Enable replace, y pulsar el botón Replace
                                 

                            XCODE


                            1. Con el Finder entrar en el directorio del proyecto Unity/Proyecto XCode generado en el paso anterior

                            2. Si XCode está abierto, cerrarlo.

                            3. Doble click en el fichero Unity-iPhone.xcodeproj Se abrirá XCode

                            4. Cambiar settings

                                 - En el frame izquierdo seleccionar el proyecto
                                 - En la pestaña capabilities, en la sección Game Center, si aparece algún aviso, pulsar Fix issues
                                 - En la pestaña Build Settings, pulsar el botón All para ver todas las opciones
                                 - Sección code signing, Provisioning profile, elegir el perfil adecuado

                            5. Recompilar el proyecto

                                 - Product > Clean
                                 - Product > Archive (este proceso es largo)
                                 - Al final se abrirá el Organizer
                                 - Pulsar el botón Validate (esto validará la app antes de subirla) NOTA: Si diera un error "no identities were available”, entrar en el portal y descargar de nuevo el perfil de provisión de distribución, luego darle doble click para que lo coja XCode.
                                 - Pulsar el botón Distribute
                                 - Elegir Submit to the iOS App Store
                                 - Pulsar el botón Next
                                 - Elegir el perfil de distribución
                                 - Pulsar el botón Next
                                 - La aplicación comenzará a subirse al App Store

                            3. Con este termina el proceso. Ahora habrá que esperar unos 7 días a que Apple acepte la aplicación y la haga disponible en el AppStore. 

                            4. En iTunes Connect se puede ver el estado de la aplicación. El primer estado será: Upload Received en New Version (la versión anterior estará Ready for Sale)



                            martes, 15 de abril de 2014

                            Ficheros de comandos de Windows

                            Los ficheros de comandos de Windows son una forma sencilla de automatizar algunas tareas.
                            Para crear un fichero de comandos, simplemente hay que ponerle la extensión .CMD
                            Veamos ahora algunas de las cosas que se pueden hacer en estos ficheros de comandos.

                            Comandos básicos

                            @echo off
                            Desactiva la salida de todos los comandos que se van ejecutando (incluido él mismo). Se suele poner como la primera línea de un fichero de comandos.

                            echo mensaje
                            Saca un mensaje en la consola.

                            msg * mensaje
                            Saca un mensaje en una ventana (popup), pero sin detener la ejecución.

                            pause
                            Detiene la ejecución de los comandos hasta que se pulse una tecla. Muestra el mensaje "Presione una tecla para continuar . . .". Si no se desea que salga el mensaje estándar, utilizar pause>nul (sin espacios).

                            exit
                            Termina la ejecución de los comandos.

                            rem
                            Línea de comentario

                            Comandos del sistema operativo

                            cd
                            Cambia a un directorio

                            copy
                            Copia un fichero a otro fichero o lugar

                            del
                            Borra un fichero

                            move
                            Cambia el nombre de un fichero o lo mueve a otro directorio

                            cls
                            Borra la pantalla

                            type
                            Muestra el contenido de un fichero

                            Creación de ficheros de texto

                            Es posible crear un fichero con contenido textual:
                            echo línea 1 > fichero.txt
                            echo línea 2 >> fichero.txt
                            echo línea 3 >> fichero.txt

                            Variables

                            set var=valor
                            Crea una variable y le asigna un valor

                            %var%
                            Accede al valor de la variable

                            set /P var=Introduzca valor:
                            Pide al usuario que introduzca el valor de la variable

                            set /A var=%var1% + %var2%
                            Realiza una operación matemática y el resultado lo mete en la variable

                            Redirección

                            |
                            Pasa la salida de un comando a la entrada de otro comando
                            Ejemplo:
                                 ipconfig | findstr /C:"LAN"

                            ||
                            Ejecuta el segundo comando sólo si el primero falla
                            Ejemplo:
                                copy fichero1 fichero2 || echo No existe el fichero

                            >
                            Escribe la salida de un comando en un fichero
                            Ejemplo:
                                type fichero1 > fichero2

                            >>
                            Añade la salida de un comando a un fichero existente
                            Ejemplo:
                                dir >> fichero2

                            <
                            Toma como entrada de un comando el contenido de un fichero

                            Condiciones

                            if [not] string1 == string2 comando
                            Ejecuta el comando si el string1 es igual al string2

                            if [not] exist fichero comando
                            Ejecuta el comando si existe el fichero

                            Saltos

                            goto etiqueta
                            .....
                            :etiqueta


                            jueves, 3 de abril de 2014

                            Ejecución de paquetes solaris

                            Cuando se instala un paquete Solaris con el comando pkgadd, lo que se hace es procesar el fichero prototype (man -s4 prototype).
                            Este fichero consta de líneas con uno de los siguientes formatos:


                            • # comentario
                            • ! comando
                            • objeto

                            Las líneas de comentario se ignoran.
                            Las líneas de comando y de objeto se explican a continuación.

                            Líneas de comando

                            Se permiten los siguientes comandos:

                            !param=value

                            Permite definir variables que se pueden utilizar después.
                            Ejemplo:
                                    !USER=usuario2
                                    !GROUP=grupo4

                            !include

                            Para incluir otro fichero prototype

                            !default

                            Especifica el modo, usuario y grupo por defecto.
                            Ejemplo:
                                    !default 0755 $USER $GROUP

                            Líneas de objeto

                            En cada línea de objeto se especifica un fichero, directorio, etc.
                            El formato de las líneas de objeto es:

                                    type class location mode owner group

                            type

                            Especifica el tipo de objeto:
                            • f : fichero
                            • e : fichero para editar después de instalar
                            • v : fichero volátil que puede cambiar después de la instalación. Se crea con tamaño cero (por ej, ficheros de log)
                            • d : directorio
                            • i : fichero de control (pkginfo, request, depend, postinstall, preremove, etc)

                            class

                            En pkginfo se definen una serie de clases. Por ejemplo:

                                CLASSES="none parche"

                            En prototype se asigna una de estas clases a cada fichero. Estas clases indican el orden de instalación (en el ejemplo anterior, se instalarán primero los ficheros de la clase none y después los ficheros de la clase parche). En request se pueden decidir las clases a instalar.

                            location

                            Hay tres opciones para especificar un location:
                            • path absoluto: el objeto se instala en dicho path absoluto
                            • path relativo: el fichero es relocatable
                            • path1=path2: el path2 indica un fichero cuyo contenido se copiará al path1 durante la instalación
                            Un path puede contener una variable:
                            • $variable
                            • si la variable comienza con minúscula, es una variable build time
                            • si la variable comienza con mayúscula, es una variable install time (del request)

                            mode (opcional)

                            Son los permisos del objeto destino en octal

                            owner (opcional)

                            Es el usuario propietario del objeto

                            group (opcional)

                            Es el grupo propietario del objeto




                            viernes, 14 de febrero de 2014

                            Programación funcional en LUA

                            Las funciones como valores

                            En LUA, las funciones son valores con las mismas propiedades que el resto de valores (strings, números, tablas, etc). Las funciones se pueden almacenar en variables (globales o locales) y en tablas. Se pueden pasar funciones como argumentos y se pueden retornar funciones.
                            Una propiedad muy importante de las funciones en LUA es que tienen "lexical scoping". Esto significa que una función puede acceder a las variables de las funciones que le rodean. O sea, si una función f1 contiene internamente a otra función f2(), entonces todas las variables que se declaran en el scope de f1 pueden ser accedidas desde la función f2. Esto que parece algo sin importancia, se convertirá en LUA en algo fundamental para la programación funcional.
                            Las funciones en LUA son objetos anónimos, como el resto de valores, o sea, no tienen nombre. Cuando hablamos de la función print, realmente estamos hablando de una variable a la que se le ha asignado una función (valor) que muestra texto por consola.
                            Habitualmente, cuando escribimos una función, lo hacemos con la siguiente sintaxis:
                            function suma(a,b)
                                return a + b;
                            end
                            Sin embargo, esto es una forma de escribir lo que realmente estamos haciendo, que es lo siguiente:
                            suma = function (a,b)
                                return a + b;
                            end
                            Por consiguiente, una definición de función es realmente una sentencia de asignación que crea un valor de tipo "function" y lo asigna a  una variable.
                            Aunque lo normal es construir un valor "function" y asignarlo a una variable, hay ocasiones en que nos interesa construir la función y que permanezca anónima. Veamos un ejemplo.
                            La librería table ofrece una función table.sort() que recibe como argumento una tabla y ordena sus elementos. Esta función debe permitir ordenación ascendente, descendente, numérica, por fecha, etc de los elementos de la tabla. Para ello, en lugar de existir cientos de variantes de esta función, lo que se hace es pasar un segundo argumento (opcional) que es una función para comparar dos valores. Por ejemplo, supongamos la siguiente tabla de alumnos con sus notas:
                            alumnos = {
                                { nombre="Luis", nota=6.3 },
                                { nombre="Juan", nota=8.6 },
                                { nombre="María", nota=9.8 }
                            };
                            Para ordenar esta tabla por nombre de alumno:
                            table.sort(alumnos, function (a1, a2) return a1.nombre < a2.nombre; end );
                            También podemos ordenar la tabla por nombre pero de forma descendente:
                            table.sort(alumnos, function (a1, a2) return a1.nombre > a2.nombre; end );
                            Las funciones que reciben como argumento otra función se denominan high-order functions.
                            Otro ejemplo de función high-order es el cálculo de la derivada de una función matemática en un punto. La derivada de una función f() en un punto es ( f(x+d) - f(x) ) / d
                            function derivada(f, d)
                                d = d or 0.0001;
                                return function (x)
                                    return (f(x+d) - f(x)) / d;
                                end
                            end
                            De esta forma, podemos tener la función derivada de sen(x):
                            d_seno = derivada(math.sin);
                            Las funciones se pueden almacenar no sólo en variables globales, sino también en variables locales e incluso en tablas (esto dará lugar a los módulos y programación orientada a objetos en LUA).

                            Closures

                            Cuando escribimos una función dentro de otra función, la función interna tiene acceso a todas las variables de la función contenedora, incluidas las variables locales. A esta característica le llamamos lexical-scoping. Aunque esto suena bastante obvio, realmente no lo es, si lo combinamos con el hecho de que las funciones son valores como el resto de valores del lenguaje.
                            Veamos un ejemplo:
                            newCounter = function ()
                                local i = 0;
                                return function ()
                                    i = i + 1;    -- incrementa la variable local externa i
                                    return i;
                                end
                            end
                            Esta función (que se asigna a la variable newCounter) crea y retorna una función anónima que incrementa el valor de la variable i (local a la función contenedora). ¿Qué ocurre cuando utilizamos newCounter para crear nuevas funciones? Cuando invoquemos a la nueva función construida, ¿cómo accederá a la variable i?
                            Veamos un ejemplo de uso:
                            func1 = newCounter();
                            print(func1());     -- 1
                            print(func1());     -- 2
                            Lo interesante en este ejemplo es que cuando invocamos a la función construida y retornada por newCounter(), se ejecuta una función que accede a una variable que era interna a su función contenedora, la variable i. Pero entonces, esa variable i, ya ha desaparecido ¿no? Ya que era una variable local a la función newCounter() y newCounter ya no existe. ¿Que ha ocurrido?
                            LUA maneja esta situación adecuadamente, mediante el concepto de closure. Para simplificar, un closure es una función más todo el entorno que la rodea. En este caso, la función retornada por newCounter() es un closure, que incluye la propia función anónima y la variable i.
                            Por ejemplo, si creamos otra nueva función anónima volviendo a invocar a newCounter(), se vuelve a retornar un closure nuevo con otra variable i:
                            func2 = newCounter();
                            print(func2());    -- 1
                            print(func1());    -- 3
                            print(func2());    -- 2
                            Esto ocurre porque func1 y func2 son dos closures distintos, cada uno con su propia instancia de la variable i.
                            Hablando técnicamente, los valores son closures, no las funciones.
                            Si la función newCounter() recibiera por ejemplo un argumento, ese argumento pasaría al closure:
                            newIncrementer = function (inc)
                                local i = 0;
                                return function ()
                                    i = i + inc;
                                    return i;
                                end
                            end
                            En este caso, el closure incluye la función anónima, la variable i y el argumento inc.
                            Un ejemplo aclaratorio de los closures:
                            a = { };local x = 20;for i = 1, 10 do    local y = 0;    a[i] = function() y = y + 1; return x + y; end;end
                            En este ejemplo, el bucle for crea 10 closures (10 instancias de una función anónima). Cada uno de estos closures tiene una instancia de la variable y que es distinta. Sin embargo, todos los closures comparten la misma variable x.

                            Funciones en las tablas

                            Una consecuencia obvia de que las funciones (realmente closures) son valores como otro cualquiera es que podemos almacenarlas en variables locales, variables globales y en tablas.
                            Almacenar funciones en tablas es algo muy común en LUA. Por ejemplo, cuando llamamos a math.sin(2.56) estamos invocando a un miembro de la tabla math que se llama sin y es una función.
                            Para crear una tabla que tenga miembros función hacemos lo siguiente:
                            util = { };
                            util.suma = function (x, y) return x + y; end
                            util.resta = function (x, y) return x - y; end
                            De igual forma, también podemos utilizar el constructor de la tabla:
                            util = {
                                suma = function (x, y) return x + y; end,
                                resta = function (x, y) return x - y; end
                            };
                            Además, LUA ofrece una tercera alternativa para tablas con miembros función:
                            util = { };
                            function util.suma(x, y) return x + y; end
                            function util.resta(x, y) return x - y; end
                            En cualquiera de los casos, la invocación a las funciones de la tabla util sería de la siguiente forma:
                            util.suma(3, 2);
                            util.resta(4,7);

                            Funciones locales

                            Cuando almacenamos una función en una variable local, dichas funciones sólo pueden ser utilizadas en el scope en que se definen:
                            local func1 = function (a, b) return a ^ b; end
                            local func2 = function (x) return f(x, x); end
                            Esto es muy útil en los packages, donde las funciones del package (scope) pueden utilizar las otras funciones locales








                            martes, 11 de febrero de 2014

                            iOS: Certificados y perfiles de provisión

                            Introducción

                            Desarrollar programas para iOS (iPod, iPhone o iPad) tiene, además de lo que es el propio programa, otras tareas "accesorias" que a veces complican bastante el despliegue de la aplicación en dispositivos reales.
                            Apple adoptó la filosofía de que sólo Apple da permiso para que una aplicación corra en dispositivos iOS. Esto es algo bastante estricto, pero da muchas ventajas a los usuarios, ya que así se garantiza que las aplicaciones que se instalan han pasado por un control de Apple.

                            Certificados digitales de Apple

                            Pero, si sólo Apple puede autorizar aplicaciones, ¿cómo podemos desarrollar una aplicación nosotros?
                            La idea es que los desarrolladores tengan un certificado reconocido (autorizado) por Apple y que firmen las aplicaciones, de forma que los dispositivos vean que la firma está avalada por Apple.
                            Cuando un desarrollador se apunta al programa de desarrolladores de Apple, puede solicitar un certificado firmado por el propio Apple que se puede utilizar para firmar aplicaciones. Para ello, el desarrollador tiene que generar un CSR (certificate signing request) mediante la aplicación de llaveros de MAC OS.
                            La aplicación de llaveros genera automáticamente dos claves: una privada y una pública. El CSR lleva la clave pública, además del nombre y mail del desarrollador y se firma utilizando la clave pública. El CSR se envía a Apple (http://developer.apple.com). Como el CSR está firmado utilizando la clave privada, Apple se asegura que realmente viene del desarrollador.
                            Cuando Apple recibe el CSR, emite un certificado firmado por Apple. El certificado emitido por Apple contiene los mismos datos (incluida la clave pública) pero con la firma de Apple.
                            El certificado emitido por Apple (fichero .cer) lo descargamos a nuestro equipo y lo arrastramos (drag and drop) a la aplicación de llaveros.

                            Perfiles de provisión

                            Ya tenemos un certificado firmado por Apple con el que podremos firmar nuestras aplicaciones. Pero los dispositivos todavía no saben si pueden confiar en ti, y para esto surgen los perfiles de provisión.
                            Cuando creamos un perfil de provisión, lo que estamos haciendo es asociar una serie de dispositivos al certificado que hemos generado en el apartado anterior. El perfil de provisión es un fichero .provision se utiliza durante el proceso de compilación de una aplicación iOS y se despliega también en el dispositivo. Para crear un fichero de provisión hay que hacerlo en la web de desarrollo de Apple y descargarlo en el equipo. Luego se hace doble click y lo coge el Organizer de XCode.
                            Se pueden tener varios perfiles de provisión, uno para cada aplicación. Esto es lo normal.
                            En resumen, el perfil de provisión dice que el código compilado por nosotros se le permite ejecutar en una serie de dispositivos.

                            Compilación y ejecución de la aplicación

                            A la hora de compilar, lo importante es decirle a XCode que utilice nuestro certificado y el perfil de provisión.
                            Si miramos lo que contiene una aplicación compilada, podemos ver el perfil de provisión (que es una copia del original) y un directorio denominado _CodeSignature que contiene un fichero llamado CodeResources que es una lista de hashes de todos los ficheros del proyecto (realmente, firmas con nuestro certificado de todos los ficheros del proyecto).
                            Cuando se instala la aplicación, iOS hace una serie de comprobaciones: busca el fichero de provisión firmado por Apple, chequea los hashes en CodeResources utilizando la clave pública que viene en el certificado (asegurándose así que los ficheros no se han modificado durante el proceso).
                            Al ejecutar la aplicación, iOS vuelve a chequear el perfil de provisión y los hashes de todos los ficheros.

                            Para terminar...

                            Se necesitan dos certificados, uno para desarrollo y otro para distribución. Estos certificados se pueden utilizar para cualquier número de aplicaciones. Los certificados garantizan que la aplicación la ha desarrollado alguien reconocido por Apple, y además, garantizan que la aplicación no se ha modificado o corrompido.
                            El desarrollo de una aplicación para iOS tiene tres etapas:
                            1. Desarrollo
                            2. AdHoc
                            3. Distribución
                            Cada fase tiene tres partes (aunque no todas las partes se aplican a todas las etapas):
                            • Ficheros fuente
                            • Certificados
                            • Perfiles de provisión
                            Algunas de estas partes tienen elementos dentro de ellas:

                            • Nombres de desarrolladores: son los que desarrollan la aplicación y la firman
                            • UDIDs de dispositivos: son identificadores de dispositivos que se pueden utilizar para testear una aplicación (Apple permite un máximo de 100 dispositivos por desarrollador registrado).
                            • AppIDs de aplicación



                            viernes, 24 de enero de 2014

                            LUA: Lenguaje de programación

                            Qué es LUA

                            LUA es un lenguaje de programación de extensión. Lo cual significa que no es un lenguaje para escribir programas ejecutables stand-alone. En lugar de esto, LUA está pensado para integrarse dentro de un programa host. Este programa host es el que se encarga de lanzar y ejecutar el programa LUA. Además, el programa host puede declarar funciones escritas en C que pueden ser invocadas desde el programa LUA.
                            LUA ofrece soporte para los siguientes conceptos de programación:

                            • programación orientada a objetos
                            • programación funcional
                            • programación data-driven

                            El concepto de lenguaje de extensión es similar a VBA en las aplicaciones de microsoft office.
                            LUA tiene un garbage collector, con lo cual no es necesario liberar los objetos que se van creando. Pero hay que tener cuidado con referencias a objetos que se quedan para siempre en alguna variable.

                            Léxico

                            LUA tiene un léxico basado en C:
                            • Los punto y coma al final de sentencia son opcionales.
                            • Los identificadores constan de letras, números y underscore (no pueden empezar con números; tampoco se recomienda que empiecen con underscore, ya que esta notación se utiliza para identificadores internos de LUA)
                            Bloques: son una secuencia de sentencias que termina en end (funciones, bucles, if, bloques do -- end)
                            Palabras reservadas: and, break, do, else, elseif, end, false, for, function, goto, if, in, local, nil, not, or, repeat, return, then, true, until, while.
                            Operadores que son distintos a otros lenguajes:
                            • ~= distinto
                            • .. concatenación de strings
                            • ... número variable de argumentos en una función
                            Comentarios: 
                            • -- comentario de una línea
                            • --[[ comentario multilínea ]]--

                            Variables, Valores y Tipos

                            LUA es un lenguaje tipado dinámicamente, lo que significa que las variables pueden apuntar a valores de cualquier tipo y, además, pueden apuntar a valores de diferentes tipos en diferentes momentos. Por ejemplo:
                            v = 12;
                            v = "abc";
                            Tipos de datos:

                            • nil : indica que a una variable no se le ha asignado todavía ningún valor
                            • boolean : puede ser true o false
                            • number : todos los números son reales, punto flotante doble precisión
                            • string : es una cadena de caracteres
                            • function : un bloque de código con nombre
                            • table : es similar a los objetos en otros lenguajes de programación
                            La función global type() devuelve el tipo de una variable: nil, boolean, number, string, function, table.
                            Una variable que se declara sin ámbito, es accesible desde cualquier lugar del programa (posterior a su declaración).
                            Si se desea una variable con ámbito de bloque (lo cual es muy recomendable), hay que declararla con ámbito local:
                            local v = 12038;
                            Asignación a múltiples variables:
                            local x, y = 1, 2; -- x = 1; y = 2;
                             Declaración de una tabla:
                            local t = { "a", "b", "c" };
                            Acceso a los elementos de una tabla:
                            local a = t[1];  -- El primer índice de una tabla es 1 (no 0)
                            Operador longitud #:
                            local s = "test";
                            local t = { 1, 2, 3 };
                            print(#s);    -- 4
                            print(#t);     -- 3

                            Tablas

                            Simplificando, las tablas son objetos también, como los strings, números, etc.
                            Realmente, son arrays asociativos, esto es, arrays de elementos que se acceden utilizando un identificador. El identificador, también llamado clave, puede ser un índice numérico o un valor alfanumérico. Las tablas son similares a los diccionarios o tablas hash de otros lenguajes, y comparten características con los arrays. Las tablas son la estructura de datos más potente de LUA. De hecho, son la única estructura de datos que tiene LUA. Con una tabla podemos representar arrays, conjuntos, registros, listas, mapas, etc. También se pueden utilizar tablas para representar módulos y objetos (ej. io.read() es el método read() del módulo io, sin embargo para LUA, es la clave read de la tabla io).
                            La forma más simple de declarar una tabla vacía es:
                            local table = { };
                            Un array es una tabla:
                            local a = { "a", "b", "c" };
                            local v = a[2];   -- v = "b"; 
                            Una tabla un poco más complicada:
                            local t = { one = "a", two = "b", three = "c" };
                            En ambos casos se utilizan las llaves para definir los elementos de la tabla, pero en el segundo caso, se da una clave específica a cada uno de los elementos. Esta clave se puede utilizar para acceder a cada uno de los elementos de una de las siguientes maneras:
                            t["two"];
                            t.two;
                            Se pueden añadir elementos a una tabla después de creada:
                            local t = { };
                            t.property1 = "abc";
                            t["property2"] = "fer";
                            t[3] = "hola";
                            Ejemplo de creación de una tabla y cómo rellenar sus elementos:
                            local t = { };
                            for i = 1, 100 do t[i] = tostring(i) end;
                            Se está creando una tabla de 100 elementos donde cada elemento contiene un string.
                            Si se accede a un elemento que no existe, obtendremos nil:
                            print(t[200]);  -- nil
                            print(t["x"]);  -- nil 
                            print(t.x);  -- nil
                            Es importante distinguir entre t.x, t["x"] y t[x]. Los dos primeros casos son equivalentes. El último depende del valor de la variable x.
                            Para eliminar un índice de una tabla simplemente hay que hacerlo nil:
                            local t = { a = 1, b = 2 };
                            t.a = nil;

                            Se puede utilizar el operador de longitud # para obtener el número de elementos que tiene una tabla.
                            Una tabla puede contener elementos de cualquier tipo (números, strings, funciones, o incluso otras tablas):
                            local tabla = {
                                t = { prop1 = "hello" }
                            };
                            tabla.t.prop1;
                            Una tabla puede contener una función:
                            function f1()
                            end
                            local tabla = {
                                 fun1 = f1,
                                 fun2 = function() 
                                            end
                            }

                            Tablas como arrays

                            El equivalente LUA de un array es una tabla. Características de los arrays:

                            • Todos los arrays en LUA tienen el índice base en 1 (no en 0 como en muchos lenguajes de programación). 
                            • Los arrays necesitan tener un índice numérico. 
                            • Los arrays pueden ser multidimensionales, haciendo que sus elementos sean a su vez arrays.
                            • Los arrays no necesitan ser dimensionados en su declaración, ya que pueden crecer y dimensionarse en runtime.

                            Tablas como arrays asociativos (mapas)

                            El equivalente LUA para un array asociativo es una tabla. La diferencia entre un array asociativo y un array es que, en lugar de tener índices numéricos para acceder a los elementos del array, un array asociativo puede usar strings para acceder a sus elementos.
                            Se pueden mezclar en una misma tabla los dos tipos: array y array asociativo.

                            Tablas como objetos

                            Las tablas pueden usarse como objetos. Para ello podemos incluir funciones y datos en sus elementos.
                            Más adelante, en este mismo artículo, se verá la orientación a objetos en LUA.

                            Funciones

                            Una función en LUA se define con la palabra reservada function:
                            function add(n1, n2)
                                  return n1 + n2;
                            end
                            Nótese que los argumentos no tienen un tipo y por tanto se podrá pasar un valor de cualquier tipo a la función. Nótese también que no se especifica un tipo de valor retornado, y por tanto la función puede retornar cualquier valor (si no hay return dentro de la función, se retornará nil).
                            Una función puede retornar múltiples resultados:
                            function operaciones(n1, n2)
                                return n1+n2, n1 - n2, n1 * n2;
                            end

                            Y se pueden recoger sus resultados en varias variables:
                            local x, y, z = operaciones(4, 6)  -- x = 10, y = -2, z = 24
                             Una función puede aceptar un número variable de argumentos:
                            function fvar( ... )
                                print(arg[1], arg[2], arg[3] );
                            end
                             fvar(1, "a");  -- 1 a nill
                            Algunos argumentos pueden ser fijos, y otros variables:
                            function fvar2( a1, a2, ... )
                                print(a1, a2, arg[1], arg[2], arg[3] );
                            end
                            Como ya sabemos, se pueden añadir funciones a una tabla. Pero la función no tiene el concepto de self (this de otros lenguajes) y por tanto no podrá acceder a otros datos de la tabla. Para que la función sea realmente un método de la tabla la notación tiene que ser:
                            local table = { a = 3, b = 3 };
                            function table:add(n1, n2)
                                return self.a + self.b + n1 + n2;
                            end
                            Y para invocarla:

                             table:add(4, 7);  -- 3 + 3 + 4 + 7 = 17

                            Funciones globales predefinidas

                            Realmente, en LUA una función global es una variable que tiene asignada una función.

                             ipairs()

                            Permite recorrer los elementos de una tabla indexada numéricamente (no vale para tablas con identificadores).
                            for index, value in ipairs(table) do
                            end

                            pairs()

                            Permite recorrer los elementos de una tabla de cualquier tipo.
                            for index, value in ipairs(table) do
                            end

                            print()

                             Imprime en consola todos sus argumentos separándolos por TAB.

                            tostring()

                            Convierte su argumento a string.
                            Lo interesante de este método es que cuando se aplica a una tabla que tiene definida una función en su propiedad __tostring, entonces llama a esa función.
                            Ejemplo:
                            local table = { a = "hola" };
                            function table:tostring() 
                                return "a=" .. self.a; 
                            end
                             setmetatable(table, { __tostring = table.tostring } );
                            print( table );   -- a=hola

                            type()

                            Retorna, como un string, el tipo de su argumento: Boolean, string, table, etc.

                            tonumber()

                            Convierte a número el parámetro de tipo string. Si el parámetro no representa un número, entonces retorna nil.

                            pcall()

                            Cuando ocurre un error en LUA, se propaga hacia arriba, normalmente hasta el programa host que lo maneja. En LUA no existe try/catch, pero existe pcall() que hace una función similar.
                            La llamada a pcall() incluye como parámetro la función que se quiere invocar y sus argumentos. Si ocurre un error dentro la función invocada, entonces pcall() retorna false y el error ocurrido. Si todo va bien, retorna true.
                            Ejemplo:
                            function badFunction(a, b)
                                return a + b;
                            end
                            local result, error = pcall(badFunction,"1","2");

                            Estructuras de control

                            if

                            if condition1 then
                                -- condition1 = true
                            elseif condition2 then
                                -- condition2 = true
                            else
                                -- all other cases

                            for

                            for variable = start, end, step do
                                -- loop
                            end

                            while

                            while condition do
                                -- loop
                            end

                            repeat

                            repeat
                                -- loop
                            until condition;

                            Módulos

                            Cuando se escribe un programa LUA, se puede meter todo el código del programa en un único fichero main.lua, no hay problema sintácticamente hablando. Sin embargo, arquitecturalmente no es muy conveniente.
                            LUA ofrece el concepto de módulos para organizar mejor el código.
                            Desde el punto de vista de usuario, un módulo es un trozo de código LUA que puede ser cargado a través de require y que crea y retorna una tabla. Todo lo que el módulo exporta (funciones, constantes etc) se define dentro de esta tabla, la cual funciona como un namespace.
                            Por ejemplo, la librería estándar math es un módulo y se puede usar de la siguiente forma:
                            local m = require("math");
                            m.sin(2.4);
                            Sin embargo, el intérprete LUA precarga todas las librerías estándar con un código parecido al siguiente:
                            math = require("math");
                            string = require("string"); 
                            ...
                            Con lo cual, podremos utilizar las librerías estándar como es habitual: math.sin(2.4);
                            Cada módulo sólo se carga una vez. Si más adelante, en otra parte del programa, se llama a require para volver a cargar un módulo ya cargado, LUA no lo carga, simplemente retorna la tabla que ya se ha cargado anteriormente. Si el módulo no se ha cargado anteriormente, require busca un fichero .lua con el nombre del módulo y lo carga.
                            Si un módulo lua se encuentra en un subdirectorio con respecto a main.lua, entonces la forma de cargarlo es la siguiente:


                            m = require("subdir.modulo");  -- carga modulo.lua que está en el directorio subdir/modulo.lua
                            La tabla package.loaded contiene todos los módulos cargados:
                            package.loaded.

                            La forma más sencilla de crear un módulo es la siguiente:

                            1. creamos un fichero .lua con el nombre del módulo
                            2. creamos una tabla local vacía
                            3. ponemos en la tabla todas las funciones que queremos exportar
                            4. retornamos la tabla

                            Ejemplo utils.lua:
                            local t_utils = { }; 
                            function utils.add(n1, n2) return n1 + n2; end; 
                            return t_utils;
                            Un ejemplo para manejar números complejos en LUA, fichero complex.lua:

                            local c = { }; 
                            -- Creación de un número complejo
                            function c.new (pr, pi) return { r = pr, i = pi }; end;
                             
                            -- La constante i
                            c.i = c.new(0, 1);
                             
                            -- Operaciones con números complejos
                            function c.add(c1, c2) return c.new(c1.r + c2.r, c1.i + c2.i); end;
                            function c.sub(c1, c2) return c.new(c1.r - c2.r, c1.i - c2.i); end; 
                            function c.mul(c1, c2) return c.new(c1.r * c2.r - c1.i * c2.i, c1.r * c2.i + c1.i * c2.r); end; 
                            local function inv(c) local n = c.r^2 + c.i^2; return c.new(c.r / n, -c.i / n); end; 
                            function c.div(c1, c2) return c.mul(c1, inv(c2)); end; 
                            function c.tostring(c) return "(" .. c.r .. "," .. c.i .. ")"; end; 
                            -- retornar la tabla
                            return c;
                            Utilización de este módulo:

                            local complex = requires("complex"); 
                            local c1 = complex.new(3,4); 
                            local c2 = complex.new(-3,6); 
                            local c3 = complex.div(c1, c2); 
                            print(complex.tostring(c3));

                            Metatablas

                            LUA tiene un conjunto de operaciones que son predecibles: podemos sumar dos números, concatenar dos strings, insertar una clave-valor en una tabla, etc. Sin embargo, no podemos sumar dos tablas o comparar dos funciones, por ejemplo. A menos que usemos metatablas.
                            Las metatablas nos permiten cambiar el comportamiento de un tipo de datos cuando se enfrenta a una operación no definida para ese tipo (recordemos que cada tabla es un tipo de datos distinto). Cuando intentamos sumar dos tablas, t1 + t2, LUA mira si alguna de ellas tiene metatabla y si la metatabla contiene la el miembro __add (que tiene que ser una función, llamado metamétodo en este caso). Si existe __add, LUA lo invoca para calcular la suma.
                            Cada tabla tiene su propia metatabla, pero los valores de otros tipos, números, strings, etc tienen una única metatabla para todos los valores de ese tipo.
                            Cuando se crea una nueva tabla, no tiene metatabla asociada. Podemos asociar una metatabla a una tabla mediante la siguiente función:
                            setmetatable(table, metatable);
                            En LUA sólo  podemos cambiar las metatablas de las tablas, no las metatablas de los demás tipos.

                            El metamétodo __tostring

                            Un uso típico de las metatablas es redefinir la función tostring() de una tabla. La función print(tabla) invoca a tostring(tabla), lo cual por defecto imprime algo que no es muy útil: 0x4724350. La función tostring() chequea si la tabla tiene una metatabla con la función __tostring. Así, para redefinir el comportamiento de tostring() para una tabla:
                            tabla =  { };
                            metatabla = { };
                            metatabla.__tostring = function(t)
                                return ".....";
                            end
                            setmetatable(tabla, metatabla);

                             El metamétodo __index

                            Como sabemos, si accedemos a un índice (o clave) de una tabla que no existe, el resultado es nil. Este es el comportamiento por defecto. Sin embargo, si la metatabla tiene un método denominado __index, en caso de no encontrar el índice, se invoca este metamétodo y se retorna su resultado.

                            Programación orientada a objetos en LUA

                            En LUA, una tabla es un objeto. Igual que los objetos, las tablas tienen estado. Como los objetos, las tablas tienen identidad, self.Y también, como los objetos, las tablas tienen un ciclo de vida.
                            Veamos otros aspectos de las tablas/objetos:

                            Operaciones

                            La siguiente definición crea una nueva función y la almacena en el campo retirar del objeto Account:
                            Account = { balance = 0 };
                            function Account.retirar(v) Account.balance = Account.balance -  v; end;
                            A esto casi se le podría llamar método de un objeto, pero no es exactamente, ya que utiliza dentro de la función a la variable global Account. Por tanto, sólo funcionará para ese objeto concreto.
                            Una primera solución es pasar como parámetro a la función el objeto sobre el que aplica:
                            function Account.retirar(self, v) self.balance = self.balance - v; end;
                            En los lenguajes orientados a objetos, self (o this) se suele ocultar en las llamadas y se pasa implícitamente. Esto también se puede hacer en LUA utilizando : en lugar de .
                            Account =  { balance = 0 }; 
                            function Account:retirar(v) self.balance = self.balance - v; end;
                            function Account:ingresar(v) self.balance = self.balance + v; end; 
                            a = Account;
                            a:ingresar(200); 
                            a:retirar(100);
                            El siguiente problema es cómo crear objetos del mismo tipo, o sea, cómo definir clases.
                            ¿Cuál es la diferencia entre invocar a una función con . y con : ? Cuando usamos . le estamos diciendo a LUA que la función es un miembro de la tabla, del objeto tabla. Cuando usamos : le estamos diciendo a LUA que pase un parámetro oculto a la función que es el propio objeto, o sea, self.
                            Veamos un ejemplo sencillo:
                            a = { };
                            a.move1 = function (self, v)
                                print("self = ", self, " v = ", v);
                            end
                            function a:move2(v)
                                print("self = ", self, " v = ", v);
                            end
                            Ejemplos de uso:
                            a.move1(10);  -- self = 10 v = nil
                            a.move1(a,10);  -- self = 0x3457563 v = 10
                            a:move1(10);   -- self = 0x3457563 v = 10
                            a:move2(10);   -- self = 0x3457563 v = 10
                            a.move2(10);    -- self = 10 v = nil

                            Clases

                            Una clase es como un molde para crear objetos. Todos los lenguajes orientados a objetos ofrecen el concepto de clase. Pero LUA no. Sin embargo, es fácil emular este comportamiento. La idea es crear un objeto que sirva como prototipo para crear otros objetos:
                            Account = { balance = 0 }; 
                            function Account:new()
                                obj = { };
                                setmetatable(obj, self);
                                self.__index = self;
                                return obj;
                            end;
                            function Account:retirar(v)
                                self.balance = self.balance - v;
                            end;
                            obj1 = Account:new();
                            obj2 = Account:new();
                            obj1:retirar(10);