viernes, 27 de abril de 2012

Erlang (V) HTTP reverse proxy

[Repaso]
Repasemos la infraestrctura: Ahora mismo tenemos un servidor web hecho en Erlang, MochiWeb, que accepta mucha mucha carga.

Este servidor en si mismo no hace el trabajo, sino que deja que un Actor de Erlang (llamado Usuario) se encargue de gestionar la petición. Así conseguimos una concurrencia brutal y soportamos un montonazo de carga[1]. El actor usuario, en caso de recibir un mensaje de otro usuario, devolverá este mensaje al servidor web, que a su vez se lo mandará al usuario final y este lo recibirá en su navegador. Bien. También tenemos en un almacén de datos en memória la gestión de las sesiones (en mnesia): allí se asocia un ID de sessión HTTP con el ID de usuario.

Ahora mismo la arquitectura de la aplicación está pensada para ser ejecutada en una misma máquina, lo que supone algunos problemas: cuando toca reiniciar el equipo debido a actualizaciones de kernel y demás el chat debe permanecer "cerrado". No solo esto, si no que el usuario no recibiría un bonito mensaje de error que diga "We are under maintenance", sinó mas bien un "server not found" o algo así. Algo feo feo. Pero arreglarlo tampoco es algo fácil.

También hay otros pequeños problemas como por ejemplo el logueo de las peticiones HTTP (típico fichero access.log) y ofrecer HTTPS (SSL/TLS).

Veamos una imágen actual de la arquitectura de Gaab Web Chat:




Bien. Tenemos un único punto de entrada directamente al servidor de chat de Erlang. En si mismo esto no está mal, ya que el servidor accepta una concurrencia "que te cagas", pero tenemos los problemas comentados anteriormente. Parece que la solución más simple es (como casi siempre) añadir una capa mas de complejidad y añadir un: http reverse proxy.


[Cherokee]
No explicaré lo que es un reverse proxy (consúltese en la wikipedia, o léase este[2] artículo), pero sí explicaré por qué he elegido Cherokee. Aparte de por que en todos los bencharks tiene unos muy buenos resultados, tiene unos ficheros de configuración con una gestión muy clara, es relativamente nuevo pero tiene un gran comunidad y está publicado bajo GPL es porque el creador del servidor web es español. Si señor. Me ha salido la vena patriótica y me apetece apoyar (ummm... vale, con "apoyar" quizás me he pasado, digamos "usar") un proyecto hecho en casa. Y punto.

Y las primeras impresiones no pueden ser mejores: gestión vía web, creado un virtual host en menos de 1 minuto, creado el http proxy en 5 minutos. Amén. Así da gusto.

La idea es usar Cherokee como balanceador de carga entre diferentes aplicaciones GaabWebChat(Erlang+Mnesia+Aplicacion) que se estarían ejecutando en máquinas distintas. La carga de tráfico sería balanceada entre todas las máquina que pudiese tener la aplicación. En caso de que se tuviese que actualizar la plataforma, sería tan simple como decirle a Cherokee que no mandara más tráfico en aquella máquina, actualizarla tranquilamente, y después volver a dirigir tráfico a la misma.

La arquitectura quedaría algo así:



Mucho mas chula que antes, no? Así Cherokee se puede encargar de gestionar las conexiones HTTP/HTTPS, loguear todos los requests en un fichero "access.log", balancear la carga entre varias aplicaciones y servir contenido estático.


[Mejoras]
Esta plataforma todavía se puede mejorar ya que todavía hay un único punto de fallo, el servidor de Cherokee. Esto se podría remediar poniendo varios Cherokees en diferentes máquinas y balanceando la carga por DNS, por ejemplo. Pero esto ya queda (de momento) fuera del alacance de este post.

Esta solución aquí propuesta es un algo que puede que en un futuro pueda necesitar, pero des de luego no lo incluiré en la primera versión que ponga online, ya que MochiWeb puede soportar muucha carga (estoy seguro que ahora mismo por cada 500MiB de RAM que pueda conseguir en un hosting puede soportar a 5K usuarios).

Pues nada, señores. Un saludo.

P.D: Mierda de Blogger. Las imágenes quedan como el culo, pero tienen que tener este tamaño para que se vean claras. So sorry. Prometo cambiar pronto el blog de sitio :/

[1] Me faltan hacer un par de benchmarks para demostrar que esto es así, pero la pereza puede a uno.
[2] http://www.cherokee-project.com/doc/modules_handlers_proxy.html

jueves, 26 de abril de 2012

Peticiones Ajax en IE8 y cachés: crap, crap, crap


IE8 apesta. Y por extensión Microsoft también.


[Problema]
Las peticiones por Ajax que lanza IE8 son cacheadas. Todas ellas. Si lanzas un requests por Ajax a la url "/check_mail_box" y el resultado de la petición dice que no tienes nuevos mails, tooodos los demás requests que hagas a esta URL estarán cacheados y realmente ni se volverá a lanzar una petición HTTP para comprobar nada. IE8 es inteligente (o almenos esto cree) y dice: ¿si ya tengo la información, para qué voy a volver a pedirla?

¡PUES PORQUE PUEDE HABER CAMBIADO, CACHO RETRASADO! ¡POR ESTO LANZO OTRA PETICIÓN! ¡PARA COMPROBARLO DE NUEVO!

Calémonos.

[Repasando las bases]

El protocolo HTTP hace AÑOS que se usa en el mundo Web. El cliente hace una petición de un recurso, el servidor busca el recurso y se lo devuelve al cliente. En caso de que se haga otra petición, el proceso se repite.

Hace hace años también que dejó de servirse contenido estático y ahora se están sirviendo contenidos dinámicos, generados por PHP, .NET, CGI y un largísimo ETC... Esto significa que si tu pides un recurso, como por ejemplo "/last_users_connected", el resultado de la petición HTTP puede cambiar en el tiempo, ya que es una información CALCULADA en la parte servidor.

Bien.

Ajax. Ha aparecido hace relativamente poco. Es lo mismo que una petición HTTP pero de manera asíncrona. ¿Qué significa esto? Pues que el usuario puede estar navegando tranquilamente mientras, en segundo plano, se descarga otra información. Muy cómodo para no tener que ir recargando la página cada dos por tres. Pero esto de Ajax es solo un artificio creado por el navegador. En realidad se lanzan peticiones HTTP como las "normales". Exactamente iguales. No hay diferéncia entre ellas.


[Acercándonos al problema]
Cada vez que el usuario entra en "www.facebook.com" el navegador lanza una petición HTTP pidiendo el recurso "/index.html". Bien. Si le damos a F5 (refrescar) o volvemos a abrir en otro Tab del navegador "www.facebook.com", vuelve a lanzar la petición y puede que los resultados del primer tab al segundo tab sean diferentes. Obvio.

Imaginemos que el navegador cachea la primera petición a "/index.html" y que a partir de ahora cada vez que entramos en www.facebook.com nos sirve la versión cacheada. Pues vaya inutilidad, verdad?


[Meollo]
Bien, pues esto es lo que hace IE8 con las llamadas de AJAX.  

Se le tiene que decir explícitamente a IE8 que NO haga uso de la caché en las peticiones por Ajax par que funcione de un modo normal. Otro modo de decir lo mismo es que se tiene que instruir a IE8 para que funcione CORRECTAMENTE y como un usuario corriente y moliente esperaría. Va contra la lógica más básica: le estás diciendo al navegador que descargue una información de un servidor (pero de modo asíncrono), y el navegador -por que le sale de los cojones- te devuelve una versión antigua de la misma sin lanzar peticiones ni ostias. Fuck.

[Conclusión]
IE8 apesta.




Y por extensión Microsoft también.


Un saludo, Jan.

P.D: Me pregunto los miles de millones de euros que habrá costado esta decisión por parte del equipo de IE8. Pensadlo, la de librerías que se tienen que adaptar y la de programadores que tienen que aprender la gilipollez esta.
P.D2: He de decir que W7, y IE9 ya parecen SO/navegador decentes, y que parece que las ñapas que había estado haciendo Microsoft hasta ahora (W98, W2000, WMe, WVista, IE{5,6,7,8}) quedan en el pasado. Ya iba siendo hora.








lunes, 9 de abril de 2012

Errores en el manual de bash

Me aburro. No os creáis, me pasa amenudo. Y no lo veo como algo malo, ya que normalmente para quitarme el aburrimiento hago lo que yo llamo "cosas productivas". Como leer el manual de bash, por ejemplo.

$ man bash

Así de fácil. Y la verdad que está bastante interesante y uno se entera de cosas que no sabia, o entiende cosas en las que hasta ahora dudaba. Pero estoy yo tan tranquilo leyendo el manualillo en mi Kindle[0] en el avión, cuando me encuentro con una cosa que no me ha acaba de gustar. En el apartado REDIRECTION, pone lo siguiente:

Bash handles several filenames specially when they are used in redirections, as described in the following table:
/dev/tcp/host/port
If host is a valid hostname or Internet address, and port is an integer port number or service name, bash attempts to open a TCP connection to the corresponding socket.

Bueno, hasta aquí no tengo ninguna queja. Estoy conforme con esto. Pero ya no me parece tan bien el siguiente bloque. ¡Ai señor! ¡Cuánto daño ha hecho el copy-paste!

/dev/udp/host/port
If host is a valid hostname or Internet address, and port is an integer port number or service name, bash >>>attempts to open a UDP **connection**<<< to the corresponding socket.
O sea, exactamente igual que el bloque de anterior, pero cambiando TCP por UDP. Solo que estos dos protocolos no son lo mismo. Y dá la casualidad que en el protocolo UDP no se establecen conexiones. Y no es porque lo diga yo. Si hacemos un "man udp", podemos leer en la primera línea:

This is an implementation of the User Datagram Protocol described in RFC 768. It implements a **connectionless**, unreliable datagram packet service. Packets may be reordered or duplicated before they arrive.

Connectionless, sin conexiones. Por lo que el término "connection" al lado de UDP ya es incorrecto en si mismo, ya que las conexiones UDP no existen (si eso, se implementan en capas superiores en la pila TCP/IP, o sea, en la capa de Aplicación. Pero en el protocolo UDP no existen conexiones). Además, "attemps to open" también es incorrecto, ya que UDP no intenta "abrir" nada, sinó mas bien intenta mandar el paquete a la red. ¿Que el paquete llega? Bues bien. ¿Que no llega? Pues a UDP se la suda. De hecho el protocolo UDP no sabe ni si ha llegado el paquete[1], ni le importa. Repito, no existe el concepto de conexión en el protocolo UDP.

¿Qué todavía no me crees? Vaya, macho... a ver. RFC793, TCP[2]. Ocurrencias de la palabra connection: 271. RFC768, UDP[3]. Ocurrencias de la palabra connection: ninguna!!!

¿Qué propongo? Pues cambiar la frase a algo así:

If host is a valid hostname or Internet address, and port is an integer port number or service name, bash attempts to send a UDP datagram to the corresponding socket.

Ya he mandado un email al mantenedor del manual de bash, en mi perfecto inglés de mierda (gracias hermana por ayudarme a corregir mi inglesanalfabetismo). A ver que dice. Ya os diré cosas cuando conteste. A ver que le parece a él.

Un saludo, Jan.


[0] Noooo! Tranquilos!! No soy tan bruto como para inslarle Linux en un Kindle! Es simplemente que quería decir que me había comprado el juguetito ^_^
[1] no debería decir paquete sinó datagrama, para ser mas exacto, ya que lo que comunmente llamamos paquetes se trata siempre del nombre del PDU de Capa de Red, o sea, protocolo IP, y ahora estamos hablando de la capa de tranmisión de datos, donte tenemos datagramas en el caso de UDP y segmentos en el caso de TCP, pero bueno..... sé que diciendo paquete ya nos entendemos! Y recordemos que UDP es unreliable y por esto se la suda si llegan o no los paquetes.

sábado, 7 de abril de 2012

Actualizando el microcódigo de la CPU (microcode.ctl)

[Explicación]
Los procesadores tienen un firmware, también llamado microcódigo. El firmware está situado entre el hardware y el lenguaje ensamblador. Mas o menos la cosa iría así:

hardware -> firmware (microcódigo) -> ensamblador -> kernel -> S.O. y aplicaciones

Es un bloque de instrucciones máquina que establece la lógica a más bajo nivel y que está fuertemente ligado a la electrónica subyacente. Pero así mismo, es un cacho de código. O sea, software.

Cuando compras una CPU en una tiena, o te compras un portátil estás adquiriendo un modelo concreto, que viene con una revisión (versión) de firmware concreta. Pero ¿Qué passa si Intel saca una nueva versión de firmware con mejoras, optimizaciones, uso de algoritmos mas eficientes y con menor consumo de energía, corrección de bugs, etc? Pues que no los puedes disfrutar, a menos que instales el firmware nuevo. Y...¿Cómo se consigue actualizar el microcódigo? Pues normalmente no te queda otra que actualizar la BIOS de tu máquina actualizando así el microcódigo de tu procesador.

Pero ya sabes que actualizar la BIOS no siempre es buena idea. Si mientras la estás actualizando se vá la corriente, puedes tirar el procesador directamente, que es irrecuperable. Es algo peligroso. Se puede hacer, pero no es algo que quieras hacer muy amenudo. Enserio.

Cagonlaputa. Entonces, ¿no hay alternativa posible? Si. Si la hay. Almenos en Linux si.

[Conociendo la herramienta]
Hay un hermoso paquete llamado microcode.ctl que hace éste trabajo por nosotros y que se encarga de cargar el microcódigo de manera dinámica en nuestro procesador. Esto significa que no flashea el procesador (no hace los cambios permanentes) sinó que actualiza el firmware del procesador al vuelo y en tiempo de ejecución cada vez que se inicia el S.O. Esto tiene varias ventajas:
  1. Lista numeradaNada de actualizar la BIOS del modo tradicional, por lo que nos evitamos un procedimiento peligroso (y tedioso).
  2. Se descarga automáticamente las nuevas versiones sacadas por Intel y las aplica en el próximo reinicio.
  3. En caso de que se descargue una versión que por lo que sea falla y nos dá problemas, solo debemos desactivar el daemon que se encarga de actualizar el microcódigo del procesador cada vez que se inicia el sistema, y santas pascuas.
[Instalación]
Pff.... ¿enserio que tengo que hacer esta sección? Si las secciones de "instalación" siempre son iguales en Linux :/
$ sudo aptitude install microcode.ctl

...y a correr. En la instalación te pedirá si te quieres descargar también el paquete propietario intel-microcode que contiene las últimas versiones de los microcódigos. Obviamente la resuesta es SI, QUIERO. Y punto. Si vas a ver el fichero /var/log/syslog puede que te encuentres con estas bonitas líneas (pero dependen de la versión de firmware que tengas, claro):

microcode: CPU0 sig=0x20655, pf=0x10, revision=0x2
microcode: CPU1 sig=0x20655, pf=0x10, revision=0x2
microcode: CPU2 sig=0x20655, pf=0x10, revision=0x2
microcode: CPU3 sig=0x20655, pf=0x10, revision=0x2
microcode: Microcode Update Driver: v2.00 , Peter Oruba
microcode: CPU0 updated to revision 0x3, date = 2011-09-01
microcode: CPU1 updated to revision 0x3, date = 2011-09-01
microcode: CPU2 updated to revision 0x3, date = 2011-09-01
microcode: CPU3 updated to revision 0x3, date = 2011-09-01

Un saludo!


viernes, 6 de abril de 2012

Gestor de portapapeles

Entrada rápida. Parcellite[1] es un gestor de portapapeles. Simplemente pulsando Control+Alt+H te aparece un listado con las últimas cosas que has copiado mediante Control+C, seleccionándolas con el ratón o mediante el teclado. Elige un elemento de la lista y para pegar pulsa una de las siguientes combinaciones de teclas:
  • Control+V
  • Shift+Insert
  • Control+Shift+V
Para programar con Vim es una monada. Pruebalo. Ya. Te encantará.

En mi configuración (a través de GTK) en la pestaña Behavior tengo marcadas solo las opciones:
  • Use Copy (Control-C)
  • Use Primary (Selection)
  • Syncronize clipboards
  • Save history
Y todo lo demás desactivado.

Un saludo.

jueves, 5 de abril de 2012

Erlang (IV) Semana 2

La Semana 1 construímos un core de servidor de chat que hacía bastante bien su trabajo. Ahora el paso lógico es:
  1. Integrar este core con un servidor HTTP
  2. Crear una User Interface (UI) bonita y amigable
[Servidor HTTP]
El primer punto, que al principio no acababa de ver claro, ha sido muy simple. Lo primero de todo era encontrar un servidor HTTP para Erlang. Mis requisitos eran:
  • Escrito 100% en Erlang. Para no liarnos con otras tecnologías
  • Que pueda soportar mucha concurrencia. Por motivos obvios en la aplicación que queremos crear.
  • Con un sistema muy simple de hacer dispatch de llamadas, con controladoras y todo el tinglado. Quería algo que ya te lo diese un poco mascado, pero que fuera altamente personalizable.
El servidor web MochiWeb cumple con todos los requisitos anteriores. El trabajo principal ha sido crear un pequeño dispacher para cada una de las llamadas, para poder ofrecer una interfície restful simple, limpia y clara pero que nos sirva para nuestros propósitos. Para saber exactamente cómo está implementado, que se mire el código (mas sobre esto al final), pero para dar una idea general os enseño la API REST que he creado:

  • Acción: /login Método: GET. Parámetros: N/A. Explicación: Renderiza la pantalla de login
  • Acción: /login Método: POST. Parámetros: username, password. Explicación: Loguea el usuario, en caso de que los datos sean correctos, mediante el establecimiento de una cookie.
  • Acción: /logout Método: GET. Parámetros: N/A. Explicación: Lo obvio
  • Acción: /msg Método: GET. Parámetros: N/A. Explicación: Éste es el mas importante de todos. Está pensado para ser llamado por AJAX y manetener la conexión abierta durante 20 segundos. Recibe como respuesta un JSON con los mensajes que puedan haber enviado enviado otros usuarios durante estos 20 segundos, la notificación de que otro usuario se ha logueado, etc... Más sobre esto, luego.
  • Acción: /msg Método: POST. Parámetros: msg, to Explicación: Envia el mensaje msg al usuario to. ¿Fácil, no?
  • Acción: /users_list Método: GET. Parámetros: N/A. Explicación: Devuelve un JSON con el listado de amigos conectados en este momento.

¿Simple, verdad?

La acción mas interesante es /msg per método GET. Vamos a explicarla un poquito mas. Como comentaba mas arriba, esta llamada devuelve un MIME-type application/json. Nada de XML ni líos, JSON nos va perfecto para este caso, ya que esto lo recibe la parte cliente y tiene que interpretarse en Javascript. Sabiendo que JSON significa JavaScript Object Notation, queda claro que para este caso ésta es la elección ideal. Esta acción está pensado para ser llamado por AJAX, pero también puede ser llamada por el método tradicional sin problemas. Cuando se cree la conexión, ésta permanecerá abierta durante 20 segunos. Durante este tiempo, si se produce algún evento en la parte servidor como por ejemplo recicibir un mensaje, se mandará el mensaje hacia el usuario a través de esta conexión. Si no hay nada que notificar en 20 segundos, se cierra la conexión y se espera que el cliente abra otra inmediatamente después, esperando así indefinidamente a recibir algún tipo de mensaje.

¿Qué tipo de información devuelve éste método? Pues información que tenga que ver con cambios de estado, mensajes de chat, notificaciones, etc... Veamos los JSON's de posibles resultados:
  • {"action":"message", "user":"inedit00", "msg": "Hello world!"}
  • {"action": "friend_logged", "name": "foo"}
  • {"action": "friend_unlogged", "name": "bar"}
  • ....
Como podeis ver la interfície es muy clara y legible. El action determina qué tipo de mensaje es, y cada mensaje tiene los campos con la información necesaria. Procesar esto en JS es prácticamente un chiste. Mirad, así, en pseudo-javascript y para que os hagáis una idea:


MessageHandler = {
message: function(request) { alert(request.msg) },
friend_logged: function(request) {alert("The user " + request.name + "logged in")},
.....
}
var process_http_response = function(html) {
json = JSON.decode(html)
MessageHandler[json["action"]](json)
}
nada... un juego de niños. La ventaja de utilizar algo así es que para añadir una acción más, simplemente hay que mandar un mensaje distinto y crear su handler en el código JS. Obviamente el código JS está pensado para ignorar todos los mensajes con un action para los que no tiene handler.

Total, que con todo esto ya tenemos una manera de mandar mensajes a otros usuarios en el sistema des de la parte cliente (JavaScript) y que sean recibidos por otros usuarios mediante HTTP. Genial.

[User Interface]
Bueno, esto simplemente es HTML+CSS+JS. Aquí no hay mucho que contar. Si alguien se lo quiere mirar que vaya al repositorio y mire el código fuente de todo el percal (es poquito y simple). Os dejo con el número de líneas que he usado para cada una de las cosas, y un par de capturas de pantalla, y arreando....
  • Página principal /index 37 líneas de HTML
  • Página de login /login 31 líneas de HTML
  • Fichero con todos el CSS's del proyecto: 98 líneas
  • Fichero con todo el JS del proyecto 195 líneas. Incluye la creación, cambio de tamaño/posición de las ventanas de chat, control de las conexiones por AJAX al servidor y los handlers que procesan las respuestas (mensajes nuevos, usuarios conectados/desconectados, etc....)....
Nhá... ha quedado todo muy pequeñito y limpio. Por supuesto nada de estilos inline, ni CSS en bloques style dentro de los ficheros HTML, ni Javascript inline en atributos HTML (¡aberración!) ni demás guarradas varias en el código. Sabéis que si hacéis estas cosas vais a ser sodomizados por Diox durante toda la eternidad, ¿verdad?
En el HTML hay dos imports, uno al CSS y otro al fichero JS y palante toda. Y mas o menos el tema luce así (que siiiiiii, que sé que todo lo anterior que os he contado os la sudaaaa, que coméis por los ojos!! ):




Ahora mismo solo hay un usuario conectado con el que pueda hablar (yo mismo en otro navegador ¬¬'). En las ventana de chat soporta modificación de tamaño, ponerla donde quieras en la pantalla, cerrarla y puedes tener abiertas tantas conversaciones de chat como quieras (siempre que tengas mas amigos, obviamente). Para ser una primera versión sacada en una tarde (HTML+CSS), estoy muy contento con el resultado.

Y nada.... esto es básicamente todo lo que tiene que ver con UI. Los ficheros interesantes del repositorio que tienen que ver con UI son:
  • /templates/index.dtl
  • /temlates/login.dtl
  • /priv/www/js/gaab.js
  • /priv/www/css/gaab.css
[Repositorio]
¿Repositorio? ¿Quién dijo repositorio? Pues claro que si, hombre! Si trabajas con código y no lo versionas es que te gusta vivir peligrosamente. Seguramente tus hobbies puedan ser hacer puenting sin cuerda, hacer la danza de pescado crudo en playas infestadas de tiburones en Madagascar o intentar ligar con la chica buenorra con el novio culturista.... a saber.

Todal, que tengo todo este código subido a un repositorio de bitbucket para uso y disfrute de todo quien quiera verlo:


y esto es todo... quien quiera preguntar alguna cosa, sugerir alguna otra, pedirme que explique algo un poco mas o tenga curiosidad en lo que sea: que me mande una caja de cerveza...... digooooooooo..... que deje un comentario!

Un saludo!

Vim y sus plugins

Menos pizzas, Vim puede hacerte todo lo demás. Si tienes complejo de pulpo (aka te gustaría tener un par de manos mas para poder usar Emacs como es debido), este post no es para ti.

He empezado a programar con Erlang y hay un par de pluguins a instalar que son indispensables. Es importante recalcar la importancia que tiene tener un entorno de trabajo cómodo y eficiente, para que el trabajo salga mas rápido y mejor.

[Plugin para Erlang]
En el caso de erlang me he instalado
vimerl[1] que entre sus muchas características como syntax highlight, code completion, auto-indent, etc... tiene una cosa que es una pasada que ellos llaman el QuickFix. Cada vez que guardas un fichero, comprueba si hay errores de sintaxis, variables no declaradas, variables no usadas, funciones no usadas, y demás... muy cómodo para no tener que hacer el proceso de: compilar, buscar errores en la consola, ver qué fichero y en qué línea han ocurrido, etc... E aquí un ejemplo. Fijaos que en la parte inferior de la pantalla nos pone una descripción del error en donde tenemos el cursor:


Los warnings los marca de color amarillento, mientras que los errores te los pone en rojo. Muy cómodo ya que cada vez que guardas el archivo puedes ver si tienes errores en el fichero o no. Muy chulo.

[Plugins generales]
Mas cosicas. Pues mira, uno que es básico, pero que hasta ahora no me gustaba mucho, es el NerdTree[2](vaya puto nombre feo le han puesto a la criatura). Le he dado una oportunidad y de momento va de lujo. Es un explorador de ficheros, con el que se puede navegar por la estructura de directorios des de Vim:


Cómodo y fácil de usar. Hay dos configuraciones que te pueden hacer la vida mas fácil. La primera es para poder ignorar ficheros, y que no aparezcan en el listado. Añadiendo en el .vimrc la siguiente línea, se consiguen omitir los resultados que hagan match a las expresiones regulares de la lista (en mi caso ignorar loc ficheros *.pyc )
let NERDTreeIgnore=['.*pyc$']
La segunda es añadir un keybinding para mostrar/ocultar el NerdTree. Esto se consigue añadiendo ésta línea en el .vimrc:
map :NERDTreeToggle
El pulsar F2, se abre/cierra el NerdTree. Muy cómodo también.

Y por último, un pluguin que complementa al NerdTree: CommandT[3]. Básicamente es un listado de tooodos los ficheros del proyecto y escribiendo un par de letras puedes abrir el fichero que quieras. Ejemplo:
Digamos que tenemos un fichero en www/back/back/controlers/dashboard.mako. Sería suficiente con escribir 'dash', como para que él ya te lo encuentre. Pero vamos a otro caso mas complicado en el que tenemos:
/www/back/back/templates/foo
/www/back/back/templates/bar
/www/back/back/templates/lala
/www/back/back/widget/foo/template.mako
/www/back/back/widget/bar/template.mako

En este caso, si quremos encontrar el fichero "/www/back/back/widget/foo/template.mako" será suficiente con escribir: "wifte", que se corresponde a:
  • "wi" de "widget"
  • "f" de "foo"
  • "te" de template
Aparte de mi burda explicación que he hecho aquí, recomiendo que mireis el screencast[4] hecho por el mismo creador del plugin.

También crearemos un keybinding para este plugin, de modo que la pulsar F3 se abra automáticamente CommandT. El comportamiento por defecto para ejecutar CommandT es pulsar la combinación t. La tecla es la tecla "\", que en teclado inglés está en una posición muy cómoda, pero en el teclado castellano está a tomar por culo. Para esto la mayoría de gente se redefine la tecla a la coma ",".
map :CommandT
let mapleader = "," " Change the key from \ fo ,


Con estos pluguins tenemos una combinación ideal, ya que si no sabemos qué buscamos, tenemos el NerdTree para navegar por los directorios y encontrar el fichero buscado. Si sabemos el nombre exacto, podemos usar CommantT para localizar el fichero, y palante toda. Por falta de recursos, que no sea.

He probado mas plugins (para buffers, tabs, cerrar paréntesis automáticamente, escribir html automáticament, etc..) pero ninguno de ellos me ha gustado. Pero la verdad que ha valido la pena encontrar estos tres plugins ya que mejoran mucho mi entorno de desarrollo.

Y nada mas por hoy. Un saludo, Jan.


[2] http://www.vim.org/scripts/script.php?script_id=1658 pero otra vez recomiendo mejor bajar la última versión de https://github.com/scrooloose/nerdtree
Post relacionado http://inedit00.blogspot.com.es/2010/04/dandole-cana-vim.html . Ahora que lo veo me parece super elemental, pero oye, que de esto hace 2 años y digamos que esto ya lo tengo bastante "mascado", jeje.