<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
 
 <title>dialelo</title>
 <link href="http://alejandrogomez.github.com/atom.xml" rel="self"/>
 <link href="http://alejandrogomez.github.com/"/>
 <updated>2013-03-06T11:14:52-08:00</updated>
 <id>http://alejandrogomez.github.com/</id>
 <author>
   <name>Alejandro Gómez</name>
   <email>alejandroogomez@gmail.com</email>
 </author>
 
 
 <entry>
   <title>Automatiza la ejecución de tus tests en Python con tdaemon</title>
   <link href="http://alejandrogomez.github.com/Python/TDD/productividad/2012/08/12/automatiza-la-ejecucion-de-tus-tests-con-tdaemon.html"/>
   <updated>2012-08-12T00:00:00-07:00</updated>
   <id>http://alejandrogomez.github.com/Python/TDD/productividad/2012/08/12/automatiza-la-ejecucion-de-tus-tests-con-tdaemon</id>
   <content type="html">&lt;p&gt;El desarrollo guiado por pruebas o &lt;em&gt;Test-Driven Development&lt;/em&gt; (en adelante TDD) es un ciclo de desarrollo que consiste en aplicar el siguiente algoritmo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Escribir un &lt;strong&gt;test automático que verifique una funcionalidad&lt;/strong&gt;.&lt;/li&gt;

&lt;li&gt;&lt;strong&gt;Ejecutar la prueba y verla fallar&lt;/strong&gt;.&lt;/li&gt;

&lt;li&gt;Escribir el &lt;strong&gt;mínimo código&lt;/strong&gt; que haga a la prueba pasar.&lt;/li&gt;

&lt;li&gt;&lt;strong&gt;Refactorizar&lt;/strong&gt; para eliminar duplicidad y conseguir un código limpio.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Los beneficios de utilizar esta práctica son numerosos, desde mantenerte concentrado en una sola tarea cada vez hasta generar una documentación &lt;em&gt;viva&lt;/em&gt; (en código) de la especificación del sistema que estamos desarrollando.&lt;/p&gt;

&lt;p&gt;Para una muy buena introducción al TDD en español recomiendo el libro &lt;a href='http://www.dirigidoportests.com/el-libro' title='Diseño Ágil con TDD, el libro'&gt;Diseño Ágil con TDD&lt;/a&gt; de &lt;a href='http://carlosble.com/' title='Blog de Carlos Ble'&gt;Carlos Ble&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id='tdaemon'&gt;tdaemon&lt;/h3&gt;

&lt;p&gt;&lt;a href='https://github.com/brunobord/tdaemon' title='Repositorio de tdaemon en GitHub'&gt;&lt;code&gt;tdaemon&lt;/code&gt;&lt;/a&gt; vigila un directorio y ejecuta los tests automáticamente cada vez que detecta un cambio.&lt;/p&gt;

&lt;p&gt;Esto nos permite un flujo de trabajo TDD sin necesidad de ejecutar las pruebas manualmente. &lt;code&gt;tdaemon&lt;/code&gt; está &lt;a href='http://pypi.python.org/pypi/tdaemon/' title='tdaemon en PyPI'&gt;disponible en PyPI&lt;/a&gt; para su descarga, por lo que podemos instalarlo con &lt;code&gt;pip&lt;/code&gt; fácilmente.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;tdaemon&lt;/code&gt; utiliza un programa adicional para ejecutar los tests, que por defecto es &lt;a href='http://pypi.python.org/pypi/nose/' title='nose en PyPI'&gt;nose&lt;/a&gt;. &lt;code&gt;nose&lt;/code&gt; descubre los tests de forma automática y es extensible mediante &lt;em&gt;plugins&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Vamos a instalar ambos programas para probar su funcionamiento:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='nv'&gt;$ &lt;/span&gt;pip install nose tdaemon
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Una buena forma para tener &lt;em&gt;feedback&lt;/em&gt; de los tests a medida que cambiamos el código es utilizar dos paneles en una ventana de &lt;a href='http://tmux.sourceforge.net/' title='Página oficial de tmux en Sourceforge'&gt;tmux&lt;/a&gt;, una para el editor y otra para ejecutar &lt;code&gt;tdaemon&lt;/code&gt;. Si te interesa saber más sobre &lt;code&gt;tmux&lt;/code&gt; &lt;a href='http://dialelo.com/VIM/productividad/tmux/2012/06/18/productividad-en-la-consola-con-vim-y-tmux.html' title='Productividad en la consola con VIM y tmux'&gt;mi artículo sobre productividad con VIM y tmux&lt;/a&gt; merece unos minutos de tu tiempo.&lt;/p&gt;

&lt;p&gt;Para empezar a utilizar &lt;code&gt;tdaemon&lt;/code&gt; no tenemos más que ejecutarlo en el directorio que queremos monitorizar. Opcionalmente podemos pasarle como argumento la ruta hasta el directorio que queremos que vigile. Aquí vemos como detecta un cambio y ejecuta los tests automáticamente:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='nv'&gt;$ &lt;/span&gt;tdaemon
Ready to watch file changes...
2012-08-12 18:06:46.964502
..................EE.....
&lt;span class='o'&gt;======================================================================&lt;/span&gt;
ERROR: Failure: ImportError &lt;span class='o'&gt;(&lt;/span&gt;cannot import name create_status&lt;span class='o'&gt;)&lt;/span&gt;
----------------------------------------------------------------------
&lt;span class='c'&gt;# ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id='integracin_con_django'&gt;Integración con Django&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;tdaemon&lt;/code&gt; soporta otros programas para ejecutar los tests, entre los que están &lt;a href='http://pytest.org' title='Página del proyecto py.test'&gt;py.test&lt;/a&gt; y el &lt;em&gt;test runner&lt;/em&gt; de &lt;a href='http://djangoproject.com' title='Página del framework web Django'&gt;Django&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Para utilizar &lt;code&gt;tdaemon&lt;/code&gt; en un proyecto Django, no tenemos más que invocarlo de la siguiente forma (en el directorio donde vive nuestro &lt;code&gt;manage.py&lt;/code&gt;):&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='nv'&gt;$ &lt;/span&gt;tdaemon --test-program&lt;span class='o'&gt;=&lt;/span&gt;django --custom-args&lt;span class='o'&gt;=&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;mi_app&amp;quot;&lt;/span&gt;
&lt;span class='c'&gt;# que ejecutará: &lt;/span&gt;
&lt;span class='c'&gt;#   $ ./manage.py test mi_app&lt;/span&gt;
&lt;span class='c'&gt;# cada vez que detecte un cambio&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr /&gt;
&lt;p&gt;Esto es todo por hoy, espero que os sea útil. Y vosotros ¿cómo hacéis TDD en Python?&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Recursos para aprender Python</title>
   <link href="http://alejandrogomez.github.com/Python/2012/06/28/recursos-para-aprender-python.html"/>
   <updated>2012-06-28T00:00:00-07:00</updated>
   <id>http://alejandrogomez.github.com/Python/2012/06/28/recursos-para-aprender-python</id>
   <content type="html">&lt;p&gt;En este artículo pretendo recopilar enlaces relacionados con Python que puedan servir tanto a principiantes como desarrolladores expertos. La intención es hacer de este artículo un documento vivo e ir añadiendo enlaces a medida que los propongáis; también añadiré la información que encuentre por las interwebs.&lt;/p&gt;

&lt;h1 id='documentacin'&gt;Documentación&lt;/h1&gt;

&lt;p&gt;Quizá quieras empezar leyendo &lt;a href='http://docs.python.org.ar/tutorial/contenido.html'&gt;el tutorial de Python en español&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;El sitio más obvio para aprender Python es la &lt;a href='http://docs.python.org'&gt;documentación oficial&lt;/a&gt;, es &lt;strong&gt;completísima&lt;/strong&gt; y de mucha calidad. Definitivamente tienes que guardarla en tus marcadores.&lt;/p&gt;

&lt;p&gt;A continuación enumero algunos recursos que me han ayudado muchísimo a la hora de aprender:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href='http://python-guide.org'&gt;Python guide&lt;/a&gt; está en desarrollo pero es muy prometedora, recomiendo a los principiantes leerla para absorber buenas prácticas desde el momento en que comienzan a experimentar con el lenguaje.&lt;/li&gt;

&lt;li&gt;&lt;a href='http://www.doughellmann.com/PyMOTW/'&gt;Python Module Of The Week&lt;/a&gt; contiene artículos cubriendo &lt;strong&gt;gran parte de la librería estándar&lt;/strong&gt; con ejemplos y artículos de muy buena calidad. Suelo consultarlo como referencia y cuando quiero probar algún módulo que desconozco.&lt;/li&gt;

&lt;li&gt;&lt;a href='http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html'&gt;Code like a Pythonista: Idiomatic Python&lt;/a&gt; es un documento que describe buenas prácticas a la hora de escribir código, existe una &lt;a href='http://mundogeek.net/traducciones/python-idiomatico/'&gt;traducción al español&lt;/a&gt; por &lt;a href='http://mundogeek.net/'&gt;Raúl González Duque&lt;/a&gt;.&lt;/li&gt;

&lt;li&gt;&lt;a href='http://www.python.org/dev/peps/pep-0008/'&gt;pep8&lt;/a&gt; es la guía de estilo Python que todo programador debería leer. Existe una &lt;a href='http://mundogeek.net/traducciones/guia-estilo-python.htm'&gt;versión traducida al español&lt;/a&gt; también por Raúl.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id='literatura'&gt;Literatura&lt;/h1&gt;

&lt;p&gt;No he leído muchos libros sobre Python así que espero sugerencias para incluir en esta sección.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href='http://www.diveintopython.net/'&gt;Dive into Python&lt;/a&gt; es un libro gratuito que enseña Python a base de ejemplos. Yo empecé a aprender leyéndolo y lo recomiendo si te gusta experimentar y &lt;em&gt;learn by doing&lt;/em&gt;.&lt;/li&gt;

&lt;li&gt;Los &lt;a href='http://rmi.net/~lutz/'&gt;libros de Mark Lutz&lt;/a&gt; han sido recomendados por &lt;a href='http://joedicastro.com'&gt;Joe Di Castro&lt;/a&gt;.&lt;/li&gt;

&lt;li&gt;&lt;a href='http://mundogeek.net/tutorial-python/'&gt;Python para todos&lt;/a&gt; es un libro &lt;strong&gt;completo y para todos los niveles&lt;/strong&gt; en español.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id='sitios_web'&gt;Sitios web&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;El &lt;a href='http://planet.python.org'&gt;agregador de blogs oficial de Python&lt;/a&gt; contiene artículos escritos por &lt;strong&gt;miembros destacados de la comunidad&lt;/strong&gt;, ahí puedes encontrar contenido de mucha calidad escrito por programadores expertos.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id='blogs_en_espaol'&gt;Blogs en español&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href='http://pybonacci.wordpress.com/'&gt;Pybonacci&lt;/a&gt; es un blog dedicado a la &lt;strong&gt;uso de Python en la computación científica&lt;/strong&gt; en español. Aunque no vayas a utilizar Python con dichos fines te recomiendo echarle un ojo.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id='comunidades'&gt;Comunidades&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href='http://www.reddit.com/r/python'&gt;El subreddit de Python&lt;/a&gt; es un sitios fantástico para encontrar buenos artículos sobre Python. Si estás dando tus primeros pasos quizá te sea útil el &lt;a href='http://www.reddit.com/r/learnpython'&gt;subreddit para aprender Python&lt;/a&gt;.&lt;/li&gt;

&lt;li&gt;El &lt;a href='http://python.majibu.org/'&gt;sitio de preguntas y respuestas Python Majibu&lt;/a&gt; es un sitio genial para interactuar con la comunidad hispanohablante de Python. El hombre detrás del proyecto es &lt;a href='http://joedicastro.com/'&gt;Joe Di Castro&lt;/a&gt;, ¡&lt;em&gt;kudos&lt;/em&gt; para él!&lt;/li&gt;

&lt;li&gt;&lt;a href='http://python.org.ar/pyar/'&gt;Python Argentina&lt;/a&gt; y &lt;a href='http://python-hispano.org/'&gt;Python Hispano&lt;/a&gt; son comunidades de entusiastas Python hispanohablantes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id='ejercicios'&gt;Ejercicios&lt;/h3&gt;

&lt;p&gt;En &lt;a href='http://www.pythonchallenge.com/'&gt;Python Challenge&lt;/a&gt; y &lt;a href='http://projecteuler.net'&gt;Project Euler&lt;/a&gt; puedes practicar lo aprendido después de leer alguno de los recursos de este artículo.&lt;/p&gt;

&lt;h1 id='programadores'&gt;Programadores&lt;/h1&gt;

&lt;p&gt;Una buena forma de aprender a escribir buen código es aprendiendo de programadores expertos, e incluso colaborando en sus proyectos. A continuación presento una lista de algunos de los que más me han inspirado:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href='http://lucumr.pocoo.org/'&gt;Armin Ronacher&lt;/a&gt; es el autor de &lt;a href='http://pygments.org/'&gt;Pygments&lt;/a&gt;, &lt;a href='http://sphinx.pocoo.org/'&gt;Sphinx&lt;/a&gt; y &lt;a href='http://flask.pocoo.org/'&gt;Flask&lt;/a&gt; entre otros muchos proyectos.&lt;/li&gt;

&lt;li&gt;&lt;a href='http://kennethreitz.com/'&gt;Kenneth Reitz&lt;/a&gt; es el autor de la popular librería &lt;a href='http://docs.python-requests.org/en/latest/index.html'&gt;requests&lt;/a&gt;, un programador muy prolífico y un tipo muy amigable.&lt;/li&gt;

&lt;li&gt;&lt;a href='http://www.holovaty.com/'&gt;Adrian Holovaty&lt;/a&gt; y &lt;a href='http://jacobian.org/'&gt;Jacob Kaplan-Moss&lt;/a&gt; son los autores del popular &lt;em&gt;framework&lt;/em&gt; para desarrollo web &lt;a href='http://djangoproject.com'&gt;Django&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id='boletines_de_noticias'&gt;Boletines de noticias&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href='http://www.PythonWeekly.com'&gt;Python Weekly&lt;/a&gt; y &lt;a href='http://pycoders.com/'&gt;Pycoder&amp;#8217;s Weekly&lt;/a&gt; son dos boletines de noticias semanales con artículos y proyectos Python.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;¿Qué recursos recomiendas tú?&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Los beneficios de liberar el código de turses</title>
   <link href="http://alejandrogomez.github.com/open%20source/turses/2012/06/20/los-beneficios-de-liberar-el-c%C3%B3digo-de-turses.html"/>
   <updated>2012-06-20T00:00:00-07:00</updated>
   <id>http://alejandrogomez.github.com/open%20source/turses/2012/06/20/los-beneficios-de-liberar-el-código-de-turses</id>
   <content type="html">&lt;p&gt;En este artículo voy a hablar de los beneficios que me ha aportado liberar el código de mis proyectos, concretamente de &lt;a href='http://github.com/alejandrogomez/turses'&gt;turses&lt;/a&gt;, esperando que anime a más de uno a hacer lo mismo.&lt;/p&gt;

&lt;h1 id='comunidad'&gt;Comunidad&lt;/h1&gt;

&lt;p&gt;Lo que más me fascina del software libre son las &lt;strong&gt;comunidades de personas que invierten su tiempo y esfuerzo por un bien común&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Empecé a trabajar en &lt;code&gt;turses&lt;/code&gt; porque quería usar Twitter desde la consola y no encontré ningún programa que se adaptara a mis necesidades. Cuando empecé a publicar las primeras versiones insistí a algunos de mis amigos para que se lo instalaran y me dieran &lt;em&gt;feedback&lt;/em&gt;, y sus sugerencias me ayudaron a pulir detalles que no habría sido capaz de percibir por mi cuenta.&lt;/p&gt;

&lt;p&gt;A medida que el proyecto ha ido avanzando algunas personas han empezado a utilizarlo por su propia voluntad, y:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Me han ayudado a &lt;strong&gt;descubrir y arreglar &lt;em&gt;bugs&lt;/em&gt;&lt;/strong&gt;;&lt;/li&gt;

&lt;li&gt;han &lt;strong&gt;sugerido e implementado nuevas funcionalidades y mejoras&lt;/strong&gt;;&lt;/li&gt;

&lt;li&gt;y hemos &lt;strong&gt;hablado sobre la dirección que podría tomar el proyecto&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;En definitiva, juntos hacemos que &lt;code&gt;turses&lt;/code&gt; sea mejor en cada nueva versión, y estoy muy agradecido por su tiempo y paciencia. Al crear comunidad alrededor de un proyecto éste cobra mucho más sentido, y, sinceramente, que esa comunidad exista me motiva muchísimo a dedicarle tiempo.&lt;/p&gt;

&lt;h1 id='aprendizaje'&gt;Aprendizaje&lt;/h1&gt;

&lt;p&gt;A pesar de ser un programa muy pequeño (~5000 líneas de código) estoy aprendiendo muchísimo escribiéndolo. Algunas de las cosas que me está aportando son:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Un mayor &lt;strong&gt;conocimiento del lenguaje de programación Python y su librería estándar&lt;/strong&gt;;&lt;/li&gt;

&lt;li&gt;la habilidad de &lt;strong&gt;aplicar patrones de diseño para resolver problemas comunes en el desarrollo de software&lt;/strong&gt;;&lt;/li&gt;

&lt;li&gt;comprender la &lt;strong&gt;importancia de escribir pruebas para asegurar la corrección de un programa y evitar regresiones&lt;/strong&gt;;&lt;/li&gt;

&lt;li&gt;la necesidad de &lt;strong&gt;mejorar mi inglés para mantener una buena comunicación&lt;/strong&gt; con las personas involucradas en el proyecto.&lt;/li&gt;
&lt;/ul&gt;</content>
 </entry>
 
 <entry>
   <title>Productividad en la consola con VIM y tmux</title>
   <link href="http://alejandrogomez.github.com/VIM/productividad/tmux/2012/06/18/productividad-en-la-consola-con-vim-y-tmux.html"/>
   <updated>2012-06-18T00:00:00-07:00</updated>
   <id>http://alejandrogomez.github.com/VIM/productividad/tmux/2012/06/18/productividad-en-la-consola-con-vim-y-tmux</id>
   <content type="html">&lt;p&gt;En este artículo voy a hablar sobre dos herramientas que utilizo en conjunto para desarrollar: &lt;a href='http://www.vim.org' title='Página oficial de VIM'&gt;&lt;code&gt;VIM&lt;/code&gt;&lt;/a&gt; y &lt;a href='http://tmux.sourceforge.net/' title='Página oficial de tmux en Sourceforge'&gt;&lt;code&gt;tmux&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h1 id='vim'&gt;VIM&lt;/h1&gt;

&lt;p&gt;Un editor de texto es &lt;strong&gt;la herramienta más importante de un programador&lt;/strong&gt;. Pasamos la mayor parte del tiempo dentro de él, así que es deseable que:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Podamos extenderlo con &lt;em&gt;scripts&lt;/em&gt; o &lt;em&gt;plugins&lt;/em&gt; para automatizar las tareas repetitivas e incorporar funcionalidad inexistente en el editor;&lt;/li&gt;

&lt;li&gt;se integre a la perfección con programas de terceros;&lt;/li&gt;

&lt;li&gt;no se interponga entre nuestras ideas y la codificación de éstas.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Me inclino por &lt;code&gt;VIM&lt;/code&gt;, pero &lt;a href='http://www.gnu.org/software/emacs/' title='Página oficial de GNU Emacs'&gt;Emacs&lt;/a&gt; es también una buena opción. Ambos tienen una curva de aprendizaje un tanto empinada pero una documentación y comunidad excelentes. Creo que dominar uno de estos dos editores de texto te hará un mejor programador y, sobre todo, incrementará tu productividad.&lt;/p&gt;

&lt;h1 id='tmux'&gt;tmux&lt;/h1&gt;

&lt;p&gt;&lt;code&gt;tmux&lt;/code&gt; es un multiplexor de terminales. Permite tener múltiples &lt;em&gt;ventanas&lt;/em&gt; compuestas de &lt;em&gt;paneles&lt;/em&gt; con una terminal en cada uno.&lt;/p&gt;

&lt;p&gt;&lt;img src='/img/tmux_vim.png' alt='VIM y tmux' /&gt;&lt;/p&gt;

&lt;p&gt;De entre los beneficios de usar &lt;code&gt;tmux&lt;/code&gt; destacaría los siguientes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Permite visualizar varias terminales simultáneamente;&lt;/li&gt;

&lt;li&gt;puedes redimensionar paneles a placer o usar alguno de los &lt;em&gt;layouts&lt;/em&gt; disponibles y dejar que &lt;code&gt;tmux&lt;/code&gt; lo haga por ti;&lt;/li&gt;

&lt;li&gt;provee un &lt;em&gt;portapapeles&lt;/em&gt; que permite copiar texto entre paneles;&lt;/li&gt;

&lt;li&gt;es programable y extremadamente configurable.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id='integrando_ambas_herramientas'&gt;Integrando ambas herramientas&lt;/h1&gt;

&lt;p&gt;Suelo utilizar una sesión de &lt;code&gt;tmux&lt;/code&gt; por proyecto, donde tengo abierta una ventana con el editor y las terminales que hagan falta: servidor, documentación, ejecución de pruebas, etc.&lt;/p&gt;

&lt;p&gt;Para organizar mis sesiones de &lt;code&gt;tmux&lt;/code&gt; utilizo una gema llamada &lt;a href='https://github.com/aziz/tmuxinator' title='Repositorio de tmuxinator en GitHub'&gt;tmuxinator&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id='tmuxinator'&gt;tmuxinator&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;tmuxinator&lt;/code&gt; nos permite declarar sesiones de &lt;code&gt;tmux&lt;/code&gt; en &lt;a href='http://www.yaml.org/' title='Página oficial del lenguaje de marcado YAML'&gt;YAML&lt;/a&gt;, que es fácil de leer para una persona, en vez de escribir un &lt;em&gt;script&lt;/em&gt; para crear la sesión.&lt;/p&gt;

&lt;p&gt;Para declarar una sesión ejecutamos el comando&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='nv'&gt;$ &lt;/span&gt;mux open &amp;lt;nombre&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;que abrirá nuestro &lt;code&gt;$EDITOR&lt;/code&gt; para editar una sesión con el &lt;code&gt;nombre&lt;/code&gt; que indiquemos.&lt;/p&gt;

&lt;p&gt;Ésta es la sesión que utilizo para desarrollar &lt;a href='http://github.com/alejandrogomez/turses'&gt;turses&lt;/a&gt;:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='yaml'&gt;&lt;span class='l-Scalar-Plain'&gt;project_name&lt;/span&gt;&lt;span class='p-Indicator'&gt;:&lt;/span&gt; &lt;span class='l-Scalar-Plain'&gt;turses&lt;/span&gt;
&lt;span class='l-Scalar-Plain'&gt;project_root&lt;/span&gt;&lt;span class='p-Indicator'&gt;:&lt;/span&gt; &lt;span class='l-Scalar-Plain'&gt;~/repos/turses/&lt;/span&gt;
&lt;span class='l-Scalar-Plain'&gt;pre&lt;/span&gt;&lt;span class='p-Indicator'&gt;:&lt;/span&gt; &lt;span class='l-Scalar-Plain'&gt;uzbl-tabbed http://github.com/alejandrogomez/turses/issues &amp;amp;&lt;/span&gt;
&lt;span class='l-Scalar-Plain'&gt;tabs&lt;/span&gt;&lt;span class='p-Indicator'&gt;:&lt;/span&gt;
  &lt;span class='p-Indicator'&gt;-&lt;/span&gt; &lt;span class='l-Scalar-Plain'&gt;dev&lt;/span&gt;&lt;span class='p-Indicator'&gt;:&lt;/span&gt; &lt;span class='l-Scalar-Plain'&gt;vim .&lt;/span&gt;
  &lt;span class='p-Indicator'&gt;-&lt;/span&gt; &lt;span class='l-Scalar-Plain'&gt;ipy&lt;/span&gt;&lt;span class='p-Indicator'&gt;:&lt;/span&gt; &lt;span class='l-Scalar-Plain'&gt;ipython&lt;/span&gt;
  &lt;span class='p-Indicator'&gt;-&lt;/span&gt; &lt;span class='l-Scalar-Plain'&gt;docs&lt;/span&gt;&lt;span class='p-Indicator'&gt;:&lt;/span&gt; &lt;span class='l-Scalar-Plain'&gt;cd docs &amp;amp;&amp;amp; vim .&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;La sesión se compone de tres ventanas: una para desarrollar, otra para el intérprete de Python y la tercera para escribir documentación. El comando en &lt;code&gt;pre&lt;/code&gt; abre un navegador con el &lt;em&gt;issue tracker&lt;/em&gt; del proyecto.&lt;/p&gt;

&lt;p&gt;Cuando ejecuto&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='nv'&gt;$ &lt;/span&gt;mux turses
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;se abre una nueva sesión de &lt;code&gt;tmux&lt;/code&gt; (si no estaba abierta ya) conteniendo las ventanas declaradas en el fichero YAML.&lt;/p&gt;

&lt;h3 id='vimux'&gt;vimux&lt;/h3&gt;

&lt;p&gt;&lt;a href='https://github.com/benmills/vimux' title='Repositorio de vimux en GitHub'&gt;vimux&lt;/a&gt; es un &lt;em&gt;plugin&lt;/em&gt; de &lt;code&gt;VIM&lt;/code&gt; que permite interactuar con &lt;code&gt;tmux&lt;/code&gt; desde el editor. Podemos lanzar un comando mientras estemos editando y se abrirá un panel, dividiendo horizontalmente la ventana actual.&lt;/p&gt;

&lt;p&gt;Por ejemplo podemos llamar a &lt;code&gt;nosetests&lt;/code&gt; para ejecutar nuestra suite de pruebas en un proyecto Python con el siguiente comando:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='sh'&gt;:call RunVimTmuxCommand&lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;nosetests&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;vimux&lt;/code&gt; también provee un atajo para repetir el último comando ejecutado:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='sh'&gt;:RunLastVimTmuxCommand
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content>
 </entry>
 
 <entry>
   <title>turses. Un cliente de Twitter con interfaz curses</title>
   <link href="http://alejandrogomez.github.com/Python/turses/2012/03/02/turses-un-cliente-de-twitter-con-interfaz-ncurses.html"/>
   <updated>2012-03-02T00:00:00-08:00</updated>
   <id>http://alejandrogomez.github.com/Python/turses/2012/03/02/turses-un-cliente-de-twitter-con-interfaz-ncurses</id>
   <content type="html">&lt;p&gt;He estado más de un mes sin escribir en el blog pero la verdad es que no he parado. Entre &lt;a href='http://mutualismo.es'&gt;Mutualismo&lt;/a&gt;, del que esperamos &lt;strong&gt;sacar una beta en menos de dos semanas&lt;/strong&gt;; mi PFC, al que tengo que dedicar la mayor parte del tiempo del que dispongo y &lt;a href='http://github.com/alejandrogomez/turses'&gt;turses&lt;/a&gt;, un cliente de Twitter con interfaz ncurses que estoy escribiendo en Python.&lt;/p&gt;

&lt;p&gt;Hace algún tiempo descubrí &lt;a href='http://tyrs.nicoshpere.net'&gt;Tyrs&lt;/a&gt; y me encantó, tenía exactamente lo que buscaba en un cliente de Twitter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;una &lt;strong&gt;interfaz curses&lt;/strong&gt; agradable a la vista;&lt;/li&gt;

&lt;li&gt;&lt;strong&gt;personalización&lt;/strong&gt; completa (mociones, acciones y programas externos);&lt;/li&gt;

&lt;li&gt;escrita en un lenguaje de programación que me es familiar.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Después de usarlo un par de semanas empecé a echar de menos cosas como:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;mover &lt;em&gt;buffers&lt;/em&gt; a placer;&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;añadir y borrar &lt;em&gt;buffers&lt;/em&gt; dinámicamente para:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;hacer &lt;strong&gt;búsquedas&lt;/strong&gt;;&lt;/li&gt;

&lt;li&gt;visualizar una &lt;strong&gt;conversación&lt;/strong&gt;;&lt;/li&gt;

&lt;li&gt;ver el &lt;strong&gt;perfil de un usuario&lt;/strong&gt;;&lt;/li&gt;

&lt;li&gt;ver los tweets de una &lt;strong&gt;lista&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Decidí contribuir al proyecto implementando alguna de estas funcionalidades. Me familiaricé con el código y, a pesar de ser un programa totalmente funcional, encontré que hay &lt;strong&gt;un gran acoplamiento&lt;/strong&gt; y todos los componentes conocen muchos detalles de los demás.Esto hace que sea más complicado añadir y/o modificar partes del programa.&lt;/p&gt;

&lt;p&gt;Así que empecé una &lt;strong&gt;implementación desde cero&lt;/strong&gt;, &lt;strong&gt;reusando gran parte del código del proyecto&lt;/strong&gt;. Aún está en fase beta, aunque ya tiene implementadas algunas funcionalidades básicas. He aquí un par de &lt;em&gt;screenshots&lt;/em&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href='/img/turses_welcome_screen.png' title='Pantalla de bienvenida'&gt;&lt;img src='/img/turses_welcome_screen.png' alt='turses_welcome_screen' /&gt;&lt;/a&gt; &lt;a href='/img/turses_buffers.png' title='Modo timelines'&gt;&lt;img src='/img/turses_buffers.png' alt='turses_buffers' /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Si os interesa podéis ver el &lt;a href='http://github.com/alejandrogomez/turses'&gt;código&lt;/a&gt;. Me vendría muy bien un poco de ayuda, aunque sea probándola y &lt;a href='http://github.com/alejandrogomez/turses/issues'&gt;reportando bugs&lt;/a&gt;.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Entornos virtuales de Python con pip, virtualenv y virtualenvwrapper</title>
   <link href="http://alejandrogomez.github.com/Python/2012/01/08/entornos-virtuales-de-python-con-pip-virtualenv-y-virtualenvwrapper.html"/>
   <updated>2012-01-08T00:00:00-08:00</updated>
   <id>http://alejandrogomez.github.com/Python/2012/01/08/entornos-virtuales-de-python-con-pip-virtualenv-y-virtualenvwrapper</id>
   <content type="html">&lt;p&gt;Como prometí en un artículo anterior voy a hablaros de tres herramientas que todo desarrollador Python debería conocer: &lt;a href='http://pypi.python.org/pypi/pip'&gt;&lt;code&gt;pip&lt;/code&gt;&lt;/a&gt;, &lt;a href='http://pypi.python.org/pypi/virtualenv'&gt;&lt;code&gt;virtualenv&lt;/code&gt;&lt;/a&gt; y &lt;a href='http://www.doughellmann.com/projects/virtualenvwrapper/'&gt;&lt;code&gt;virtualenvwrapper&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Cuando estamos trabajando en varios proyectos que utilizan Python instalar sus dependencias a nivel de sistema es una mala idea ya que:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;puede causar &lt;strong&gt;conflictos si dos proyectos utilizan distintas versiones del mismo paquete&lt;/strong&gt;;&lt;/li&gt;

&lt;li&gt;resulta más &lt;strong&gt;complicado mantener una lista de dependencias&lt;/strong&gt;;&lt;/li&gt;

&lt;li&gt;&lt;strong&gt;ensuciamos la instalación global de Python&lt;/strong&gt; con paquetes que no utilizamos en el día a día.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Es por ello que &lt;strong&gt;aislar las dependencias en un entorno virtual de Python hace mucho más cómodo y eficiente trabajar&lt;/strong&gt; en un proyecto.&lt;/p&gt;

&lt;p&gt;Vamos a ver qué es y cómo utilizar cada herramienta de las mencionadas.&lt;/p&gt;

&lt;h3 id='pip'&gt;pip&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;pip&lt;/code&gt; es la forma mas &lt;em&gt;sana&lt;/em&gt; de &lt;strong&gt;instalar paquetes&lt;/strong&gt; de Python que conozco, además de ser útil para &lt;strong&gt;hacer un seguimiento de las dependencias&lt;/strong&gt; de un proyecto. Es muy sencillo instalar paquetes de Python con &lt;code&gt;pip&lt;/code&gt;:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='nv'&gt;$ &lt;/span&gt;pip install fabric
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Si queremos conocer los paquetes Python que tenemos instalados utilizamos la opción &lt;code&gt;freeze&lt;/code&gt;:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='nv'&gt;$ &lt;/span&gt;pip freeze
&lt;span class='nv'&gt;Fabric&lt;/span&gt;&lt;span class='o'&gt;==&lt;/span&gt;1.3.3
&lt;span class='nv'&gt;Pygments&lt;/span&gt;&lt;span class='o'&gt;==&lt;/span&gt;1.4
&lt;span class='nv'&gt;pycrypto&lt;/span&gt;&lt;span class='o'&gt;==&lt;/span&gt;2.4.1
&lt;span class='nv'&gt;ssh&lt;/span&gt;&lt;span class='o'&gt;==&lt;/span&gt;1.7.11
&lt;span class='nv'&gt;wsgiref&lt;/span&gt;&lt;span class='o'&gt;==&lt;/span&gt;0.1.2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Para desinstalar un paquete hacemos:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='nv'&gt;$ &lt;/span&gt;pip uninstall fabric
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id='virtualenv'&gt;virtualenv&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;virtualenv&lt;/code&gt; es una herramienta que permite &lt;strong&gt;crear entornos virtuales de Python independientes de la instalación global&lt;/strong&gt;. Para instalarlo y crear un entorno virtual hacemos:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='nv'&gt;$ &lt;/span&gt;pip install virtualenv
&lt;span class='nv'&gt;$ &lt;/span&gt;virtualenv env
New python executable in env/bin/python2
Also creating executable in env/bin/python
Installing setuptools............done.
Installing pip...............done.
&lt;span class='nv'&gt;$ &lt;/span&gt;&lt;span class='nb'&gt;cd &lt;/span&gt;env/
&lt;span class='nv'&gt;$ &lt;/span&gt;ls -F
bin/            
include/
lib/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Veamos qué contiene el entorno virtual que acabamos de crear para comprender mejor su funcionamiento:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;bin&lt;/code&gt; contiene los binarios de Python, &lt;code&gt;pip&lt;/code&gt; y &lt;code&gt;easy_install&lt;/code&gt; además de varios &lt;em&gt;scripts&lt;/em&gt; para activar el entorno virtual.&lt;/li&gt;

&lt;li&gt;&lt;code&gt;include&lt;/code&gt; contiene un enlace simbólico al directorio donde están contenidos los &lt;em&gt;headers&lt;/em&gt; de la versión de Python que estemos utilizando, en mi caso apunta a &lt;code&gt;/usr/include/python2.7&lt;/code&gt;.&lt;/li&gt;

&lt;li&gt;&lt;code&gt;lib&lt;/code&gt; contiene los paquetes que hemos instalado en el entorno virtual.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hemos creado un entorno virtual, veamos cómo &lt;strong&gt;activarlo e instalar paquetes&lt;/strong&gt; en él:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='c'&gt;# activamos el entorno&lt;/span&gt;
&lt;span class='nv'&gt;$ &lt;/span&gt;&lt;span class='nb'&gt;source &lt;/span&gt;bin/activate            
&lt;span class='nv'&gt;$ &lt;/span&gt;pip freeze                     
&lt;span class='nv'&gt;wsgiref&lt;/span&gt;&lt;span class='o'&gt;==&lt;/span&gt;0.1.2
&lt;span class='c'&gt;# instalamos un paquete&lt;/span&gt;
&lt;span class='nv'&gt;$ &lt;/span&gt;pip install ipython            
&lt;span class='c'&gt;# ...&lt;/span&gt;
Successfully installed ipython
Cleaning up...
&lt;span class='nv'&gt;$ &lt;/span&gt;pip freeze
&lt;span class='nv'&gt;ipython&lt;/span&gt;&lt;span class='o'&gt;==&lt;/span&gt;0.12
&lt;span class='nv'&gt;wsgiref&lt;/span&gt;&lt;span class='o'&gt;==&lt;/span&gt;0.1.2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;El script &lt;code&gt;bin/activate&lt;/code&gt; modifica la variable de entorno &lt;code&gt;PATH&lt;/code&gt; para tener disponibles los paquetes del entorno virtual y cambia nuestro &lt;code&gt;PS1&lt;/code&gt; añadiendo al principio el nombre del entorno que hemos activado.&lt;/p&gt;

&lt;p&gt;Ahora tendremos disponibles los paquetes instalados en el entorno virtual independientemente de la instalación global. Cuando terminemos de trabajar en dicho entorno, lo desactivamos.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='nv'&gt;$ &lt;/span&gt;deactivate
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id='virtualenvwrapper'&gt;virtualenvwrapper&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;virtualenvwrapper&lt;/code&gt; es un conjunto de extensiones a &lt;code&gt;virtualenv&lt;/code&gt; que, entre otras funcionalidades, provee:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;centralización de los entornos virtuales&lt;/strong&gt; en un único directorio;&lt;/li&gt;

&lt;li&gt;&lt;em&gt;wrappers&lt;/em&gt; para la &lt;strong&gt;creación, copia y borrado de entornos virtuales&lt;/strong&gt;;&lt;/li&gt;

&lt;li&gt;un &lt;em&gt;script&lt;/em&gt; para &lt;strong&gt;movernos entre entornos fácilmente&lt;/strong&gt;;&lt;/li&gt;

&lt;li&gt;&lt;strong&gt;autocompletado&lt;/strong&gt; para comandos que reciben un entorno virtual como argumento.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Antes de nada tenemos que inicializar &lt;code&gt;virtualenvwrapper&lt;/code&gt; como sigue:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='c'&gt;# especificar un directorio donde se alojen los entornos&lt;/span&gt;
&lt;span class='nv'&gt;$ &lt;/span&gt;&lt;span class='nb'&gt;export &lt;/span&gt;&lt;span class='nv'&gt;WORKON_HOME&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;~/.virtualenvs
&lt;span class='nv'&gt;$ &lt;/span&gt;mkdir -p &lt;span class='nv'&gt;$WORKON_HOME&lt;/span&gt;
&lt;span class='c'&gt;# activar virtualenvwrapper&lt;/span&gt;
&lt;span class='nv'&gt;$ &lt;/span&gt;&lt;span class='nb'&gt;source&lt;/span&gt; /usr/local/bin/virtualenvwrapper.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Nos interesará ejecutar el script &lt;code&gt;virtualenvwrapper.sh&lt;/code&gt; cada vez que abrimos una terminal así que añade la última línea al fichero de inicialización de tu consola. La ruta del &lt;em&gt;script&lt;/em&gt; puede variar en tu distribución así que comprueba primero dónde se encuentra.&lt;/p&gt;

&lt;h4 id='crear_copiar_y_eliminar_entornos_virtuales'&gt;Crear, copiar y eliminar entornos virtuales&lt;/h4&gt;

&lt;p&gt;Los comandos para crear, copiar y eliminar entornos virtuales son los siguientes:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='c'&gt;# crear entornos virtual&lt;/span&gt;
&lt;span class='nv'&gt;$ &lt;/span&gt;mkvirtualenv scripting
&lt;span class='nv'&gt;$ &lt;/span&gt;mkvirtualenv web
&lt;span class='c'&gt;# crear un entorno copiando otro&lt;/span&gt;
&lt;span class='nv'&gt;$ &lt;/span&gt;cpvirtualenv scripting jugar
&lt;span class='c'&gt;# borrar un entorno &lt;/span&gt;
&lt;span class='nv'&gt;$ &lt;/span&gt;rmvirtualenv scripting
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id='moverse_entre_entornos_virtuales'&gt;Moverse entre entornos virtuales&lt;/h4&gt;

&lt;p&gt;El comando &lt;code&gt;workon&lt;/code&gt; nos permite movernos entre entornos virtuales muy fácilmente. Además tenemos disponible el autocompletado para los nombres de los entornos.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='c'&gt;# activar el entorno &amp;#39;web&amp;#39; &lt;/span&gt;
&lt;span class='nv'&gt;$ &lt;/span&gt;workon web
&lt;span class='c'&gt;# moverse al entorno &amp;#39;scripting&amp;#39;&lt;/span&gt;
&lt;span class='nv'&gt;$ &lt;/span&gt;workon scripting
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id='un_ltimo_consejo'&gt;Un último consejo&lt;/h3&gt;

&lt;p&gt;Podemos mantener un fichero con las dependencias de un proyecto fácilmente gracias a &lt;code&gt;pip&lt;/code&gt; y &lt;code&gt;virtualenv&lt;/code&gt;, haciendo:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='nv'&gt;$ &lt;/span&gt;pip freeze &amp;gt; pip-requirements.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Cuando queramos instalar dichas dependencias en un nuevo entorno no tenemos más que crearlo y ejecutar el siguiente comando con el entorno activado:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='nv'&gt;$ &lt;/span&gt;pip install -r pip-requirements.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Esto es muy útil al trabajar desde diferentes máquinas o al desarrollar un proyecto de forma colaborativa ya que nos permite tener las mismas versiones de las dependencias en cualquier lugar.&lt;/p&gt;

&lt;p&gt;Me gustaría leer opiniones y recoger un poco de &lt;em&gt;feedback&lt;/em&gt; sobre la forma que teneis de gestionar los distintos proyectos en los que desarrolláis con Python.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy hacking!&lt;/em&gt;&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Taming dependencies with pip - Carl Meyer</title>
   <link href="http://alejandrogomez.github.com/Python/2011/12/26/taming-dependencies-with-pip.html"/>
   <updated>2011-12-26T00:00:00-08:00</updated>
   <id>http://alejandrogomez.github.com/Python/2011/12/26/taming-dependencies-with-pip</id>
   <content type="html">&lt;p&gt;Si programais en Python seguramente habreis utilizado &lt;a href='http://pypi.python.org/pypi/pip' title='Página de pip en PyPI'&gt;&lt;code&gt;pip&lt;/code&gt;&lt;/a&gt; o &lt;a href='http://peak.telecommunity.com/DevCenter/EasyInstall' title='Sitio oficial de easy_install'&gt;&lt;code&gt;easy_install&lt;/code&gt;&lt;/a&gt; para gestionar paquetes. &lt;code&gt;pip&lt;/code&gt; se postula como el sustituto de &lt;code&gt;easy_install&lt;/code&gt; en cuanto a la instalación de paquetes y gestión de dependencias entre los mismos.&lt;/p&gt;

&lt;p&gt;Usado junto a &lt;a href='http://pypi.python.org/pypi/virtualenv' title='Página de virtualenv en PyPI'&gt;&lt;code&gt;virtualenv&lt;/code&gt;&lt;/a&gt; nos permite &lt;strong&gt;aislar conjuntos de módulos Python&lt;/strong&gt; para usarlos en proyectos específicos. En una entrada futura hablaré de cómo gestionar entornos virtuales con dicha herramienta.&lt;/p&gt;

&lt;p&gt;En esta charla &lt;a href='https://twitter.com/#!/carljm' title='Cuenta de Twitter de Carl Meyer'&gt;Carl Meyer&lt;/a&gt; explica la forma en que &lt;code&gt;pip&lt;/code&gt; &lt;strong&gt;encuentra e instala paquetes&lt;/strong&gt;, &lt;strong&gt;gestiona las dependencias&lt;/strong&gt; y da &lt;strong&gt;consejos sobre su configuración&lt;/strong&gt;.&lt;/p&gt;
&lt;iframe src='http://blip.tv/play/AYLU4XgC.html?p=1' frameborder='0' height='255' width='400'&gt;
&lt;/iframe&gt;&lt;embed src='http://a.blip.tv/api.swf#AYLU4XgC' type='application/x-shockwave-flash' style='display:none' /&gt;</content>
 </entry>
 
 <entry>
   <title>Javascript programming style and your brain - Douglas Crockford</title>
   <link href="http://alejandrogomez.github.com/JavaScript/2011/12/18/javascript-programming-style-and-your-brain.html"/>
   <updated>2011-12-18T00:00:00-08:00</updated>
   <id>http://alejandrogomez.github.com/JavaScript/2011/12/18/javascript-programming-style-and-your-brain</id>
   <content type="html">&lt;p&gt;Voy a recomendar vídeos de conferencias o &lt;em&gt;workshops&lt;/em&gt; que considero interesantes semanalmente. Para empezar he elegido una conferencia de &lt;a href='http://crockford.com' title='Sitio web personal de Douglas Crockford'&gt;Douglas Crockford&lt;/a&gt;, experto en JavaScript reconocido por popularizar el formato JSON y programar &lt;a href='http://jslint.com' title='JSLint'&gt;JSLint&lt;/a&gt;.&lt;/p&gt;
&lt;iframe src='http://player.vimeo.com/video/25606006' frameborder='0' height='225' width='400'&gt;
&lt;/iframe&gt;&lt;p&gt;&lt;a href='http://vimeo.com/25606006'&gt;Douglas Crockford - JavaScript Programming Style and Your Brain&lt;/a&gt; de &lt;a href='http://vimeo.com/pixi'&gt;pixilated ideas&lt;/a&gt; en &lt;a href='http://vimeo.com'&gt;Vimeo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Además de ser una charla entretenida enseña &lt;strong&gt;buenas prácticas&lt;/strong&gt; a la hora de escribir JavaScript, habla de las &lt;strong&gt;virtudes y defectos del lenguaje&lt;/strong&gt; y señala algunos &lt;strong&gt;errores&lt;/strong&gt; que son &lt;strong&gt;fáciles de cometer&lt;/strong&gt; y &lt;strong&gt;difíciles de descubrir&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Muchos consejos son aplicables a otros lenguajes también, así que si decides verlo seguro serán 45 minutos de tu vida muy bien invertidos.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Scripting con BeautifulSoup</title>
   <link href="http://alejandrogomez.github.com/Python/2011/12/10/scripting-con-beautifulsoup.html"/>
   <updated>2011-12-10T00:00:00-08:00</updated>
   <id>http://alejandrogomez.github.com/Python/2011/12/10/scripting-con-beautifulsoup</id>
   <content type="html">&lt;p&gt;En este artículo explicaré mediante un sencillo ejemplo cómo utilizar las librerías &lt;code&gt;urllib2&lt;/code&gt; y &lt;a href='http://www.crummy.com/software/BeautifulSoup/' title='Sitio de BeautifulSoup'&gt;&lt;code&gt;BeautifulSoup&lt;/code&gt;&lt;/a&gt; para escribir scripts que recorran sitios web extrayendo información. También podemos utilizarlas para automatizar tareas repetitivas que hagamos en ciertas páginas web.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;urllib2&lt;/code&gt; nos permite abrir sitios web como ficheros y &lt;code&gt;BeautifulSoup&lt;/code&gt; es un &lt;em&gt;parser&lt;/em&gt; de documentos XML. Al combinar las dos librerías tenemos muchas posibilidades. Como soy aficionado al manga he escrito un script que se descargue de &lt;a href='http://www.submanga.com' title='Sitio web de submanga'&gt;submanga&lt;/a&gt; el último capítulo de mis mangas favoritos.&lt;/p&gt;

&lt;p&gt;Lo primero que haremos será una función que obtenga el contenido de una URL y genere un objeto &lt;code&gt;BeautifulSoup&lt;/code&gt; que nos permita recorrer el HTML de forma sencilla.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='python'&gt;&lt;span class='kn'&gt;from&lt;/span&gt; &lt;span class='nn'&gt;urllib2&lt;/span&gt; &lt;span class='kn'&gt;import&lt;/span&gt; &lt;span class='n'&gt;urlopen&lt;/span&gt;

&lt;span class='kn'&gt;from&lt;/span&gt; &lt;span class='nn'&gt;BeautifulSoup&lt;/span&gt; &lt;span class='kn'&gt;import&lt;/span&gt; &lt;span class='n'&gt;BeautifulSoup&lt;/span&gt;


&lt;span class='n'&gt;BASE_URL&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;http://submanga.com&amp;#39;&lt;/span&gt;

&lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;soup&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;url&lt;/span&gt;&lt;span class='p'&gt;):&lt;/span&gt;
    &lt;span class='n'&gt;response&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;urlopen&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;url&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='n'&gt;BeautifulSoup&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;response&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;read&lt;/span&gt;&lt;span class='p'&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Las URL de la página principal de cada serie son de la forma &lt;code&gt;http://submanga.com/Nombre_serie&lt;/code&gt; y en ellas tenemos varios enlaces a los últimos episodios de la serie. Vamos a escribir una función para averiguar el número y URL del último episodio de una serie dada.&lt;/p&gt;

&lt;p&gt;Si observamos el árbol DOM de la página principal de una serie, vemos que:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;La lista de los últimos capítulos es un tabla;&lt;/li&gt;

&lt;li&gt;las celdas de dicha tabla tienen el atributo &lt;code&gt;class=&amp;quot;u&amp;quot;&lt;/code&gt;;&lt;/li&gt;

&lt;li&gt;cada celda tiene un enlace a la URL del episodio;&lt;/li&gt;

&lt;li&gt;el texto del enlace contiene el número del episodio.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href='/img/submanga_ultimos_episodios.png' title='últimos episodios'&gt;&lt;img src='/img/submanga_ultimos_episodios.png' alt='submanga_ultimos_episodios' /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vamos a escribir la función que devuelve el número y URL del último episodio de una serie.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='python'&gt;&lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;last_episode&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;serie&lt;/span&gt;&lt;span class='p'&gt;):&lt;/span&gt;
    &lt;span class='sd'&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class='sd'&gt;    Returns a tuple with the last episode&amp;#39;s number and URL for the given serie.&lt;/span&gt;
&lt;span class='sd'&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class='n'&gt;serie_url&lt;/span&gt;   &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;join&lt;/span&gt;&lt;span class='p'&gt;([&lt;/span&gt;&lt;span class='n'&gt;BASE_URL&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;serie&lt;/span&gt;&lt;span class='p'&gt;])&lt;/span&gt;
    &lt;span class='n'&gt;html&lt;/span&gt;        &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;soup&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;serie_url&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;    
    &lt;span class='n'&gt;us&lt;/span&gt;          &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;html&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;findAll&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;attrs&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;&lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='s'&gt;&amp;#39;class&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;u&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;})&lt;/span&gt;
    &lt;span class='n'&gt;episode_num&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;us&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;a&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;strong&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;text&lt;/span&gt;
    &lt;span class='n'&gt;episode_url&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;us&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;a&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='s'&gt;&amp;#39;href&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;
    &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='nb'&gt;int&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;episode_num&lt;/span&gt;&lt;span class='p'&gt;),&lt;/span&gt; &lt;span class='n'&gt;episode_url&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Ahora tenemos la URL del último episodio, que nos lleva a la página principal de dicho episodio. Si abrimos esa página, observamos que contiene un enlace al visor de comics de submanga para dicho episodio. Como el enlace tiene un &lt;code&gt;id&lt;/code&gt; concreto extraemos dicha URL muy fácilmente.&lt;/p&gt;

&lt;p&gt;&lt;a href='/img/submanga_pagina_episodio.png' title='Página principal de un episodio'&gt;&lt;img src='/img/submanga_pagina_episodio.png' alt='submanga_pagina_episodio' /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='python'&gt;&lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;episode_pages_url&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;episode_url&lt;/span&gt;&lt;span class='p'&gt;):&lt;/span&gt;
    &lt;span class='sd'&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class='sd'&gt;    Receives the URL of the main page of an episode and returns the URL where &lt;/span&gt;
&lt;span class='sd'&gt;    the episode pages are located.&lt;/span&gt;
&lt;span class='sd'&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class='n'&gt;html&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;soup&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;episode_url&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='n'&gt;url&lt;/span&gt;  &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;html&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;findAll&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;attrs&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;&lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='s'&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;l&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;})&lt;/span&gt;
    &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='n'&gt;url&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;][&lt;/span&gt;&lt;span class='s'&gt;&amp;#39;href&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt; &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;url&lt;/span&gt; &lt;span class='k'&gt;else&lt;/span&gt; &lt;span class='s'&gt;u&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Ahora vamos a analizar el visor de comics para poder navegar por las páginas del episodio y descargar las imágenes.&lt;/p&gt;

&lt;p&gt;Cada página contiene una imagen dentro de un enlace que lleva a la siguiente. Dicho enlace e imagen están contenidos dentro del primer &lt;code&gt;div&lt;/code&gt; así que es fácil extraerlos.&lt;/p&gt;

&lt;p&gt;&lt;a href='/img/submanga_pagina1_episodio.png' title='Primera página de un episodio'&gt;&lt;img src='/img/submanga_pagina1_episodio.png' alt='submanga_pagina1_episodio' /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cuando terminamos de leer un episodio tenemos una página especial que nos permite o volver a leer el episodio o pasar al siguiente.&lt;/p&gt;

&lt;p&gt;&lt;a href='/img/submanga_ultima_pagina_episodio.png' title='Última página de un episodio'&gt;&lt;img src='/img/submanga_ultima_pagina_episodio.png' alt='submanga_ultima_pagina_episodio' /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Estamos en condiciones de escribir funciones que nos permitan saber todas las URL correspondientes a las páginas de un episodio concreto. &lt;code&gt;page_urls&lt;/code&gt; recibe como parámetro el enlace a la primera página del visor de comics, que hemos extraído previamente con &lt;code&gt;episode_pages_url&lt;/code&gt;, y genera una lista con todas las URL de todas las páginas del manga.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='python'&gt;&lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;page_urls&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;episode_pages_url&lt;/span&gt;&lt;span class='p'&gt;):&lt;/span&gt;
    &lt;span class='sd'&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class='sd'&gt;    Returns a list with all the URLs corresponding to the given episode&amp;#39;s pages.&lt;/span&gt;
&lt;span class='sd'&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class='n'&gt;urls&lt;/span&gt;     &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;episode_pages_url&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;
    &lt;span class='n'&gt;next_url&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='k'&gt;lambda&lt;/span&gt; &lt;span class='n'&gt;url&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt; &lt;span class='n'&gt;soup&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;url&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;div&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;a&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='s'&gt;&amp;#39;href&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;
    &lt;span class='n'&gt;page_url&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;next_url&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;episode_pages_url&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='k'&gt;while&lt;/span&gt; &lt;span class='n'&gt;has_manga_page&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;page_url&lt;/span&gt;&lt;span class='p'&gt;):&lt;/span&gt;
        &lt;span class='n'&gt;urls&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;append&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;page_url&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
        &lt;span class='n'&gt;page_url&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;next_url&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;page_url&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='n'&gt;urls&lt;/span&gt;

&lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;has_manga_page&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;page_url&lt;/span&gt;&lt;span class='p'&gt;):&lt;/span&gt;
    &lt;span class='n'&gt;html&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;soup&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;page_url&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='ow'&gt;not&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;bool&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;html&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;findAll&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;attrs&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;&lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='s'&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;v&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;})))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Ahora podemos, dado el nombre de una serie, saber las URL de todas las páginas que contienen las imágenes del manga. Nos queda escribir una función que que encuentre las imágenes que queremos y las descargue.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='python'&gt;&lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;download_page&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;episode_page_url&lt;/span&gt;&lt;span class='p'&gt;):&lt;/span&gt;
    &lt;span class='sd'&gt;&amp;quot;&amp;quot;&amp;quot;Downloads the image of the manga page.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class='n'&gt;html&lt;/span&gt;        &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;soup&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;episode_page_url&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='n'&gt;img&lt;/span&gt;         &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;html&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;div&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;a&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;img&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='s'&gt;&amp;#39;src&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;
    &lt;span class='n'&gt;img_content&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;urlopen&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;img&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;read&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt;
    &lt;span class='n'&gt;name&lt;/span&gt;        &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;img&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;split&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s'&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)[&lt;/span&gt;&lt;span class='o'&gt;-&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;
    &lt;span class='n'&gt;written_img&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nb'&gt;open&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;name&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;w&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='n'&gt;written_img&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;write&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;img_content&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='n'&gt;written_img&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;close&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Ya tenemos todo lo que necesitamos para descargar el último episodio de nuestro manga favorito. Ahora podemos escribir algunas funciones más para completar el script.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='python'&gt;&lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;download_episode&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;episode_pages_url&lt;/span&gt;&lt;span class='p'&gt;):&lt;/span&gt;
    &lt;span class='sd'&gt;&amp;quot;&amp;quot;&amp;quot;Downloads the episode located in the given URL.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class='n'&gt;html&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;soup&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;episode_pages_url&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='n'&gt;pages&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;page_urls&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;episode_pages_url&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='nb'&gt;map&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;download_page&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;pages&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;

&lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;create_episode_dir&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;serie&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;num&lt;/span&gt;&lt;span class='p'&gt;):&lt;/span&gt;
    &lt;span class='sd'&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class='sd'&gt;    Creates a directory with series name, and a subdirectory with episode&amp;#39;s number.&lt;/span&gt;
&lt;span class='sd'&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class='kn'&gt;from&lt;/span&gt; &lt;span class='nn'&gt;os&lt;/span&gt; &lt;span class='kn'&gt;import&lt;/span&gt; &lt;span class='n'&gt;getcwd&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;chdir&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;mkdir&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;path&lt;/span&gt;
    &lt;span class='n'&gt;serie_dir&lt;/span&gt;   &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;path&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;join&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;getcwd&lt;/span&gt;&lt;span class='p'&gt;(),&lt;/span&gt; &lt;span class='n'&gt;serie&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; 
    &lt;span class='n'&gt;episode_dir&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;path&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;join&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;serie_dir&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nb'&gt;str&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;num&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
    &lt;span class='k'&gt;try&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;
        &lt;span class='n'&gt;mkdir&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;serie_dir&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='k'&gt;except&lt;/span&gt; &lt;span class='ne'&gt;OSError&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;
        &lt;span class='k'&gt;pass&lt;/span&gt;
    &lt;span class='k'&gt;try&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;
        &lt;span class='n'&gt;mkdir&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;episode_dir&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='k'&gt;except&lt;/span&gt; &lt;span class='ne'&gt;OSError&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;
        &lt;span class='k'&gt;print&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;&amp;gt;&amp;gt; Looks like you already have downloaded the episode... aborting&amp;#39;&lt;/span&gt;
        &lt;span class='nb'&gt;exit&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='n'&gt;chdir&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;episode_dir&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;

&lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;download_last_episode&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;serie&lt;/span&gt;&lt;span class='p'&gt;):&lt;/span&gt;
    &lt;span class='n'&gt;num&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;url&lt;/span&gt;    &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;last_episode&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;serie&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='n'&gt;create_episode_dir&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;serie&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;num&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='n'&gt;pages_url&lt;/span&gt;   &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;episode_pages_url&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;url&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;pages_url&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;
        &lt;span class='n'&gt;download_episode&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;pages_url&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='k'&gt;else&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;
        &lt;span class='k'&gt;print&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;/!&lt;/span&gt;&lt;span class='se'&gt;\\&lt;/span&gt;&lt;span class='s'&gt; Something went wrong /!&lt;/span&gt;&lt;span class='se'&gt;\\&lt;/span&gt;&lt;span class='s'&gt;&amp;#39;&lt;/span&gt;


&lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;__name__&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;
    &lt;span class='kn'&gt;from&lt;/span&gt; &lt;span class='nn'&gt;sys&lt;/span&gt; &lt;span class='kn'&gt;import&lt;/span&gt; &lt;span class='n'&gt;argv&lt;/span&gt;

    &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='nb'&gt;len&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;argv&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='o'&gt;&amp;gt;&lt;/span&gt; &lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;
        &lt;span class='n'&gt;serie&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;argv&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;
    &lt;span class='k'&gt;else&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;
        &lt;span class='nb'&gt;exit&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;

    &lt;span class='k'&gt;print&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;&amp;gt;&amp;gt; Downloading last episode of &lt;/span&gt;&lt;span class='si'&gt;%s&lt;/span&gt;&lt;span class='s'&gt;&amp;#39;&lt;/span&gt; &lt;span class='o'&gt;%&lt;/span&gt; &lt;span class='n'&gt;serie&lt;/span&gt;
    &lt;span class='n'&gt;download_last_episode&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;serie&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='nb'&gt;exit&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Ya podemos descargarnos los últimos capítulos de nuestro manga preferido desde la terminal con este pequeño script.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='nv'&gt;$ &lt;/span&gt;chmod +x submanga.py
&lt;span class='nv'&gt;$ &lt;/span&gt;./submanga.py Naruto
&lt;span class='nv'&gt;$ &lt;/span&gt;tree -F
.
├── Naruto/
│   └── 566/
│       ├── 10.jpg
│       ├── 11.jpg
│       ├── 12.jpg
│       ├── 13.jpg
│       ├── 14.jpg
│       ├── 15.jpg
│       ├── 16.jpg
│       ├── 17.jpg
│       ├── 18.jpg
│       ├── 1.jpg
│       ├── 2.jpg
│       ├── 3.jpg
│       ├── 4.jpg
│       ├── 5.jpg
│       ├── 6.jpg
│       ├── 7.jpg
│       ├── 8.jpg
│       └── 9.jpg
└── submanga.py
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Todo el código está alojado en mi &lt;a href='http://bitbucket.org/dialelo/scripts' title='&amp;quot;'&gt;repositorio de scripts en Bitbucket&lt;/a&gt; por si queréis usarlo, cualquier comentario o sugerencia es bienvenido, ¡salud!.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Gestionar plugins de VIM con git y pathogen</title>
   <link href="http://alejandrogomez.github.com/VIM/git/2011/12/06/gestionar-plugins-de-VIM-con-git-y-pathogen.html"/>
   <updated>2011-12-06T00:00:00-08:00</updated>
   <id>http://alejandrogomez.github.com/VIM/git/2011/12/06/gestionar-plugins-de-VIM-con-git-y-pathogen</id>
   <content type="html">&lt;p&gt;Llevo unos meses utilizando &lt;a href='http://www.vim.org/' title='Sitio web de VIM'&gt;VIM&lt;/a&gt; para editar texto, y ningún editor que conozco es tan eficiente y personalizable. En este artículo hablaré de cómo gestiono mis plugins además de otros ficheros de configuración utilizando el sistema de control de versiones &lt;a href='http://www.git-scm.com/' title='Sitio web de git'&gt;git&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href='/img/screenshot_VIM.png' title='Screenshot de mi escritorio con VIM abierto'&gt;&lt;img src='/img/screenshot_VIM.png' alt='VIM' /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href='http://www.vim.org/scripts/script.php?script_id=2332' title='Pathogen vimscript'&gt;Pathogen&lt;/a&gt; es un script para VIM que sirve para manipular la variable de entorno &lt;code&gt;runtimepath&lt;/code&gt; de forma sencilla. Esto nos permite, además de modificarla para acceder más rápidamente a los directorios que usamos a menudo, tener nuestros plugins en directorios privados para mantener limpio nuestro directorio &lt;code&gt;~/.vim&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Además, dado que en GitHub hay un &lt;a href='http://github.com/vim-scripts' title='Repositorios con todos los vim-script'&gt;mirror de todos los vim-script&lt;/a&gt; disponibles en en el sitio oficial de podemos usar la magia de &lt;code&gt;git&lt;/code&gt; y &lt;code&gt;pathogen&lt;/code&gt; para gestionar todos los plugins que usemos y tenerlos disponibles rápidamente desde cualquier parte.&lt;/p&gt;

&lt;p&gt;Lo primero que haremos será crear un nuevo repositorio y guardar nuestra configuración de VIM actual.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='nv'&gt;$ &lt;/span&gt;mkdir plugins-vim &lt;span class='o'&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class='nb'&gt;cd &lt;/span&gt;plugins-vim
&lt;span class='nv'&gt;$ &lt;/span&gt;git init
&lt;span class='nv'&gt;$ &lt;/span&gt;cp -R ~/.vim ~/.vimrc .
&lt;span class='nv'&gt;$ &lt;/span&gt;git add .
&lt;span class='nv'&gt;$ &lt;/span&gt;git commit -m &lt;span class='s1'&gt;&amp;#39;Ficheros de configuración de VIM&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Ahora vamos a hacer uso de una característica de &lt;code&gt;git&lt;/code&gt; muy interesante: los &lt;strong&gt;submódulos&lt;/strong&gt;. Un submodulo en &lt;code&gt;git&lt;/code&gt; es un &lt;strong&gt;repositorio externo&lt;/strong&gt; que referenciamos desde el nuestro. Una situación en la que puede sernos útil (además de en ésta) es cuando tenemos un proyecto que depende de cierta librería y queremos una referencia al repositorio donde está alojada.&lt;/p&gt;

&lt;p&gt;Podemos añadir &lt;code&gt;pathogen&lt;/code&gt; como un submódulo pero yo prefiero descargar solamente el script en vez del repositorio completo y guardarlo en &lt;code&gt;~/.vim/autoload/&lt;/code&gt;. Cuando abrimos VIM se ejecutan todos los scripts del directorio &lt;code&gt;autoload&lt;/code&gt;.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='nv'&gt;$ &lt;/span&gt;mkdir ~/.vim/autoload
&lt;span class='nv'&gt;$ &lt;/span&gt;curl -so ~/.vim/autoload/pathogen.vim https://raw.github.com/tpope/vim-pathogen/HEAD/autoload/pathogen.vim 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Ya tenemos &lt;code&gt;pathogen&lt;/code&gt; instalado, ahora sólo nos queda añadir las siguientes líneas al principio de nuestro &lt;code&gt;.vimrc&lt;/code&gt; para que añada al &lt;code&gt;runtimepath&lt;/code&gt; todos los directorios que hay debajo de &lt;code&gt;.vim/bundle/&lt;/code&gt; y cree la documentación necesaria.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='vim'&gt;&lt;span class='k'&gt;filetype&lt;/span&gt; off
&lt;span class='k'&gt;call&lt;/span&gt; pathogen#runtime_append_all_bundles&lt;span class='p'&gt;()&lt;/span&gt;
&lt;span class='k'&gt;call&lt;/span&gt; pathogen#&lt;span class='k'&gt;helptags&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt;
&lt;span class='k'&gt;filetype&lt;/span&gt; plugin indent &lt;span class='k'&gt;on&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Ya está todo preparado para que podamos tener nuestros plugins en subdirectorios de &lt;code&gt;.vim/bundle&lt;/code&gt; y que VIM los cargue. Lo que queda es añadir los plugins como submódulos al repositorio que hemos creado, vamos a hacerlo con uno de los indispensables: &lt;a href='http://github.com/scrooloose/nerdtree' title='Repositorio de NERDTree'&gt;NERDTree&lt;/a&gt;.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='nv'&gt;$ &lt;/span&gt;git submodule add http://github.com/scrooloose/nerdtree .vim/bundle/NERDTree
&lt;span class='nv'&gt;$ &lt;/span&gt;git submodule init
&lt;span class='nv'&gt;$ &lt;/span&gt;git submodule update
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Después de esta secuencia de comandos habremos &lt;strong&gt;inicializado y descargado el contenido del submódulo&lt;/strong&gt;. Ahora vamos a escribir un pequeño &lt;em&gt;script&lt;/em&gt; para sincronizar el contenido del repositorio con nuestro directorio &lt;code&gt;~&lt;/code&gt;, lo llamaremos &lt;code&gt;bootstrap&lt;/code&gt;.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='c'&gt;#!/bin/sh&lt;/span&gt;

&lt;span class='c'&gt;# descarga la última versión de pathogen&lt;/span&gt;
mkdir ~/.vim/autoload
curl -so ~/.vim/autoload/pathogen.vim https://raw.github.com/tpope/vim-pathogen/HEAD/autoload/pathogen.vim 

&lt;span class='c'&gt;# descarga la última versión de nuestro repo&lt;/span&gt;
git pull --rebase

&lt;span class='c'&gt;# descarga la última versión de los submódulos&lt;/span&gt;
git submodule init
git submodule update

&lt;span class='c'&gt;# sincroniza el contenido del repo con ~&lt;/span&gt;
rsync --exclude &lt;span class='s2'&gt;&amp;quot;.git&amp;quot;&lt;/span&gt; --exclude &lt;span class='s2'&gt;&amp;quot;bootstrap&amp;quot;&lt;/span&gt; -av . ~
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;El script asume que lo ejecutas desde el directorio en el que se aloja el repositorio.&lt;/p&gt;

&lt;p&gt;Espero que este artículo te haya servido para organizar mejor tus plugins de VIM. A partir de ahora, cuando estés en un entorno nuevo sólo tienes que &lt;strong&gt;descargar tu repositorio&lt;/strong&gt;, &lt;strong&gt;ejecutar el script&lt;/strong&gt; y &lt;strong&gt;tendrás toda tu configuración y plugins de VIM disponibles&lt;/strong&gt;. Útil, ¿verdad?&lt;/p&gt;

&lt;p&gt;Si eres programador y no utilizas VIM te recomiendo que le dediques un poco de tiempo a aprender a utilizarlo, merece la pena.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Crear un blog con Jekyll</title>
   <link href="http://alejandrogomez.github.com/Jekyll/2011/12/04/crear-un-blog-con-jekyll.html"/>
   <updated>2011-12-04T00:00:00-08:00</updated>
   <id>http://alejandrogomez.github.com/Jekyll/2011/12/04/crear-un-blog-con-jekyll</id>
   <content type="html">&lt;p&gt;Voy a escribir un par de artículos para explicar cómo crear y desplegar un blog usando &lt;a href='http://jekyllrb.com' title='Sitio oficial de Jekyll'&gt;Jekyll&lt;/a&gt;. En este artículo cubro la creación de un blog básico.&lt;/p&gt;

&lt;p&gt;Jekyll es una herramienta de fuentes abiertas que sirve para generar HTML estático a partir de texto escrito en lenguajes de marcado como &lt;a href='http://daringfireball.net/markdown' title='Markdown'&gt;Markdown&lt;/a&gt; o &lt;a href='http://www.textism.com/tools/textile/' title='Textile'&gt;Textile&lt;/a&gt;; además de HTML bruto. Está escrito en Ruby y hace uso de la librería &lt;a href='http://pygments.org' title='Pygments'&gt;Pygments&lt;/a&gt; de Python para el coloreado de sintáxis.&lt;/p&gt;

&lt;p&gt;Es simple, ligero y flexible. Hay muchos &lt;a href='http://github.com/mojombo/jekyll/wiki/Sites' title='Sitios web creados con Jekyll'&gt;ejemplos&lt;/a&gt; de sitios web que lo usan así que podemos partir del código de uno que nos agrade y modificarlo a nuestro gusto. Este blog que lees está basado en los estilos de &lt;a href='http://rebase.github.com' title='GitHub Rebase'&gt;Rebase&lt;/a&gt; y apenas he tenido que ajustar algunos detalles.&lt;/p&gt;

&lt;p&gt;Antes de nada, asegúrate de tener instalados:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python y &lt;a href='http://www.pip-installer.org/' title='pip'&gt;pip&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;Ruby y &lt;a href='http://rubygems.org/' title='gem'&gt;gem&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;El &lt;a href='http://github.com/mojombo/jekyll' title='repositorio de jekyll en GitHub'&gt;código fuente de Jekyll&lt;/a&gt; está disponible en GitHub pero por comodidad utilizaremos RubyGems para instalar las gemas necesarias. También nos hace falta la librería Pygments de Python.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='nv'&gt;$ &lt;/span&gt;gem install jekyll
&lt;span class='nv'&gt;$ &lt;/span&gt;pip install pygments
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Al instalar la gema de Jekyll se instalarán automáticamente sus dependencias. Jekyll utiliza &lt;a href='http://liquidmarkup.org/' title='Liquid'&gt;Liquid&lt;/a&gt; para insertar contenido en las plantillas HTML que definimos.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;En mi caso la última versión de Liquid me dió problemas con Pygments, la solución fue instalar la versión 2.2.2.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ahora ya podemos crear el blog. Para empezar creamos la estructura de directorios y los ficheros básicos.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='nv'&gt;$ &lt;/span&gt;mkdir mi_blog &lt;span class='o'&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class='nb'&gt;cd &lt;/span&gt;mi_blog
&lt;span class='nv'&gt;$ &lt;/span&gt;mkdir _includes _layouts _posts 
&lt;span class='nv'&gt;$ &lt;/span&gt;touch _config.yml _layouts/master.html _layouts/post.html index.html 
&lt;span class='nv'&gt;$ &lt;/span&gt;tree
.
|-- _config.yml       &lt;span class='c'&gt;# fichero YAML de configuración&lt;/span&gt;
|-- _layouts          &lt;span class='c'&gt;# plantillas HTML &lt;/span&gt;
|   |-- master.html
|   &lt;span class='sb'&gt;`&lt;/span&gt;-- post.html
|-- _posts
&lt;span class='sb'&gt;`&lt;/span&gt;-- index.html
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;El siguiente paso es rellenar el layout que sirve de base para todas las páginas del sitio que hemos creado en &lt;code&gt;_layouts/master.html&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;master.html&lt;/code&gt;&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='html'&gt;&lt;span class='cp'&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class='nt'&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
  &lt;span class='nt'&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class='nt'&gt;&amp;lt;title&amp;gt;&lt;/span&gt;{{ page.title }}&lt;span class='nt'&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class='nt'&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class='nt'&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  {{ content }}
  &lt;span class='nt'&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class='nt'&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Esta es la plantilla base. Hemos introducido los tags &lt;code&gt;{{ page.title }}&lt;/code&gt; y &lt;code&gt;{{ content }}&lt;/code&gt;, que sirven para inflar de contenido la plantilla.&lt;/p&gt;

&lt;p&gt;Antes de rellenar la plantilla para los artículos vamos a escribir &lt;code&gt;index.html&lt;/code&gt; y ver cómo se generan los sitios estáticos.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;index.html&lt;/code&gt;&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='html'&gt;---
layout: master
title:  Página principal
---
&lt;span class='nt'&gt;&amp;lt;nav&amp;gt;&lt;/span&gt;
  &lt;span class='nt'&gt;&amp;lt;a&lt;/span&gt; &lt;span class='na'&gt;href=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;/about.html&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;About&lt;span class='nt'&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  &lt;span class='nt'&gt;&amp;lt;a&lt;/span&gt; &lt;span class='na'&gt;href=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;/atom.xml&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;Feed&lt;span class='nt'&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class='nt'&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
&lt;span class='nt'&gt;&amp;lt;section&lt;/span&gt; &lt;span class='na'&gt;id=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;content&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
  &lt;span class='nt'&gt;&amp;lt;article&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;{{ site.posts.first.title }}&lt;span class='nt'&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;div&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;body&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
      {{ site.posts.first.content }}
    &lt;span class='nt'&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class='nt'&gt;&amp;lt;/article&amp;gt;&lt;/span&gt;
&lt;span class='nt'&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Observad que hemos introducido una cabecera en YAML para especificar el layout que vamos a rellenar cuando inflemos esta plantilla. Así, cuando Jekyll crea el fichero &lt;code&gt;index.html&lt;/code&gt; estático, hace lo siguiente:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Procesa esta plantilla rellenando los tags de Liquid.&lt;/p&gt;

&lt;p&gt;Los tags del tipo &lt;code&gt;{{ site.posts.first.* }}&lt;/code&gt; acceden a la variable global &lt;code&gt;site&lt;/code&gt; para tomar atributos del primer post e insertarlos en la plantilla.&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;Infla el layout &lt;code&gt;master&lt;/code&gt; con el HTML que ha generado en el paso anterior.&lt;/p&gt;

&lt;p&gt;En &lt;code&gt;_layouts/master.html&lt;/code&gt; Liquid sustituirá el tag &lt;code&gt;{{ page.title }}&lt;/code&gt; por el &lt;code&gt;title&lt;/code&gt; que hemos especificado en la cabecera YAML de &lt;code&gt;index.html&lt;/code&gt; y &lt;code&gt;{{ content }}&lt;/code&gt; por el HTML generado en el paso anterior.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Escribimos la plantilla para los artículos individuales.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;post.html&lt;/code&gt;&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='html'&gt;---
layout: master
---
&lt;span class='nt'&gt;&amp;lt;div&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;post&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
&lt;span class='nt'&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;{{ page.title }}&lt;span class='nt'&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class='nt'&gt;&amp;lt;div&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;body&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;{{ content }}&lt;span class='nt'&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class='nt'&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Después de crear los layouts, escribimos nuestro primer post. En este caso utilizo el lenguaje Markdown, pero puedes usar Textile o HTML crudo. Los nombres de los artículos siguen la convención &lt;code&gt;año-mes-día-título.lenguaje-marcado&lt;/code&gt; así que nuestro primer artículo lo llamaremos &lt;code&gt;2011-12-04-hola-mundo.md&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;2011-12-04-hola-mundo.md&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;
---
layout: post
title:  Hola mundo
---

Este es mi **primer** artículo.

{% highlight sh %}
$ echo &quot;¡Hola mundo!&quot;
{% endhighlight %}
&lt;/pre&gt;
&lt;p&gt;Lo guardamos en el directorio &lt;code&gt;_posts&lt;/code&gt; y ya estamos listos para probar el blog. Vamos a modificar nuestro &lt;code&gt;_config.yml&lt;/code&gt; para lanzar un servidor localmente y ver los cambios reflejados según vamos trabajando.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='yaml'&gt;&lt;span class='c1'&gt;# recrea los ficheros estáticos si se modifican&lt;/span&gt;
&lt;span class='l-Scalar-Plain'&gt;auto&lt;/span&gt;&lt;span class='p-Indicator'&gt;:&lt;/span&gt;     &lt;span class='l-Scalar-Plain'&gt;true&lt;/span&gt;
&lt;span class='c1'&gt;# lanza un servidor al ejecutar Jekyll&lt;/span&gt;
&lt;span class='l-Scalar-Plain'&gt;server&lt;/span&gt;&lt;span class='p-Indicator'&gt;:&lt;/span&gt;   &lt;span class='l-Scalar-Plain'&gt;true&lt;/span&gt;
&lt;span class='c1'&gt;# permite utilizar el tag highlight&lt;/span&gt;
&lt;span class='l-Scalar-Plain'&gt;pygments&lt;/span&gt;&lt;span class='p-Indicator'&gt;:&lt;/span&gt; &lt;span class='l-Scalar-Plain'&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Por defecto Jekyll genera los HTML estáticos en el subdirectorio &lt;code&gt;_site&lt;/code&gt; pero podemos cambiarlo en la configuración o al invocar &lt;code&gt;jekyll&lt;/code&gt; desde la línea de comandos.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='nv'&gt;$ &lt;/span&gt;jekyll
&lt;span class='c'&gt;# jekyll genera los siguientes HTML estáticos&lt;/span&gt;
&lt;span class='nv'&gt;$ &lt;/span&gt;tree _site
.
|-- 2011
|   &lt;span class='sb'&gt;`&lt;/span&gt;-- 12
|       &lt;span class='sb'&gt;`&lt;/span&gt;-- 04
|           &lt;span class='sb'&gt;`&lt;/span&gt;-- hola-mundo.html
&lt;span class='sb'&gt;`&lt;/span&gt;-- index.html
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Tendremos un servidor en el puerto 4000 sirviendo el HTML estático que ha generado Jekyll y regenerando los ficheros que cambiemos. Abre &lt;code&gt;localhost:4000&lt;/code&gt; en tu navegador favorito y&amp;#8230; &lt;em&gt;voilá&lt;/em&gt;.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Hola mundo</title>
   <link href="http://alejandrogomez.github.com/2011/12/03/hola-mundo.html"/>
   <updated>2011-12-03T00:00:00-08:00</updated>
   <id>http://alejandrogomez.github.com/2011/12/03/hola-mundo</id>
   <content type="html">&lt;p&gt;Llevaba tiempo queriendo abrir un blog para escribir sobre programación, compartir enlaces e ideas que considero interesantes y tener un sitio web &lt;strong&gt;propio&lt;/strong&gt; donde expresarme.&lt;/p&gt;

&lt;p&gt;Hace unos días descubrí &lt;a href='http://github.com/mojombo/jekyll' title='Jekyll'&gt;Jekyll&lt;/a&gt;, un generador de sitios estáticos escrito en &lt;a href='http://ruby-lang.org' title='Ruby'&gt;Ruby&lt;/a&gt; que me llamó la atención. Es sencillo y muy flexible, y el hecho de poder extenderlo fácilmente mediante plugins escritos en Ruby me animó aún más a usarlo ya que me gustaría aprender el lenguaje.&lt;/p&gt;

&lt;p&gt;Después de echar un ojo a unos cuantos blogs generados con Jekyll, copié los estilos de &lt;a href='http://rebase.github.com' title='Rebase'&gt;Rebase&lt;/a&gt; y aquí está el mío. El favicon lo encontré en &lt;a href='http://defaulticon.com' title='Default icon'&gt;Default Icon&lt;/a&gt; y los comentarios los he implementado usando &lt;a href='http://disqus.com' title='Disqus'&gt;Disqus&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;En la próxima entrada hablaré de cómo crear un blog como este y desplegarlo fácilmente. Para los impacientes, el código del blog está alojado en &lt;a href='http://bitbucket.org/dialelo/dialelo' title='Código del blog en Bitbucket'&gt;Bitbucket&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;¡Hasta pronto!&lt;/p&gt;</content>
 </entry>
 
 
</feed>
 
