Monthly Archives: March 2008

Funciones básicas de Prototype

Para incluír Prototype en las páginas web se debe agregar la siguiente línea a la sección de head, reemplazando la ruta src con la adecuada.

La función $() reemplaza a la expresión document.getElementById del JavaScript estándar revisando el árbol de elementos DOM y retornando una versión ampliada del elemento en cuestión. Esta función recibe como parámetro la etiqueta del ID del elemento o directamente la referencia al objeto DOM para obtener su versión ampliada. Más información.

a = document.getElementById('seccion_1');         // JavaScript convencional.
b = $('seccion_1');     // Estilo Prototype.
a.style.display = 'none';    // JavaScript convencional.
b.hide();     // Gracias a la extensión de los elementos que provee Prototype.

Esta función puede utilizarse con varios parámetros en un único llamado.

secciones = $('seccion_1', 'seccion_2', 'seccion_3');

Esto equivale a decir lo siguiente.

secciones = [];
secciones[0] = $('seccion_1');secciones[1] = $('seccion_2');
secciones[2] = $('seccion_3');

La función $w() convierte cadenas con palabras separadas por espacios en arreglos cuyas celdas corresponden a estas palabras. Más información.

numeros = $w('uno dos tres cuatro');

El contenido de numeros será entonces ['uno', 'dos', 'tres', 'cuatro'].

La función $$() realiza búsquedas basadas en los selectores de CSS, el resultado de esta función será siempre un arreglo toda vez que la búsqueda podrá arrojar múltiples resultados. Más información.

$$('div')     // Todos los divs.
$$('#seccion_1')     // Igual que $('seccion_1') pero en forma de arreglo.
$$('li.resaltado')     // Todos los LI que tengan la clase 'resaltado'.
$$('#seccion_1 p')     // Todos los P dentro de seccion_1.

Existen múltiples opciones para esta función flexible, para consultar mas información al respecto revise el API correspondiente.

La función $A() convierte (promueve a) los elementos estilo colección (o arreglo) en objetos Array de Prototype. La ventaja de utilizar este método es que enriqucerá el resultado con los método de la clase Array como each, indexOf y toJSON, facilitando el procesamiento de la información contenida en él. Más información.

Esta función opera bajo las siguientes premisas:

  • Si el valor es null, undefined o false, el arreglo resultante es vació.
  • Si el objeto presenta el método toArray() entonces este método es utilizado.
  • De lo contrario los valores del objeto son recorridos y manipulados de la manera convencional.

La función $F() toma un elemento campo de un formulario, ya sea el objeto DOM o su respectivo ID y retorna el valor contenido en el campo. Es una versión mejorada de la sentencia document.getElementByID('elemento').value. Más información.

Esta función opera bajo las siguientes premisas:

  • Para las listas de selección múltiple se obtiene un arreglo con cada uno de los valores selccionados.
  • Para los checkboxes y radiobuttons no seleccionados se obtiene null.
  • Para cualquier otro elemento se obtiene una cadena de texto con el valor del campo.

La función $H() convierte (promueve a) un hash de JavaScript un objeto Hash de Prototype con sus consiguientes métodos ampliados. Más información.

opciones = $H({color: 'rojo', sabor: 'salado', tipo: 'alimento'});
opciones.keys();    // Retorna ['color', 'sabor', 'tipo'].
opciones.values();    // Retorna ['rojo', 'salado', 'alimento'].

La función $R() crea objetos rango (ObjectRange) de Prototype con sus respectivos métodos y usos útiles. Más información.

$R(1, 10).each(function(i)
{
// Se ejecuta 10 veces iterando a i desde 1 hasta 10.
});
$A($R(1, 10));    // Retorna el arreglo [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].

Proceso de Desarrollo

Proceso de Desarrollo

Instalación de redmine 0.6.4 en un host compartido

Desde hace unos días tenía ganas de instalar un software estilo forge (SourceForge, GForge, Trac, entre otros) para el control y registro de los proyectos en la Fundación donde paso la mayor parte del tiempo.Inicialmente estuve probando dotNetProject, sin embargo se veía complejo (muchas opciones) y me pareció que se centraba mas en el control del tiempo y de las actividades que en otras cosas mas técnicas que era lo que yo busco. Me interesa en este momento poder llevar el control de solicitudes (tickets) y errores reportados (bugs) a los diferentes proyectos y que se integre con el control de versiones del código para llevar la historia (time line) del desarrollo del proyecto, ojalá documentando los esfuerzos realizados por los ingenieros desarrolladores. Si a esto se le puede agregar el manejo de archivos y de documentación incluyendo un Wiki pues mucho mejor.

Algo muy similar a mis requerimientos fue lo que encontré en Trac, software muy utilizado por varios proyectos importantes opensource. Estaba muy interesado en instalarlo y probarlo cuando me di cuenta que está desarrollado en Python. Me preocupó que nunca había instalado nada en Python y menos para web, sin embargo eso nunca será un problema porque me divierte intentar cosas nuevas.

Sin embargo mientras revisaba información acerca de la instalación de este software encontré varias referencias positivas de otro similar que me empezó a llamar la atención: Retrospectiva. Este en cambio está desarrollado en Ruby on Rails. Igual situación, nunca había instalado nada en RoR pero todo se puede hacer en esta vida, nada es imposible.

Y nuevamente sucedió. Revisando documentación acerca de este software me llamó la atención otro mas con muchas características interesantes: Redmine. También hecho en RoR terminó por gustarme mas que el resto así que me decidí instalarlo para probarlo lo más rápido posible antes de que encontrara otro :-P No se nada de RoR pero he estado aprendiendo un poco de Ruby y esto me dió más ánimos.

Pensé que si tanta gente habla tan bien de RoR debe ser fácil de instalar … error. No se si fue porque era la primera vez que instalaba un software desarrollado con este framework pero me pareció difícil y poco intuitivo. Seguí inicialmente las instrucciones y sugerencias generales que encontré después de una revisión rápida y sólo logré que apareciera un mensaje diciendo: "Application error, Rails application failed to start properly". Si, claro, con gusto yo lo arreglo, pero dígame donde le duele ? Qué no le funciona ? Nada, no mas mensajes, cero logs.

Descubrí que era posible ejecutar el controlador frontal (public/dispatch.fcgi) desde línea de comando y pude ver que se generaba por consola una página de error 500. Esto por lo menos provocó que se generar registros (logs) y tuviera que investigar. El mensaje de error no ayudó mucho: "The error occurred while evaluating nil.downcase". Por lo que a mi respectaba era un error de la aplicación que intentaba llamar un método en un objeto sin instanciar, sin embargo la búsqueda en Internet de este error sugirió que se podía deber a múltiples causas, la principal de ellas a una mezcla de versiones de software entre Ruby, Rails y librerías de Gems.

Todo pintaba mal. Sin embargo después de un día de trabajo (de 24 horas), mucha paciencia, muchos experimentos y consultas en Internet lo pude hacer funcionar :-o Ahora tengo que tratar de sacar un tiempo para configurarlo completamente (integración con SVN), jugar con él y concluir si me es útil para mis labores como desarrollador de software.

A continuación detallo los pasos que seguí para realizar la instalación del software.

Descargué la distribución mas reciente de RubyForge, en este momento la 0.6.4.

$ wget http://rubyforge.iasi.roedu.net/files/redmine/redmine-0.6.4.zip

Se descomprimió y la renombre $INSTALL_HOME. Según parece las aplicaciones RoR se instalan por fuera del árbol público de directorios compartidos a través de web (alias public_html) y sólo debe hacerse público el directorio homónimo: /public. Para esto instalo el software en $INSTALL_HOME y después hago un enlace dinámico a /public.

$ unzip $HOME/redmine-0.6.4.zip

$ mv $HOME/redmine-0.6.4.zip $HOME/$INSTALL_NAME

$ cd $HOME/$INSTALL_NAME

Realicé la configuración de la base de datos.

$ cp $HOME/$INSTALL_NAME/config/database.yml.example $HOME/$INSTALL_NAME/config/database.yml

$ vi $HOME/$INSTALL_NAME/config/database.yml

production:
adapter: mysql
database: DATABASE_NAME
host: DATABASE_HOST
username: DATABASE_USERNAME
password: "DATABASE_PASSWORD"

Se solicitó la creación de las tablas en la base de datos.

$ rake db:migrate RAILS_ENV="production"

Y de la inserción de los datos iniciales.

$ rake redmine:load_default_data RAILS_ENV="production"

Se organizó el script que actúa como controlador frontal de la aplicación. Nótese que se eligió la versión FastCGI (fcgi) lo cual debe -en Dreamhost- activarse para el dominio específico donde se está trabajando, de lo contrario deberá utilizarse la versión CGI la cual es increiblemente lenta.

$ cp $HOME/$INSTALL_NAME/public/dispatch.fcgi.example $HOME/$INSTALL_NAME/public/dispatch.fcgi

$ dos2unix $HOME/$INSTALL_NAME/public/dispatch.fcgi

Se establecieron las reglas de ModRewrite para redireccionar todo el tráfico web a través del controlador frontal definido anteriormente. Nótese que esta regla se deja de última ya que inicialmente la tenía cerca al comienzo y estaba apareciendo la página pero fallaba el acceso a los contenidos de javascripts/ y stylesheets/ entre otros bajo /public ya que estos segmentos estaban siendo pasados al controlador como si fueran solicitudes de otros controladores y acciones.

$ vi $HOME/$INSTALL_NAME/public/.htaccess

SetEnv RAILS_ENV production
RewriteEngine On
RewriteRule ^$ index.html [QSA]
RewriteRule ^([^.]+)$ $1.html [QSA]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ dispatch.fcgi [QSA,L]
ErrorDocument 500 "Application errorRails application failed to start properly"

Como mencioné inicialmente, sólo /public debe poderse acceder a través de una petición web, por eso desde el directorio web del dominio ($DOMAIN_NAME) que en Dreamhost se ubica en $HOME y tiene el nombre del [sub]dominio creo un enlace que servirá como punto de acceso al sitio y apunta a la carpeta pública.

$ ln -s $HOME/$INSTALL_NAME/public $HOME/$DOMAIN_NAME/redmin

Se especifican los permisos de archivo necesarios para la ejecución del FCGI y se verifica la ejecución del mismo.

$ chmod 755 $HOME/$INSTALL_NAME/public/

$ chmod 755 $HOME/$INSTALL_NAME/public/dispatch.fcgi

$ $HOME/$INSTALL_NAME/public/dispatch.fcgi

Se remueve el archivo de distribución.

$ rm $HOME/redmine-0.6.4.zip

Y listo, software instalado! Se puede acceder al sitio a través de un navegador visitando $DOMAIN_NAME/redmine.

En caso de problemas tenga en cuenta estas sugerencias.

  • Verifique el funcionamiento y salida estándar de la ejecución directa por linea de comando del controlador frontal.$ $HOME/$INSTALL_NAME/public/dispatch.fcgi
  • Revise los logs del framework que se ubican en $HOME/$INSTALL_NAME/log. Es posible que no se estén generando durante el acceso web, sin embargo si se generarán si ud. realiza la sugerencia anterior.
  • Gracias al módulo WEBrick de Ruby es posible iniciar un servidor web independiente (puerto 3000) para verificar la instalación. Es útil para verificar que se va por buen camino, sin embargo no es la verificación final porque en mi caso el sitio se podía acceder a través de este servidor pero no a través del puerto 80 convencional debido a que tenía problemas de permisos de archivos y en el archivo .htaccess.$ ruby script/server -e production

Esta última sugerencia debe hacerse con cuidado y rapidez ya que Dreamhost detiene el servidor unos segundos después de iniciado y le envían a uno un correo regañadolo por incumplir los términos del contrato :-(

Our automated system has come across the following activity under your account that is in violation of our Terms of Service.
We request that you take any necessary action to cease the activity as quickly as possible or we will be forced to take further action. If
you were not aware of any such activity, please feel free to contact support and we can look into the issue further.

User fdq_usr running "ruby script/server -e production". This process is against our policies.

This appears to be a Denial of Service attack script

Enlaces: Actualización a Redmine 0.73.

CatalogoArchivos 0.1

Screenshot CatalogArchivos
Este script genera el catálogo de una carpeta determinada por el usuario especificando el nombre de las carpetas y de los archivos contenidos incluyendo su extensión y última fecha de modificación. Los archivos se colorean según unas extensiones predefinidas.
La finalidad de este simple programita era la de utilizar el lenguaje Ruby del cual he estado leyendo un poco en esta semana de reflexión y evitar que un humano hiciera este trabajo manualmente en el lugar donde trabajo actualmente :-P
Para ejecutar esta aplicación es necesario tener instalado el runtime de Ruby, descargar el script de la sección de enlaces y ejecutar el siguiente comando:

$ ruby CatalogoArchivo.rb <ruta orígen> <ruta destino> [titulo]

Donde CatalogoArchivo.rb es el script descargado y ubicado en la carpeta actual, ruta origen es la carpeta raíz desde la cual se realizará el catálogo de los archivos, ruta destino es la ubicación del archivo HTML donde se guardará el resultado y título (opcional) es el mensaje que aparecerá en la parte superior del reporte.

Actualizado a la versión 0.1.1 que soluciona ciertos bugs encontrados recientemente: problemas con la portabilidad respecto a los separadores de path y problemas con la clase DateTime.

Enlaces:

Funciones anónimas en PHP

Al parecer PHP todavía no soporta funciones anónimas. Hoy necesitaba una. Iba a hacer un usort de un arreglo cuyas celdas eran a su vez otros arreglos, motivo por el cual no me servía un sort normal.

Inicialmente hice lo siguiente …

usort($datos['opciones'], "ordenar_opciones_menu");

Y poner el código de la función por fuera de mi clase de Controlador.

function ordenar_opciones_menu($a, $b)
{
    return strcmp($a['titulo'], $b['titulo']);
}

Resultado … muy feo.

Encontré que PHP tiene una forma un poco practica para crear funciones de manera dinámica a partir de una cadena de texto con su código. El ejemplo anterior se simplificó de la siguiente manera.

usort($datos['opciones'],
         create_function('$a, $b',
                         'return strcmp($a["titulo"], $b["titulo"]);'));

Un tanto preferible esta versión.

position:fixed en IE

Nada funciona bien en IE. La gente inteligente debería utilizar Firefox únicamente. Desarrollar para IE es totalmente frustrante, no se adhiere completamente a los estándares y lo que se desarrolla para Firefox de CSS y JavaScript casi nunca le funciona. Hoy tuve un capítulo mas de esta historia.

Mi IE 7.0.5730.11 no entiende la instrucción position:fixed de CSS!

Tuve que implementar la solución expuesta por http://www.gunlaug.no/contents/wd_additions_15.html.

* html div#miDivId
{
top:expression(eval(document.compatMode &&
document.compatMode=='CSS1Compat') ?
documentElement.scrollTop + 5: document.body.scrollTop + 5);
}

Véalo en vivo y a todo color siguiendo este enlace.

Cuál software de gestión documental utilizar ?

Problemas con el módulo Localizer y Gallery en Drupal

La semana pasada detecté un problema con uno de los portales basados en Drupal. Por solicitud del cliente se instaló el módulo Localizer que permite mantener versiones en distintos idiomas de los contenidos de los nodos.

Este módulo modifica el estilo de los URL que maneja Drupal cambiándolos del estilo ?q=modulo/accion al estilo ?q=idioma/modulo/accion. Esto requirió que en algunas partes donde se autoriza o no el uso de algún recurso según el URL (como por ejemplo donde mostrar TinyMCE) se actualizara de node/* a */node/* y así sucesivamente.

Todo funcionaba perfectamente hasta que me percaté que el módulo de Galería de Imágenes que en realidad realiza la interacción con Gallery estaba fallando, los enlaces estaban siendo mal construídos y por ende llevaban al usuario a un error 404.

  • Incorrecta: http://HOST/apps/site/?q=es/index.php?q=gallery&g2_itemId=1&g2_GALLERYSID=XXX
  • Correcta: http://HOST/apps/site/?q=es/gallery&g2_itemId=1&g2_GALLERYSID=XXX

A pesar de que el autor de Localizer menciona en su sitio web que existen varias formas de configurar el módulo para que integre la información del idioma (la modificación mencionada al parámetro q es una de ellas) no encontré documentación acerca de como hacerlo. Investigué un poco y encontré una solución al problema.

En el archivo gallery_base.inc ubicado en modules/gallery realicé las siguientes modificaciones basadas en las incluídas para el módulo de I18.

En la línea 87 (función _gallery_init) agregué el siguiente código después de la línea "include_once($embed_path);".

// Localizer url rewrite
if(module_exists('localizer'))
{
$embed_uri = preg_replace('/index.php\?q=/', '', $embed_uri);
}

Esto corrige el URL de forma adecuada para que los visitantes puedan acceder a los álbumes de Gallery desde Drupal.