Compilar aplicaciones de Titanium Mobile desde línea de comandos

Titanium

Cada uno tiene sus preferencias a la hora de editar código. Por ejemplo, para utilizo Netbeans, para html Textmate, …

Para Mobile prefiero Textmate, no me hago al Studio.

Gracias a  Matt Apperson ahora disponemos de un sencillo script que nos permitirá compilar, ejecutar en el simulador y hacer la distribución de nuestras aplicaciones desarrolladas en Titanium Mobile, y este es MakeTI.

Una vez instalado en el raiz de nuestro proyecto podremos ejecutarlo en el simulador con un simple

  1. make run

O compilarlo con otro simple:

  1. make deploy

Todas las opciones y ayuda están en la página del proyecto en : MakeTi

Aplicaciones Facebook sobre SSL

mensaje https facebook

Hace unos días Facebook ha empezado a mostrar mensajes de error cuando se accedía a aplicaciones desde https, estando disponible para los desarrolladores, en la configuración de la aplicación, dos nuevos campos, para indicar las urls seguras (sobre https).

Para evitar esto debemos, claramente, añadir soporte https a nuestra aplicación, algo que si bien es sencillo, nos dará más que un quebradero de cabeza. Esto se debe a que nos encontraremos con los nuevos y vitales “bugs” con los que Facebook nos premia a todos los desarrolladores. [Leer más...]

Optimizar las aplicaciones de Facebook más allá del código

Red NetLab

El de aplicaciones para Facebook nos requiere una especial optimización de estas para que funcionen en base a dos premisas muy importantes: Seguridad y rapidez.

La rapidez depende de muchos factores, desde la optimización del código hasta la plataforma sobre la que se ejecuta esta, es por esto último que todas las aplicaciones que estamos haciendo son ejecutados con varios servidores con funciones muy específicas.

- Balanceador de carga: Con una conexión a Internet de 100MB/s con un nginx para esta función.
- Servidores de Aplicaciones: Todos son replicas que se crean bajo demanda, permitiendo siempre dar una calidad de servicio óptima independientemente de la carga.
- Servidores de Datos: Los datos están centralizados en estos servidores, que también están replicados. Los datos están, dependiendo de la aplicación en diferentes servidores de bases de datos: SQL (MySQL, PostgreSQL), noSQL ( MongoDB, Membase ).

Las aplicaciones utilizan además un sistema optimizado de caché basado en un sistema de varios nodos de Memcache, reduciendo la carga de consultas a base de datos y a el API de Facebook.

La conectividad es también muy importante, de ahí que la red de Servidores de Aplicaciones tiene una conexión de 100MB/s a internet sólo para acceder a la API de Facebook, y la conectividad interna de todo el ecosistema es con una red GigaEthernet.

Las copias de seguridad se hacen con otra conexión de 100MB al servicio S3 de Amazon.

Evitando de esta forma encontrarnos con “cuellos de botella” en la red.

¿ Lentitud en IIS con FastCGI y PHP ?

Mediante FastCGI para IIS podemos tener soporte para sobre el servidor de Microsoft para Windows (IIS).

No es la mejor opción para servir aplicaciones en PHP pero, cuando los requisitos de servidor son estos, no queda otro remedio.

Estos días he estado realmente ocupado en el de de una plataforma de eCommerce sobre esta plataforma. Para mejorar el rendimiento se utiliza membase (algo más que un servidor de Memcache para windows), así como un sistema de datos híbrido: MySQL y MongoDB, Haanga como sistema de plantillas y como buscador una modificación del Zend_Search_Lucene.

Todo parecía ir bien hasta que empezamos a ver una extraña lentitud en algunas peticiones, por lo que, se revisaron las consultas, dando la más pesada (en el MySQL slow query log) 2 segundo, algo por debajo de los 20 segundos que tardaban algunas páginas, sin complejidad aparente.

No había una carga de red grande, ni un número de peticiones considerable simultáneas, pero había momentos de extrema lentitud.

No encontrando el origen, y comprobado que en el servidor de desarrollo y pre-producción, con los mismos datos, no se producían esos problemas, acabamos añadiendo, en producción, diferentes error_log() en zonas concretas para afinar el origen del problema.

Cada vez que se veían los logs, observábamos pautas muy diferentes, los tiempos eran aparentemente aleatorios, unas veces eran antes de una consulta sencilla, otras en el momento de renderizar la plantilla, otras a la hora de buscar un valor en la caché … aparecían en cualquier sitio y en cualquier momento, haciendo la web completamente inoperativa.

Una vez se descartaron los diferentes orígenes: MongoDB, Consultas SQL, Caché en memoria … me fijé en que los logs eran erráticos, no los veía como en un Unix con un tail -f, sino que los veía a saltos.

¿ Y si este era el problema ?

Efectivamente, mi suposición fue tan simple como: Con FastCGI se lanzan multiples procesos php5-cgi.exe que van recibiendo el código php y lo procesan, y si algo tiene Windows con los ficheros, especialmente NTFS, es una gran dificultad para poder añadir información al final de un fichero desde múltiples procesos. En este caso los simples error_log() que se lanzaban, provocaban bloqueos entre los distintos procesos FastCGI.

Sólo eliminando todos los error_log y grabaciones similares a archivos, y pasándolo a base de datos y todo volvió a la normalidad, velocidad correcta.

En resumen: Si no queda otra que utilizar un IIS con FastCGI + PHP, bajo ningún concepto utilizar ningún fichero para logs, o funciones como error_log() (que hacen lo mismo) ya que nos encontraremos con bloqueos de los diferentes procesos a la hora de acceder a ese archivo y añadir la información. En teoría grabar un log, básico, en Base de datos es menos eficiente que hacerlo en texto puro en un archivo, pero en Windows no tendremos más remedio que resolverlo así.

Nota: Desconozco si apache (mod_php) en windows tiene este problema.

He buscado en Internet alguna información sobre este problema, pero no he encontrado absolutamente nada.

Implementar Haanga como sistema de plantillas para OpenCart

haanga

OpenCart es una plataforma de comercio electrónica hecha en , con un impecable, 100% MVC. Como prueba de concepto he reescrito el sistema de plantillas de OpenCart a Haanga (plantillas “Django” para PHP, über eficiente) de César Rodas.

¿ Por que Haanga ?, simplemente porque me gusta.
¿ Por qué OpenCart ?, simplemente porque me gusta como está desarrollado.

Implementación, he creado una clase TemplateEngine con el siguiente código:

  1.  class TemplateEngine {
  2.    public $template = null ;
  3.    public $default = array();
  4.    public $config = array();
  5.  
  6.    function __construct( $templateDir = null  ) {
  7.     $this->loadDefaults( $templateDir);
  8.    }
  9.    
  10.  
  11.    protected function loadDefaults( $templateDir) {
  12.     $this->default = array();
  13.      $this->config = array(
  14.     'template_dir' => (( is_null( $templateDir )) ? Settings::getInstance()->getValue('Web.root') .'' : $templateDir),
  15.     'cache_dir' =>  Settings::getInstance()->getValue('Web.root'). 'cache.templates',
  16.     'compiler' => array(  
  17.      'if_empty' => FALSE,
  18.      'autoescape' => FALSE,
  19.      'strip_whitespace' => TRUE,
  20.       'allow_exec'  => TRUE,
  21.      'global' => array( ),
  22.      'use_hash_filename' => FALSE  
  23.     )
  24.    );
  25.    }
  26.  
  27.   function loadEngine() {
  28.    // incluimos  
  29.     require_once Settings::getInstance()->getValue('Web.root') . 'lib/vendors/Haanga.php';
  30.      Haanga::configure($this->config);
  31.  
  32.  
  33.  
  34.   }
  35.     function loadTemplate( $name ) {
  36.     $this->template =   $name  ;
  37.    }
  38.     function display( $vars = array()) {
  39.     $this->loadEngine();
  40.      $vars = array_merge( $this->default, $vars);
  41.      Haanga::Load( $this->template , $vars);
  42.    
  43.    }
  44.  
  45.  }

Ahora sólo queda indicarle al que haga uso de esta clase:
en /system/engine/controller.php

Modificando los métodos render y fetch, renombrando el antiguo fetch a __fetch

  1. []
  2. protected function render($return = FALSE) {
  3.   foreach ($this->children as $child) {
  4.    $action = new Action($child);
  5.    $file   = $action->getFile();
  6.    $class  = $action->getClass();
  7.    $method = $action->getMethod();
  8.    $args   = $action->getArgs();
  9.  
  10.    if (file_exists($file)) {
  11.     require_once($file);
  12.  
  13.     $controller = new $class($this->registry);
  14.    
  15.     $controller->index();
  16.     $this->data[$controller->id] =  $controller->output;
  17.    
  18.     $this->addToModule( $controller->id, $controller->output );
  19.     } else {
  20.     exit('Error: Could not load controller ' . $child . '!');
  21.    }
  22.   }
  23.  
  24.   if ($return) {
  25.    return $this->fetch($this->template);
  26.   } else {
  27.    $this->output = $this->fetch($this->template);
  28.   }
  29.  }
  30.  
  31.   protected function addToModule( $module , $output ) {
  32.    $i = 0 ;
  33.    if ( !isset( $this->data['modules'] )) { return ;}
  34.    foreach( $this->data['modules'] as $item ) {
  35.     if ( $item['code'] == $module ) {
  36.      $this->data['modules'][$i]['output'] = $output;
  37.      return ;
  38.    
  39.     }
  40.     $i++;
  41.    }
  42.  
  43.   }
  44.  
  45.   protected function fetch($filename) {
  46.    if ( substr( $_SERVER['REQUEST_URI'], 0, 7) == '/admin/') {
  47.     return $this->__fetch( $filename);
  48.    
  49.    } else {
  50.           ob_start();
  51.        $this->templateEngine->loadTemplate( $filename );
  52.        $this->templateEngine->display( $this->data);
  53.      $content = ob_get_contents();
  54.         ob_end_clean();
  55.         return $content;
  56.         }
  57.     }
  58. protected function __fetch($filename) {
  59.   $file = DIR_TEMPLATE . $filename;
  60.       if (file_exists($file)) {
  61.    extract($this->data);
  62.     ob_start();
  63.      
  64.      require($file);
  65.      
  66.      $content = ob_get_contents();
  67.  
  68.         ob_end_clean();
  69.  
  70.         return $content;
  71.      } else {
  72.         exit('Error: Could not load template ' . $file . '!');
  73.      }
  74.  }
  75.  
  76. []

Para evitar utilizar Haanga en la administración se comprueba que la url no sea de la administración, de ser así se utiliza el motor de plantillas original.

  1. []
  2.    if ( substr( $_SERVER['REQUEST_URI'], 0, 7) == '/admin/') {
  3.     return $this->__fetch( $filename);
  4. []

Una de las particularidades de las plantillas es que hacen uso de $$variable a la hora de mostrar la salida de los módulos, y es por esto (imposible, por lo que parece, en Haanga) que he añadido un método y comprobación para que cada módulo devuelva a la plantilla su salida en un elemento “output” de la matriz y es el que mostraremos.

Así, al final, una plantilla como la de la visualización de las categorías queda en algo como los siguientes ejemplos:
/catalog/view/theme/default/common/column_right.tpl

  1. <div id="column_right">
  2.  
  3.  {% for module in modules %}
  4.  
  5.     {{ module.output }}
  6.  
  7.   {% endfor %}
  8.  
  9. </div>

/catalog/view/theme/default/product/category.tpl

  1. {% extends "layout/default.html" %}
  2. {% block content %}
  3. <div id="content">
  4.   <div class="top">
  5.     <div class="left"></div>  
  6.     <div class="right"></div>
  7.     <div class="center">
  8.       <h1>{{ heading_title }}</h1>
  9.     </div>
  10.   </div>
  11.   <div class="middle">
  12.     <table style="padding-bottom:10px;">
  13.    <tr>
  14.         {% if thumb %}
  15.         <td><img src="{{ thumb }}" alt="{{ heading_title }}" /></td>  
  16.  {% endif %}
  17.         {% if description %}
  18.       <td>{{ description }}</td>
  19.  {% endif %}
  20.    </tr>    
  21.  </table>
  22.  
  23.  {% if  !categories  &&   !products %}<div class="content">{{ text_error|default:"" }}</div>{% endif %}
  24.      
  25.     {% if categories %}
  26.  
  27.    <table>
  28.     {% for category in categories %}
  29.      
  30.    
  31.           <a href="{{ category['href'] }}"><img src="{{ category['thumb'] }}" title="{{ category['name'] }}" alt="{{ category['name'] }}" style="margin-bottom: 3px;" /></a><br />
  32.           <a href="{{ category['href'] }}">{{ category['name'] }}</a>
  33.  
  34.     </table>
  35.     {% endfor %}
  36.  
  37.  {% endif %}
  38.  
  39.  
  40.  
  41.     {% if products %}
  42.     <div class="sort">
  43.       <div class="div1">
  44.         <select name="sort" onchange="location = this.value">
  45.          {% buffer sort_order %}{{sort}}-{{order}}{% endbuffer %}
  46.  
  47.          {% for sort  in sorts %}
  48.            <option value="{{ sort['href'] }}" {% if sort_order == sort['value'] %} selected="selected"{% endif %}>{{ sort['text'] }}</option>
  49.           {% endfor %}
  50.         </select>
  51.       </div>
  52.       <div class="div2">{{ text_sort }}</div>
  53.     </div>
  54.    
  55.    
  56.    {% inline "elements/lista_productos.html" %}
  57.    
  58.      
  59.      
  60.     <div class="pagination">{{ pagination }}</div>
  61.  
  62.  {% endif %}
  63.  
  64.   </div>
  65.   <div class="bottom">
  66.     <div class="left"></div>
  67.     <div class="right"></div>
  68.     <div class="center"></div>
  69.   </div>
  70. </div>
  71. {% endblock %}