Delegación de eventos en Javascript: Un caso práctico

2378867408_4cc90791d6_z

Supongamos que tenemos un blog ( :) ) y queremos que todos los enlaces a páginas fuera de nuestro dominio se abran en una pestaña nueva del navegador. Por desgracia tenemos muchos artículos y no podemos cambiar el atributo target de los enlaces manualmente. ¿Qué hacer? La solución más conveniente es utilizar un poco de Javascript para cambiar el target dinámicamente. Usando jQuery, una posible implementación sería la siguiente:

$(".article-body").find("a").each(function(i, el) {
	if($(el).attr("href").indexOf(window.location.hostname) === -1 ) {
		$(el).attr("target", "_blank");
	}
});

Esta función busca todos los enlaces dentro del elemento con la clase article-body, y para cada uno de ellos, compara con el hostname de nuestra página. Si el enlace pertenece a un sitio distinto, añade el atributo target="_blank" para abrir el enlace en una nueva ventana. ¿Fácil, verdad?

Otra posible solución sería capturar el momento en el que se clica el enlace con el evento onclick, y abrir el enlace en una nueva ventana.

$(".article-body").find("a").each(function(i, el) {
	$(this).click(function() {
		if($(this).attr("href").indexOf(window.location.hostname) === -1 ) {
			window.open($(this).attr("href"));
			return false;
		}
	});
});

También muy fácil. ¿Pero cuál de las 2 opciones es mejor usar? Haciendo un test con una página de prueba con 20000 enlaces (no vamos a quedarnos cortos :) ), vemos que la primera alternativa tarda unos 200ms de media, mientras que la segunda tarda más de 300ms.

¿Cómo podríamos hacer este código más eficiente? La respuesta es usar lo que se conoce como Delegación de eventos. El concepto de Delegación de eventos en Javascript significa que un evento asociado a un nodo padre (Ej: article-body) puede ser disparado por uno de los nodos hijos (Ej: a), gracias al mecanismo de propagación de eventos en un navegador -de dentro para afuera-. Esto quiere decir que si ocurre un evento, este se va propagando por los nodos ascendentes hasta llegar a la raiz del documento (), puede ser capturado por cualquier elemento de la jerarquía que espere dicho evento. Es un poco difícil de explicar pero una vez que se entiende, se trabaja con ello de forma bastante intuitiva.

Despues del rollo teórico, vamos a reescribir el código usando la función .on() de jQuery, que nos permite usar delegación de eventos de forma sencilla:

$(".article-body").on("click", "a", function(event){
	if($(this).attr("href").indexOf(window.location.hostname) === -1 ) {
		window.open($(this).attr("href"));
		return false;
	}
});

Este código tarda apenas ¡2ms! en una página con 20000 enlaces. Esto es posible ya que el evento click se asigna a un único elemento (la capa contenedora article-body), en comparación con el caso anterior, donde se asignan n eventos en una página con n enlaces. De este modo hemos mejorado exponencialmente el rendimiento de nuestra función.

¿Serías capaz de hacer este código aún más eficiente? ¡Explica cómo en los comentarios!

Solr: búsquedas 3 veces más rápidas en WordPress

sun

La búsqueda nativa de WordPress es un pesado Juggernaut que puede ser muy lenta, sobre todo si tienes una gran cantidad de artículos. Además existe el problema de que por defecto se muestran los artículos ordenados por fecha de publicación, no por relevancia, lo cual no es demasiado útil para ayudar a tus lectores a encontrar lo que esten buscando. En resumen, lo que queremos es una búsqueda rápida, eficiente y relevante, algo que WordPress no ofrece por defecto.

En The Next Web nos hemos enfrentado recientemente a este problema, y gracias a una solución basada en Apache Solr hemos incrementado la velocidad de las búsquedas un 300%, a la vez que hemos mejorado la relevancia de los resultados mostrados. ¿Quieres saber cómo lo hemos conseguido?

Instalar Solr

Solr es un potente motor de búsqueda de código abierto. Está implementado en Java, así que necesitamos ejecutarlo algún servidor web Java como Apache Tomcat o Jetty. En nuestro caso vamos a usar Jetty ya que es facil de configurar y su carga es relativamente ligera. Dependiendo de tu versión de Linux puedes instalarlo con sudo apt-get install jetty o sudo yum install jetty.

A continuación descargaremos la última versión de Solr (3.5.0). Ten en cuenta que necesitas el runtime de Java 1.6 para que Solr funcione correctamente (Puedes averiguar la versión instalada en tu servidor con el comando java -version)

Por último vamos a crear un servicio para que sea más sencillo iniciar y detener la ejecución de Solr. (Nota: Asumimos que Solr se encuentra en el directorio /var/opt/solr. Usa el parámetro -Xmx para indicar el máximo de memoria que la máquina virtual de Java puede usar, para este ejemplo, 512MB son más que suficientes.

sudo vi /etc/init.d/solr
#chkconfig: – 80 20
#!/bin/sh -e
# Starts, stops, and restarts solr

SOLR_DIR="/var/opt/solr/example"
JAVA_OPTIONS="-Xmx512m -DSTOP.PORT=8079 -DSTOP.KEY=stopkey -jar start.jar"
LOG_FILE="/var/log/solr.log"
JAVA="/usr/bin/java"

case $1 in
start)
echo "Starting Solr"
cd $SOLR_DIR
$JAVA $JAVA_OPTIONS 2> $LOG_FILE &
;;
stop)
echo "Stopping Solr"
cd $SOLR_DIR
$JAVA $JAVA_OPTIONS –stop
;;
restart)
$0 stop
sleep 1
$0 start
;;
*)
echo "Usage: $0 {start|stop|restart}" >&2
exit 1
;;
esac

Instalar el plugin de WordPress

Integrar Solr en nuestra aplicación es tarea sencilla, ya que hay disponibles múltiples librerias que implementan las APIs de Solr en varios lenguajes (PHP, Ruby, Phyton…). Para nuestra integración con WordPress, vamos a utilizar el plugin Solr for WordPress [1]. Basta con seguir las instrucciones del plugin para configurarlo de acorde a tus necesitades y si todo va bien tendremos el índice que nutrirá las búsquedas en pocos minutos.

Resultados

Comparando los tiempos de ejecución con xhprof, para el caso concreto de The Next Web (con unos 30,000 artículos publicados) hemos pasado de aprox. 1.8 segundos de media para realizar una búsqueda cualquiera, a 600-700 milisegundos. Esto hace la búsqueda con Solr un 300% más rápida que la búsqueda nativa de WordPress. Puedes ver los resultados aquí, por ejemplo: http://thenextweb.com?s=steve+jobs.

Esto es sólo el principio, ya que Solr contiene numerosas características avanzadas que iremos incorporando poco a poco, como sugerencias de búsqueda, resultados similares, un algoritmo de relevancia a medida…

En resumen, si debes realizar búsquedas fulltext en páginas con decenas de miles de artículos, y crees que MySQL se te queda pequeño, ¿a que estás esperando para probar Solr?


[1] Debo realizar una advertencia antes de usar este plugin (y en general cualquier plugin o librería que quieras integrar en tu proyecto). Asegúrate de que no hay bugs que comprometan la seguridad o el rendimiento de tu propio código.

En concreto, un serio problema que encontré en Solr for WordPress es que no desactiva la búsqueda nativa de WordPress, por lo que además de la consulta a Solr, internamente se sigue realizando una consulta a MySQL, aunque el resultado de esta última nunca es mostrado. Esto hace inútil nuestro propósito de mejorar la velocidad al obtener los resultados de búsqueda. Si vas a utlizar el plugin, te recomiendo que arregles este bug. Si bien no sé si esta es la mejor solución, la siguiente función, que resetea el valor de la cadena de búsqueda, ha funcionado para mí:

function s4w_filter_query( $query ) {
    if ( is_search() ) {
        $query->query_vars[s] = false;
        $query->query[s] = false;
    }
}
add_action( 'parse_query', 's4w_filter_query' );

Cómo realizar mantenimiento en el servidor sin afectar tu SEO

server-down-tnw

Este fin de semana hemos realizado unas labores de mantenimiento en los servidores de The Next Web que nos han obligado a cerrar la página por unos minutos, para que el tráfico normal no interfiriera con dicho mantenimiento. Este tipo de operaciones son frecuentes pero la mayoría de las veces no se tiene en cuenta que el tiempo que esté caído el servidor puede afectar negativamente el SEO de tu página.

En este artículo, el ingeniero de Google Pierre Far explica en detalle por qué debes siempre devolver una cabecera HTTP con status 503: Service Temporarily Unavailable mientras tu servidor no está disponible para que tu ranking de Google no se vea penalizado. En resumen, el artículo explica que un bot no indexará tu sitio si la cabecera HTTP contiene un error 503. Si no lo haces así, Google pensará que el contenido de todas tus páginas ha cambiado, lo que causará casos de contenido duplicado y perjudicará seriamente el ranking de tus páginas.

Una posible forma de evitar estos problemas es añadir las siguientes líneas en tu configuración de Apache:

KeepAlive Off
RewriteEngine On
RewriteRule ^(.*)$ /maintenance.php [L]

En primer lugar reescribiremos todas las urls (^(.*)$) pasa que sirvan una página de error que alojaremos en el servidor. Adicionalemnte, también podemos desactivar el KeepAlive, ya que nos interesa cerrar la conexión cuanto antes posible, ya que los visitantes no realizarán más de una única petición al servidor mientras este no se encuentre disponible.

En el archivo maintenance.php incluiremos la cabecera HTTP con el status 503.

<?php
    header('HTTP/1.1 503 Service Temporarily Unavailable');
?>

En este mismo archivo puedes poner más información acerca de la situación, indicando el motivo por el que el servidor está caído, y si es posible, cuanto tiempo durará el mantenimento. Esto tranquilizará a tus visitantes y evitará que estén refrescando la página cada 5 segundos :)

Una introducción a Capistrano

Capistrano

Capistrano es una aplicación open-source escrita en Ruby para automatizar tareas en uno o varios servidores remotos via SSH. Es frecuentemente usado por administradores y desarrolladores web para mover aplicaciones a los servidores de producción de forma muy sencilla, por lo que es una de las herramientas que no puede faltar si trabajas con aplicaciones web no triviales.

Instalando Capistrano

Como ya hemos dicho, Capistrano está implementado en Ruby, por lo que uno de los requisitos para utilizarlo es tener instalado el intérprete de este lenguage de programación, así como el gestor de paquetes rubygems. También (sobra decirlo), es necesario que tu proyecto haga uso de un SCM como Git, Subversion o Mercurial.

sudo apt-get install rubygems
sudo gem install capistrano

Una vez tengas todo instalado, debes correr el comando capify para que tu proyecto quede configurado para usar Capistrano.

capify .

Este comando creará varios archivos, entre ellos ./config/deploy.rb. Vamos a editar este archivo, donde incluiremos la configuración del servidor remoto y la de nuestro sistema de control de versiones.

A continuación, un ejemplo del archivo de configuración que usamos en The Next Web:

role :web, 'server1.thenextweb.com', 'server2.thenextweb.com'

set :application, 'thenextweb'

# SSH options
ssh_options[:username] = 'root'
ssh_options[:forward_agent] = true

# SCM options
set :scm, :git
set :scm_verbose, true
set :repository, 'git@localhost:thenextweb.git'
set :branch, 'master'
set :deploy_via, :remote_cache
set :use_sudo, false

# Other settings
set :deploy_to, "/var/www/#{application}/"
set :keep_releases, 5

¿Cómo poner código en producción?

Una vez tu configuración esté lista, debes inicializar los servidores remotos con el comando:

cap deploy:setup

Este comando se conectará por SSH a dichos servidores y creará la estructura de directorios necesaria para el funcionamiento interno de Capistrano, a partir de la ruta indicada en deploy_to. Una vez hecho esto, y si no has recibido ningún error, puedes hacer tu primer deploy de la siguiente manera:

cap deploy

¿Fácil, verdad? :)

Otros casos de uso interesantes de Capistrano es el de deshacer un deploy:

deploy:rollback

O ejecutar un único comando en el servidor remoto:

cap invoke

Usos avanzados

Básicamente Capistrano permite correr cualquier comando en el servidor remoto, así que el límite de posibilidades está en tu imaginación. Por ejemplo, es posible:

  • Enviar un email al resto del equipo tras poner código en producción:
  • Realizar tareas de mantenimiento antes de actualizar el código. Por ejemplo, correr un script que actualice la estructura de la base de datos.
  • Instalar nuevos paquetes o cambiar configuraciones en multiples servidores al mismo tiempo.

Puedes encontrar mucha más información en la web oficial de Capistrano, en GitHub:
https://github.com/capistrano/capistrano/wiki/
También en GitHub hay un manual muy completo con explicaciones detalladas.
https://github.com/leehambley/capistrano-handbook/blob/master/index.markdown

TNW Social Count para WordPress

Social Count banner

TNW Social Count es un plugin de WordPress para compartir tus artículos en diversas redes sociales.

Vale pero, ¿qué tiene de especial?

La mayoría de blogs y páginas web utilizan botones de Facebook, Twitter, etc. para facilitar a los lectores compartir el contenido a traves de las redes sociales. El problema con la implementación por defecto de dichos botones es que requieren incluir pesados archivos Javascript a tu página, lo que puede hacer que tu sitio sea mucho más lento si no eres cuidadoso con la implementación. Por si fuera poco, (y esto es una opinión personal), todos estos botones crean demasiado ruido en el diseño de la web y resultan poco agradables visualmente.

En mi trabajo para The Next Web, una de nuestras mayores prioridades es que la web cargue lo más rapido posible. Tras realizar múltiples mejoras para acelerar la velocidad de carga de la página, decidimos eliminar todos los botones sociales por defecto y crear nuestra propia implementación. Puedes verla en acción en cualquier artículo en TNW.

El feedback que recibimos fue bastante positivo, e incluso algunos sitios se inspiraron en la idea y realizaron sus propias implementaciones. Por eso he decidido abrir el código y crear un plugin de WordPress para que otros sitios puedan utilizar la misma solución.

¿Cómo funciona?

El plugin esta formado por dos partes independientes:

- El proceso que obtiene, a traves de las (más o menos soportadas) APIs de Twitter, Facebook, LinkedIn y Google+, el número de veces que cada artículo ha sido compartido en cada uno de estos servicios. Este número es guardado como post_meta en la base de datos.

- El botón que usa la información del post_meta para mostrar el total de veces que el artículo se ha compartido, y incluye un popup con botones para compartir en Twitter, Facebook, LinkedIn, Google+, StumbleUpon, Reddit, Digg, HackerNews, Instapaper y ReadItLater. Estos botones están implementados en puro html, por lo que no es necesario cargar ningun archivo de Javascript externo que pueda relentizar tu sitio.

Instalarlo es fácil

Una vez activado el plugin, puedes activar la frecuencia de actualización desde el menú de adminstración de WordPress.

Para mostrar el botón en el frontend, tienes que incluir el siguiente código en el archivo de tu tema dónde quieras mostrarlo.

if(function_exists('render_tnwsc_button')) {
    echo render_tnwsc_button();
}

Descarga el plugin en GitHub, instálalo en tu sitio y cuéntame qué te parece. Ten en cuenta que aún lo estoy probando, por lo que puede tener algún bug. Si encuentras algún problema, deja un comentario o mejor aún, hazle un fork y ayudame a mejorarlo! ;)

8 comandos útiles para GIT

Github

Hoy me gustaría hablar de varios comandos y pequeños trucos de Git que utilizo con bastante frecuencia y que son de gran utilidad en mi trabajo diario. Ahí van!

1. Mostrar las diferencias existentes entre master y otro branch.:

git diff master..branch

Sin más, esto es algo que uso constantemente para revisar el trabajo que se ha hecho en el branch y asegurarse que todo está correcto antes de hacer un merge con otra rama.

2. Mostrar el total de commits realizados por los miembros del equipo:

git shortlog -s -n

(554 commits desde que trabajo en el blog de The Next Web. :) )

3. Mostrar un resumen corto del log de commits:

git log --pretty=format:"%h%x09%an%x09%ad%x09%s" --date=short

Este comando mostrará el log de commits con el siguiente formato:

47a6398 root 2012-02-03 Fixed Twitter share popup

Para mostrar solo el ID del commit y el mensaje, puedes hacer:

git log --pretty=oneline

4. Evitar incluir código de depuración en tu commit:

¿Quién no ha olvidado alguna vez quitar todos los  var_dump,  console.log, etc, del código antes de hacer el commit y luego se ha encontrado en producción? Este pre-commit hook evita esta incómoda situación.
https://github.com/borisguery/git-keywords-checker/blob/master/pre-commit

5. Obtener información sobre el repositorio remoto:

git remote show origin

Esto es útil para chequear las urls de Fetch y Push, el estado de tu HEAD, los branches remotos, etc.

6. Colorea la salida de git en consola:

git config --global color.ui true

7. Guardar temporalmente el trabajo no commiteado:

El stash de Git es un lugar que se puede usar para guardar temporalmente los elementos no commiteados, eliminándolos del HEAD. Esto permite realizar operaciones que serían muy engorrosas de otro modo, por ejemplo, si necesitas cambiar de rama para arreglar un bug urgente y no quieres commitear el código que tienes a medias. Para guardar el trabajo en el stash, es tan facil como:

git stash save

De la misma manera, para recuperar los cambios guardados:

git stash pop

Y para mostrar los contenidos del stash:

git stash list

8. Mostrar una consola interactiva para añadir elementos al stage antes de un commit:

git add -p

Este comando permite añadir archivos al stage, omitir los cambios realizados y muchas cosas más.

¿Conocéis más cosas curiosas que se puedan hacer con Git? Seguramente hay muchísimos trucos que no conozco. Os invito a compartirlas en los comentarios.

Imágenes destacadas de WordPress en el feed RSS

Wordpress

Ya comenté en un post anterior cómo añadir imágenes destacadas a tu tema de WordPress, pero olvidé indicar cómo se pueden añadir también estas imágenes a tu feed RSS. Yo por ejemplo uso Feedly y Flipboard para iPad como lectores de feeds y en ambas aplicaciones este tipo de imágenes quedan de maravilla, haciendo tus artículos mucho más visuales y atractivos.

Pasando sin dilaciones al código fuente, estas son las dos funciones que tienes que añadir en el archivo functions.php de tu tema:

function rss_add_post_thumbnail($content = '') {
	global $post;
	if ( has_post_thumbnail() ) {
		$image = get_the_post_thumbnail($post->ID, 'main-thumb', array('class' => ''));
		$content = $image . '<br />' . $content;
	}
	return $content;
}
add_filter('the_excerpt_rss', 'rss_add_post_thumbnail');
add_filter('the_content',     'rss_add_post_thumbnail');

Este fragmento de código incluirá la imagen destacada antes del contenido del post en el feed RSS. El siguiente fragmento añade la etiqueta enclosure a cada item del feed:

function rss_add_enclosure() {
    global $post;
    if( has_post_thumbnail() ) {
		$thumb = wp_get_attachment_image_src( get_post_thumbnail_id($post->ID), 'main-thumb' );
		$url = $thumb['0'];
		echo "\n";
    }
}
add_action('rss_item', 	'rss_add_enclosure');
add_action('rss2_item', 'rss_add_enclosure');

4 formas creativas de añadir un botón Google +1 a tu web

googleplusone

1. Carga el botón asincronamente

La implementación asíncrona del botón +1 de Google permite cargar los scripts necesarios para renderizar el botón después del evento onload. Con esto conseguimos que los botones aparezcan una vez que todo el contenido de la página se ha cargado, lo que hará que el botón no repercuta en la velocidad de carga de tu sitio.

2. Retrasa el renderizado del botón

Usando la misma implementación asíncrona explicada anteriormente, puedes incluso evitar cargar el botón +1 de forma automática, para luego mostrarlo programáticamente solamente cuando sea necesario (por ejemplo, tras la acción de algún evento, como un click en un enlace). Este método se llama carga explícita, y también está detallado en la documentación del botón +1.

3. Usa un botón +1 personalizado

También es posible usar una imagen personalizada para tu botón Google +1, en lugar del botón por defecto. La forma de implementar este hack es situando tu botón personalizado de forma absoluta, encima de un botón +1 original, que ocultaremos usando CSS.
Puedes ver un ejemplo con el código fuente aquí. Eso sí, ten cuidado con este método ya que según los términos y condiciones del uso del botón, no está permitido alterar el aspecto del mismo.

4. Usa un simple enlace html

Existe también otro método — del que hasta dónde yo sé, aún no existe documentación oficial—, que permite hacer un +1 o compartir en Google+ de una forma similar a los a los Intents de Twitter. También es recomendable tener cuidado con este método ya que puede dejar de funcionar en cualquier momento. De hecho, hasta hace tan solo unos días, tan solo funcionaba para compartir urls en stackoverflow.com. La ventaja de este método es que se trata de la implementación más rápida y fácil de todas, ya que no hay que incluir ningún script externo.

El enlace que puedes usar para mostrarlo tiene la forma: http://plus.google.com/share=http://example.com

Demo: Comparte este artículo en Google+!

¿Conoces algún otro método para incluir la funcionalidad +1 en una web? Compartelo en los comentarios.

¿Trabajar para vivir, o vivir para trabajar?

workaholic

Leo a Jeff Atwood en su blog Coding Horror anunciar que abandona Stack Exchange, empresa que fundó para crear el popular sitio de preguntas y respuestas sobre programación Stack Overflow. La razón de su abandono es el deseo de pasar más tiempo junto a su familia. Aplaudo su decisión. Mientras tanto, Steve Jobs, en su biografía autorizada, admite que aprobó su publicación “para que mis hijos pudieran conocerme”.

La pregunta que da título al post puede sonar un poco frívola para alguien que no tiene trabajo, pero en gran parte del Primer Mundo, me da la impresión que a menudo se dedican demasiadas horas a trabajar, a quedar bien con el jefe, a poder tener la oportunidad de ascender y ganar unos euros más, sobre todo en las profesiones más competitivas. Mientras tanto, nos olvidamos de pasar tiempo con la familia o amigos, de leer un libro, o simplemente dormir o tirarse el sofá sin hacer nada.

¿Qué haríais vosotros? ¿Creéis que se puede ser feliz dedicándose en cuerpo y alma al trabajo, dejando a un lado lo demás? ¿Os parece una decisión acertada la de Jeff Atwood?

Ligeramente relacionado: Los 5 mayores remordimientos de la gente al morir, publicado en The Next Web hace casi un año, es uno de los posts más populares del blog. Merece la pena leerlo.