Cambios (¿a lo Feng shui?)

Sólo una entrada rápida, comentando sobre algunos cambios estéticos y prácticos en este blog.

  • El tema: El más visible, siempre que no me leas por correo o RSS (http://en NULL.wikipedia NULL.org/wiki/RSS). Este es un poco más ligerito, aunque me sigue gustando la combinación de colores del anterior. Pero es necesario e importante que la página cargue rapidito, que no estamos para comer cascarita de caña.
  • Plugin de coloración de código: Este es el encargado de darle color a mis secciones de código fuente, cuando estoy escribiendo en Python, HTML, etc. Sí, son esas muelas llenas de palabras de colores que ustedes se saltan, mientras refunfuñan “ya empezó con las cochinadas que nadie entiende…” y fugazmente les pasa la imagen de Neo (http://en NULL.wikipedia NULL.org/wiki/Neo_(The_Matrix)) por la cabeza. La mejora es que sólo se cargará lo estrictamente necesario (antes cargaba tremenda mano de mierda ¡por gusto!). Lo malo fue que tuve que actualizar par de posts para que se vieran correctamente (en concreto, este y este).
  • Wordbooker stripdown: Wordbooker (http://wordpress NULL.org/extend/plugins/wordbooker/), el plugin que provee integración con Facebook, está muy bueno y concreto, pero es un poco pesadito estar cargando los botoncitos de “Me gusta” en la página frontal y en los posts individuales. Si les gusta, porfa, le dan el clic en Facebook. Total, pasan más tiempo allá que en mi blog (bueno, pasan más tiempo allá que en muchos más sitios, la verdad).
  • Highslide desactivado: Este plugin (http://www NULL.scrollleiste NULL.de/online/highslide-integration-in-wordpress) hace un efecto interesante con las imágenes, mediante el cual puedo mostrar inicialmente una versión pequeña de la imagen, y con un clic se muestra la versión a tamaño completo. Pero se jodió, no sé si fue cuando actualicé a WordPress 3 (http://wordpress NULL.org/news/2010/06/thelonious/) o cambié de tema…

Me recordaron (http://leo NULL.rbol NULL.org/) que es muy recomendable incluir imágenes con los posts… así que por cumplir:

790px Fengshui Compass 300x253 Cambios (¿a lo Feng shui?) (http://en NULL.wikipedia NULL.org/wiki/File:Fengshui_Compass NULL.jpg)
Feng shui Luopan compass

Lo que hay que hacer…

Hace poco me hice mi cuentecilla en Twitter (http://twitter NULL.com/mandx_ramblings). Me gusta la idea, es algo más sencillo y práctico que Facebook (http://www NULL.facebook NULL.com/), y no tiene sus problemas con la privacidad. Pues por defecto es público. Si entras ahí, es porque transmitir algo a la “internetósfera”. Y sobre todo, no hay que ir a Twitter (http://twitter NULL.com) para postear. Hay muchas aplicacioncillas para todos los gustos, colores. sistemas operativos y gadgets que nos permiten actualizar nuestro estado desde la comodidad de una GUI nativa.

Lo primero que pensé es si se podía enlazar la cuenta de Twitter con la de Facebook (http://www NULL.facebook NULL.com/gmandx), de manera que lo que tire por el primero, automágicamente también se publique en el segundo. Claro que se puede (¿¡cómo puedes pensar lo contrario!?), usando la aplicación que tiene Facebook para Twitter (http://apps NULL.facebook NULL.com/twitter/). Acá hay una pequeña jodedera que puede atentar contra la disponibilidad de la aplicación, y es que Facebook quiere obligarnos a pasar por su paginita para hacer vida “socio-digital”. Pero ni pinga. Al Facebook entro cuando me dé la gana.

Por el momento, con esa aplicación lo que se postee en Twitter aparecerá en Facebook, pero no se cumple la viceversa, por las rencillas anteriores. Pero estoy bien con eso. Y me dí a la caza de un buen cliente para el twitteo.

Y que funcione en mi Ubuntu, mal pensados. Una de las primeras herramienticas que más me gustó fue Echofon (http://www NULL.echofon NULL.com/twitter/firefox), una extensión para Firefox que se integra muy bien con este. Pero tenía la gran pega de no tener integrado la funcionalidad de acortar links en la misma interfaz. (Explico: cada twitteo o post tiene un máximo de 140 caracteres, por lo que si quieres compartir un link, debes usar un servicio acortador de URLs para disponer de más espacio para tus propios comentarios. Algunos de estos servicios son bit.ly (http://bit NULL.ly) e is.gd (http://is NULL.gd/)). Y pasando por varios, terminé con Turpial (http://code NULL.google NULL.com/p/turpial/), pero igual tiene sus cositas que no me gustan, que terminé plasmándolas en su respectivo bug tracker (http://code NULL.google NULL.com/p/turpial/issues/list).

Y al tiempo me doy cuenta del otro fenómeno. Casi todo lo que me interesa leer, lo agrego a mi Google Reader (http://www NULL.google NULL.com/reader/) y de tantos feeds que tengo ahí, muchas cosas son interesantillas. Por lo que cuando quiero compartirlo por el Twitter, ahí comienza el festival del copia-pega-copialink-acortalink-pegalink… Hasta que se me ocurre buscar una forma de enviar mis elementos compartidos en Google Reader (http://www NULL.google NULL.com/reader/shared/gmandx) a mi Twitter!

Y casi de los primeros resultados de una búsqueda en San Google, llegué a la paginita (http://www NULL.quickonlinetips NULL.com/archives/2008/12/share-google-reader-on-twitter/) que me dió la luz. Usando el servicio Twitter Feed (http://twitterfeed NULL.com/) se publicarán los nuevos elementos de un feed RSS como twitteos.

Y otra pega. Este servicio sólo soporta feeds en formato RSS (http://en NULL.wikipedia NULL.org/wiki/RSS), y Google tiene la costumbrita de exportar todos sus feeds en formato Atom (http://en NULL.wikipedia NULL.org/wiki/Atom_(standard)). A la larga los dos son más o menos lo mismo, pero ahora la diferencia de formato es una jodedera. Y me di a la búsqueda de algún convertidor online de feeds Atom a feeds RSS.

Encontré par de ellos, pero se marean y se funden y no sirve. Terminé creando otra entrada (http://feeds NULL.feedburner NULL.com/MandxsShared) en FeedBurner (http://feedburner NULL.com), ya que este tiene una funcionalidad que automáticamente convierte el formato del feed en dependencia del lo que esté leyendo el feed. Y al fin, ya puedo compartir cosas en el Reader que automágicamente aparecerán en el Twitter. Se acabó la fiesta del copia-pega. Yupi.

Ahora que lo pienso… Como tendré menos (pero mucho menos) necesidad de usar un acortador de URLs, posiblemente pueda evaluar volver a usar Echofon… ya veré.

Terminé con este andamiaje:

publishing flow Lo que hay que hacer...
Flujo de publicación de Mandx

Qué friki soy.

Mi Mundialito

Este no es el post que tenía en cola, pero aprovecho la víspera de la final de la XIX Copa Mundial de Fútbol Sudáfrica 2010 (http://en NULL.wikipedia NULL.org/wiki/2010_FIFA_World_Cup) para dejar mis humildes impresiones sobre este evento que ha movido al mundo (¡y a Shakira! ¡Waka Waka!).

Sólo sigo el fútbol en los mundiales, y sobre todo, cuando empiezan los octavos de final, donde “si pierdes, te vas”. Esta es la etapa donde se pone interesante, donde ya hay que ponerle el empeño para avanzar.

No soy seguidor de un equipo en particular, ni mucho menos “cambia-casacas” (que son los que sobran) que siguen a varios equipos al mismo tiempo, e incluso los ordenan por preferencia; todo con tal de cuando se acabe el Mundial, poder decir “¡Gané!”.

No sigo ningún equipo no por temor a que mi preferido me decepcione; si no porque no he tenido la afición suficiente como para que me guste su estilo o técnica. No obstante; siempre, al empezar cada juego, termino yéndole invariablemente a uno de los dos participantes. Así, involuntariamente.

Este ha sido el mundial de las sorpresas. Para terminar, tenemos una final nunca vista. Pero lo que más me agradó fue la cantidad de equipos latinoamericanos que pasaron a octavos de final, llegándose a decir que este mundial se quedaría en Latinoamérica. No le iba a ningún equipo en particular, pero quería que fuera latinoamericano.

Eso de irle a Brasil o a Argentina, está gastado. No me decido por ellos sólo por que mucha gente acá los apoyan. ¿Qué me dicen de Paraguay? ¡Y más importante! ¡Uruguay! Como dice el Garrincha, con una bola de cojones (http://garrix NULL.blogspot NULL.com/2010/07/semifinales-con-una-bola-de-cojones NULL.html). Ver como Luisito Suárez, en el juego anterior, se “quemó ls manos” en el tiempo de descuento con tal de que no entrara ese gol de Ghana;, y luego Diego Forlán, tó deswavinao de los tendones, le metió un gol de ensueño a los holandeses piscineros esos… Una de las pocas veces que se me erizó la piel por un gol. Ese juego me mantuvo gritando en casa ajena hasta el último silbatazo.

Pero perdieron… Incluso, los que estaban viendo el juego conmigo, no le iban a Uruguay, pero querían sangre naranja… Por haber sacado a Brasil del Mundial a base de teatro. ¡Esto es de pinga, mis queridos amiguitos!

No tendré una final latinoamericana, de verdad quería que ganara Uruguay. Tendré que conformarme con irle a España, así al menos que gane alguien que habla mi mismo idioma.

Mucho se ha hablado de este Mundial (como en todos claro) en cuanto a estadísticas, maldiciones, predicciones y otras yerbas. La de hoy era que el que elimina a Argentina siempre se va en la siguiente fase (esta se cumplió icon biggrin Mi Mundialito ). Incluso llegué a ver una pirámide que decía quién iba a ganar, basándose en los campeonatos anteriores (siento no tener el link a mano, Lu, porfa, ponla en un comentario). Si mal no recuerdo, ahí estaban hablando cáscara.

Otro fenómeno a tener en cuenta ha sido el uso de las redes sociales con motivo del Mundial. Twitter (http://twitter NULL.com) estrenando página dedicada a la ocasión. Otros sitios utilizándolo como ganchos para aumentar las visitas. Que si usan tecnología para las decisiones apretadas. Lo que se forma en los estadios con las jugadas problemáticas. Las reseñas de los partidos pasados. Los porteros quejándose de las pajarerías de la Jabulani (http://en NULL.wikipedia NULL.org/wiki/Adidas_Jabulani) (que resulta que la final se jugará con una versión doradita de la pelotita). Que si la salá cornetica suena mucho y no deja oír otra cosa en el estadio y que cómo se ha convertido en símbolo de este evento.

En fin, todo un acontecimiento, el más mundial de los mundiales. El que hace que Larissa Riquelme prometiera encuerarse para su país si ganaba Paraguay. El que hace que la actriz porno Bobbi Eden prometa sexo oral a sus 20.000 seguidores en Twitter, si gana su selección. El que logra mover multitudes y corazones. Y sí, también a Shakira.

220px Adidas Jabulani Gold 1 Mi Mundialito
Adidas Jo'bulani, la pelota del último juego.

¡Waka Waka!

¡Ubuntu es una mierda! ¡Odio Linux!

Me dice a cada rato el Leo (http://leo NULL.rbol NULL.org). Todo porque nos encontramos con un bug de Ubuntu 9.10 (http://en NULL.wikipedia NULL.org/wiki/List_of_Ubuntu_releases#Ubuntu_9 NULL.10_ NULL.28Karmic_Koala NULL.29) y el módem no se le conecta a más de 33Kbps (que tampoco lo hacía con el Wintendo (http://en NULL.wikipedia NULL.org/wiki/Microsoft_windows)).

Pero nunca deja claro el porqué de su afirmación. Lo más probable es que sea sólo por joder. Hasta que deje de hacerle caso y tenga que reparticionar él solo.

A veces pienso en cuan difícil sería adoptar Ubuntu (o Linux (http://en NULL.wikipedia NULL.org/wiki/Linux) en general) por otros usuarios, sobre todo los que han usado Windows desde que tienen conciencia informática (sin contar esa primera experiencia en la escuela secundaria con teclados “inteligentes” MSX (http://en NULL.wikipedia NULL.org/wiki/MSX) BASIC (http://en NULL.wikipedia NULL.org/wiki/MSX_BASIC) o MS-DOS (http://en NULL.wikipedia NULL.org/wiki/Ms-dos) con TurboPascal (http://en NULL.wikipedia NULL.org/wiki/Turbopascal) en el Preuniversitario).

No soy un filántropo, por lo que mi conjunto de muestra es la gente con la que trabajo icon biggrin ¡Ubuntu es una mierda! ¡Odio Linux! . Y me doy cuenta de que es una cuestión de elección (y esa es la verdadera belleza de todo esto; pero sobre eso, más adelante). Sobre todo, me concentro en usuarios de escritorio (el mundillo de los servers no es el mío) y en el entorno cubano actual, como veo a toda la comunidad informática (si se le puede llamar así) en estos tiempos. Y pensando, a la hora de migrar, hay que tener varias cosas en mente:

  • Hay gente que sólo necesita hacer su trabajo. Y necesitan un software que le permita hacer su trabajo, y hay que buscar el equivalente del software en Linux del que usaban en Windows (casi siempre lo hay, pero muchas veces hay que buscar cuál es, que ya no es tan evidente). En las últimas versiones de Ubuntu, el “Ubuntu Software Center” trata de resolver este problema, guiando al usuario por las aplicaciones más populares por categorías. Pero todavía falta. Por ejemplo, prácticamente todo diseñador graduado domina (y en muchos casos, sólo domina) Adobe CS (http://en NULL.wikipedia NULL.org/wiki/Adobe_cs). Y no es que no haya equivalente es que trabajan con Adobe CS. Muchos de ellos me han dicho que usarían Linux de muy buena gana, si sólo esta suite de diseño se ejecutara perfectamente ahí.
  • Aprender a usar las nuevas aplicaciones y el sistema en general. Pues por mucho que los programas sirvan para lo mismo, la mayoría de las veces no son simples clones o portes. Además, aún cuando los conceptos de interfaz visual y escritorio pueden ser los mismos, Linux es otra filosofía.
  • El tema de los juegos. Si tienes una buena tarjeta gráfica, no vamos a desperdiciarla con sólo poner Compiz con toda la pacotilla activada. A veces queremos sumergirnos en otro mundo, para olvidar las penas (y sin recurrir al alcohol). Pero el mayor problema de los juegos en Linux, es simplemente que no los hay para esta plataforma. No es cuestión de controladores o rendimiento. Por suerte, esto está cambiando, pero hay que esperar. Por el momento los aventurados pueden intentar con varias opciones como Wine (http://en NULL.wikipedia NULL.org/wiki/Wine_(software)), PlayOnLinux (http://www NULL.playonlinux NULL.com/) y Cedega (http://www NULL.transgaming NULL.com/) (las dos últimas son aplicaciones de pago, derivadas de la primera). Pero nunca será lo mismo (por eso le llamo “Wintendo”).
  • Terminal Fear“. No es el último taquillazo de Hollywood, es simplemente pensar que cuando hay que abrir una consola y meter varios comandos, ya todo es difícil, mal hecho, y porqué-no-puedo-hacer-eso-haciendo-clic. Es una cosa psicológica (que ellos lo vean terrible o que yo lo vea cómodo, hay que ver quién está mal de la cabeza).
  • La comunidad de soporte. Cuando ocurre un problema, ¿a quién acudir? No todos (por acá) tienen acceso a Internet, y también hay que saber buscar (Google (http://www NULL.google NULL.com) lo sabe todo, pero no sabe qué tú quieres). ¿Y a quién llamar? Nuestra “comunidad linuxera” no se puede llamar precisamente amplia. Gente que sepa lo suficiente de Linux, tenga disposición de ayudar y tenga el tiempo para hacerlo, desgraciadamente, es muy escasa. Por lo que puedes terminar con un problema por varias semanas, esperando por el friki que te sabe arreglar el problema, pero el pobre no tiene tiempo para pasar por casita y ayudarte. Y en el peor de los casos, el problema es de los gordos, por lo que te desesperas (“¡Que va! ¡No estoy pa’ esto!”) y llamas al socito de antes, para que te instale el Wintendo, los controladores (http://en NULL.wikipedia NULL.org/wiki/Hardware_driver) y los programas que te hacen falta – y si tienes el socio, pues a veces no es gratis.
  • Soporte para hardware: Con Windows, casi todas las computadoras tienden a comportarse igual. Lento o rápido, dependiendo de cuanto tengas y cuán reciente es. En Linux es otra historia, pues no siempre los fabricantes habilitan controladores para esta plataforma (y todos sus sabores). Muchos de esos controladores son contribuciones, y funcionan en la mayoría de los casos, pero no en todos. Windows no tiene controladores para todos los dispositivos*, por lo que debemos tener a mano el disco con controladores que vino con la máquina. Pero es un requisito a la hora de obtener el equipo, por lo que es algo más o menos con lo que se puede contar. Linux viene con bastantes controladores integrados en el núcleo, otros vienen aparte en el repositorio, fácilmente instalables. Prácticamente en todos los equipos recientes (y notan recientes) todo debe ir bien. La gran excepción es con los módems, puesto que la mayoría de ellos son Winmodems (http://en NULL.wikipedia NULL.org/wiki/Winmodem), y muy distintos internamente.

* Ya no es tan así con Windows Vista (http://en NULL.wikipedia NULL.org/wiki/Windows_Vista) y 7 (http://en NULL.wikipedia NULL.org/wiki/Windows_7). Estos incluyen en su disco muchos controladores para la mayoría de las placas y tarjetas gráficas recientes; pero es por eso que ahora Windows viene en DVD, y se lleva tanto espacio de instalación. Es un mal necesario, pues para poder tener Aero la arquitectura gráfica tuvo que cambiar en favor del rendimiento, pues estaban arrastrando una arquitectura usada desde Windows 2000 (http://en NULL.wikipedia NULL.org/wiki/Windows_2000) y XP (http://en NULL.wikipedia NULL.org/wiki/Windows_XP). Al cambiar la arquitectura, cambian drásticamente los controladores, y mucha gente no los tiene actualizados todavía. Pero Windows tenía que funcionar cuando se instale, por lo que después de la petición de Microsoft, todos entregaron versiones básicas de los controladores con la nueva arquitectura. No responder a esa petición significa que el sistema operativo más usado no funcionará con tu producto, por lo que los usuarios terminan comprando otro (y eso es malo para el negocio).

Porqué Ubuntu

Me gusta Ubuntu. Mejor que cualquier otro. Al punto de que si tuviera una Mac (http://www NULL.apple NULL.com/mac/), también le instalo el Ubuntu. Porque con GNU/Linux, se trata de la elección. Para cada componente del sistema, siempre hay opciones para instalar. Los desarrolladores de Ubuntu deciden que, por defecto, el programa de mensajería instantánea es Empathy (http://live NULL.gnome NULL.org/Empathy). Pidgin (http://pidgin NULL.im/) es incomparablemente mejor. Y luego de dos comandos en una consola, bye-bye Empathy, welcome back Pidgin. Así de simple. Sin embargo en Windows, para quitar el dichoso MSN Messenger que no se usa, hay que recurrir a programitas externos o al más oscuro e indocumentado de los comandos. They just don’t want to give you the choice.

Al principio pensé: “¿Y dónde busco programitas para Linux?”. Quitando la molestia de tener un repositorio local de software para Linux (http://en NULL.wikipedia NULL.org/wiki/Advanced_Packaging_Tool#Sources) (¡30 GB!), prácticamente lo que te dé la gana de encontrar, está ahí. Tá bien, tá bien… no encontraste la pelusa de la contrapelusa softwarística. En Internet hay mil dos (and counting) lugares donde descargar la solución a tus problemas. Por que lo mejor es que se promueve la idea de compartir con la comunidad. Y lo más probable es que a alguien más, le haya hecho falta la pelusilla que quieres. Gratis. Y con la receta.

Ok, whatever… ¿Por fin, qué es mejor, Windows o Linux?

Cualquier sistema operativo, a la larga, es una herramienta (para trabajar, comunicarse, entretenerse). Todos tienen sus ventajas y desventajas; y para gustos los colores. No hay ninguno mejor.

Ubuntu y los GNU/Linux están cobrando fuerza en el sector de Pcs de escritorio, en laptops y en netbooks. También están apareciendo soluciones basadas en Linux para dispositivos móviles. El sector de servidores y supercomputadoras está completamente dominado por el pingüino.

Pero ni Mac ni Windows van a desaparecer. Siempre habrán productos comerciales hechos por compañías con prácticas monopolísticas. Muchas veces, con ansias de ganar dinero, el producto termina siendo lo mismo con cirugía estética. Y publicidad. Y a veces estúpida, como cuando Microsoft instaló un punto de venta justo enfrente del Simposio Linux en Japón (http://picasaweb NULL.google NULL.com/cschlaeger/JapanLinuxSymposium#5395400000458161906).

La gente debe conocer las opciones, ¿no? Y que sepan que pueden elegir. Microsoft Office es la mejor suite ofimática (demasiado dinero tiene para que no lo sea), pero no es la única. Si me preguntan qué es mejor, mi respuesta es siempre será Linux, pero esa no es “la respuesta”. En todo caso, aunque sea una mierda, yo escojo Ubuntu porque:

What’s life without whimsy? (http://en NULL.wikipedia NULL.org/wiki/Sheldon_Cooper)

Además el Leo lo que está es despechado. Confió en el Windows Vista que venía de fábrica con su laptop, y confió tanto que ni antivirus tenía instalado. Cuando llegó el momento en que ni podía crear una carpeta en el escritorio, fue que consideró instalar Ubuntu. Y sabemos que las relaciones por despecho nunca terminan bien. Incluso, como nombre de máquina le puso ”pharmakos (http://en NULL.wikipedia NULL.org/wiki/Pharmakos)” (el chivo expiatorio usado en la antigua Grecia para despojar a una comunidad de los males que la acechaban).

Como dice el Moya (http://twitter NULL.com/maykelmoya/statuses/15175517710):

Report: #Google moving away from #Windows – http://bit.ly/d29xMo (http://bit NULL.ly/d29xMo) who will be next? /via @glynmoody

Crónicas de un upgrade

Primera vez que actualizo Ubuntu (http://www NULL.ubuntu NULL.com) de una distribución a otra. Uso Ubuntu desde la versión 7.10 (http://en NULL.wikipedia NULL.org/wiki/Lucid_Lynx#Ubuntu_7 NULL.10_ NULL.28Gutsy_Gibbon NULL.29) (¡tres añitos ya!). Ojalá pudiera decir que todo fue “como la seda”, pero no… no en mi caso. Quizás es cuestión de suerte, hay otras historias de éxito (http://www NULL.luilver NULL.com/2010/05/happy-ubuntu-1004 NULL.html) en todo esto de la instalación, pero a mi no me tocó.

Sin embargo, estoy usando la versión actualizada. Quizás en unos días me dé el loco y reinstale desde cero. Pero, al menos, esta actualización no ha sido tan mala; hay cosas que me molestan todavía, pero se pueden sobrellevar (por el momento al menos icon wink Crónicas de un upgrade ).

Es lógico pensar que actualizar es la opción correcta, sobre en este mundillo de Linux y APT (http://en NULL.wikipedia NULL.org/wiki/Advanced_Packaging_Tool). Pero es reconocido (http://www NULL.facebook NULL.com/profile NULL.php?id=703869714) que el mecanismo de actualización de distribución de APT es un “mojón”. Ahí es cuando los Ubunteros repensaron el problema y crearon una herramienta que guía la actualización y el proceso de eliminación de paquetes obsoletos, actualización del los ya existentes e instalación de los nuevos que llegan. Una especie de lazarillo (http://www NULL.wordreference NULL.com/definicion/lazarillo) para APT, pues de distribución a distribución hay muchos cambios importantes que no se resuelven con sólo actualizar cada uno de los paqueticos a una versión superior. A ver, todo esto es mi apreciación del proceso, tampoco me he puesto a escarbar en el código del lazarillo (por cierto, está hecho en Python ¡OMG!). Simplemente ejecuté la instalación en modo consola (es más descriptiva y es algo más flexible) y leí todo lo que pasaba.

The fun part

Como uso un Squid local para la navegación (friki, ¿eh?) puedo ver todo lo que se pide en cuanto a navegación Web. El administrador de actualizaciones pide un archivito que luego ejecuta en modo gráfico. Lo que hago es detener la descarga y bajarlo yo mismo (en los registros quedó la URL) y lo ejecuto así (luego de extraerlo, claro):

$ sudo python dist-upgrade.py –frontend=DistUpgradeViewText

Después de varias advertencias (de que va a quitar, a poner y actualizar), terminó pero con serios errores. Un paquete de Python (http://python NULL.org/) (python2.6-minimal) no se quería actualizar, pues había otro corrupto (¡no hice nada, lo juro!) que no se desinstalaba bien. Fue aquí donde la “seda” se estrujó y se rompió. Y como ya Python es tan importante, otro bulto de paquetes no se querían actualizar también, pues al fallar la configuración de un paquete, se aborta la operación completa. Luego de varios cocotazos, encontré el paquete impertinente (python2.6-soaplib), al que le tuve que borrar unos archivos que dejaba regados por ahí.

Luego de eso, reinicio la instalación (él sabe donde se quedó) y todo fue bien.

My own Lynx (http://en NULL.wikipedia NULL.org/wiki/Lucid_Lynx#Ubuntu_10 NULL.04_LTS_ NULL.28Lucid_Lynx NULL.29)

A mi koalita (http://en NULL.wikipedia NULL.org/wiki/Lucid_Lynx#Ubuntu_9 NULL.10_ NULL.28Karmic_Koala NULL.29) ya le había metido bastantes parches y apuntalamientos. Para mí, es una de las versiones de Ubuntu con más problemas. Uno de los problemas que más me molestaba era que no podía tener PulseAudio (http://en NULL.wikipedia NULL.org/wiki/Pulseaudio) y mi módem Conexant al mismo tiempo. Cuando instalaba el driver (http://www NULL.linuxant NULL.com/) del módem, PulseAudio se perdía por completo (ALSA (http://en NULL.wikipedia NULL.org/wiki/Advanced_Linux_Sound_Architecture) seguía funcionando). Pero luego de unos cuantos hacks logré que PulseAudio saliera del medio y se mostrara ALSA como dispositivo de sonido. De todas maneras, el control de volumen de Gnome estaba casado con PulseAudio, así que tuve que inventar con otros.

Al menos el lince trae esto arreglado. PulseAudio está andando y mi módem también, así que ya tiene un puntico a su favor. Otras cositas que he notado, sin ningún orden en particular:

  • El applet indicador (http://launchpad NULL.net/indicator-applet) ahora muestra dos accesos más relacionados con el correo: “Redactar nuevo mensaje” y “Contactos”. Es algo más rápido para estas dos opciones, pues antes tenía que abrir Evolution (http://en NULL.wikipedia NULL.org/wiki/Evolution_(software)) o buscar su ventana.
  • El tema visual, el controvertido tema visual, está bastante bien… pero hay íconos que no sale bien, como el del volumen (luego de rescatarlo). También, me choca un poco los colores opacos contrastando con el naranja brillante de las barras de progreso y los bordes de los controles. Y los botones de la barra de título… bueno, estoy usando el tema “Radiance” con los bordes de ventana “New Wave”.
  • MusicApplet (http://www NULL.kuliniewicz NULL.org/music-applet/) ya se entera cuando Audacious (http://www NULL.audacious-media-player NULL.org/) está en pausa.
  • GNOME MPlayer (http://code NULL.google NULL.com/p/gnome-mplayer/) no muestra cacharros ni áreas blancas cuando se redimensiona, pierde el foco o se toca una tecla.
  • Mozilla ThunderBird 3 (http://www NULL.mozillamessaging NULL.com/en-US/thunderbird/). Este bicho promete, pero no puedo cambiarme fácilmente desde Evolution. Comencé a usar Evolution precisamente por las búsquedas rápidas, que es ahora lo que le agregaron Thunderbird. Si aparece un buen tutorial sencillo que permita exportar todo Evolution a Thunderbird 3 (correo, contactos, calendarios, filtros, etc…), consideraré cambiarme.
  • Retomando la reproducción de video, GNOME MPlayer no parece estar usando vídeo por superposición (http://en NULL.wikipedia NULL.org/wiki/Hardware_overlay), y me doy cuenta por que deformando la ventana con Compiz (http://en NULL.wikipedia NULL.org/wiki/Compiz), también se deforma el video. No obstante, el rendimiento es bastante bueno, por lo que implicaría mejoras en el driver libre para las tarjetas ATI (http://www NULL.amd NULL.com/).
  • Ya se dieron cuenta donde es que vivo y lograron mandarme el CD gratis (que declaran 0.13 EUR por el envío). No obstante, ya no vienen con pegatinas icon sad Crónicas de un upgrade la razón principal por la cual lo pedía…

Quizás se me hayan ido algunas otras mejoras más… icon wink Crónicas de un upgrade Las iré publicando aquí mismo en lo que me tropiezo con ellas.

Blog improvements

He agregado algunos plugincillos para WordPress (http://wordpress NULL.org), siempre pensando en la comodidad de los lectores:

  • Advanced User Agent Displayer (http://www NULL.moallemi NULL.ir/en/blog/2009/09/20/advanced-user-agent-displayer/): Mostrar información sobre el agente de usuario (http://en NULL.wikipedia NULL.org/wiki/User_agent) de los autores de los comentarios. En cubano: chismorreteo con que si usas Linux o Windows, Mozilla Firefox o Internet Explorer. (También sé de un plugin (http://wordpress NULL.org/extend/plugins/anti-internet-explorer-6/) que previene que entres al sitio usando Internet Explorer 6, mostrando un mensajito diciendo que este blog se merece algo mejor (http://www NULL.mozilla NULL.com/en-US/products/firefox/). Por supuesto, instalarlo aquí sí sería discriminación)
  • Comments On Feed (http://www NULL.moallemi NULL.ir/en/blog/2009/12/18/comments-on-feed-for-wordpress/): Incluir los comentarios de cada post en el feed (http://en NULL.wikipedia NULL.org/wiki/Web_feed). Así cuando se lean los post en un lector de feeds, también se incluirán los comentarios, evitando tener que ir al post en el blog sólo para leer lo que otros dijeron. De todas maneras, hay que ir al post para comentar.
  • Subscribe To Comments (http://txfx NULL.net/code/wordpress/subscribe-to-comments/): Si te decidiste a comentar, puede que también quieras saber qué se dijo después. Esto es lo que hace este plugincillo: agrega una casilla de verificación donde el que está comentando podrá suscribirse a los comentarios siguientes al suyo. Incluso, es posible (al parecer) configurar otras opciones de suscripción, como el correo al cual enviar los nuevos comentarios o desuscribirse de los comentarios de un post específico.

También consideré instalar Feed Delay (http://www NULL.moallemi NULL.ir/en/blog/2010/02/25/feed-delay-for-wordpress/), que demora la publicación de un post en los feeds. La idea es que el post no (me) sale perfecto a la primera; siempre se me olvida alguna coma, repito palabras, etc… Por ejemplo, FeedBurner (http://feedburner NULL.google NULL.com) está revisando a cada ratico lo nuevo que hay en el feed, y enseguida publica lo nuevo, dando lugar a que los suscriptores reciban un post que todavía contiene errorcillos que se escaparon. Pero no lo tengo activado, pues resulta que los suscriptores que me señalan las erratas (¡gracias!) están suscritos con FeedBurner; muy rara vez se llegan por el sitio.

También quiero ver cómo me va con estos plugins antes de instalarlos en otros blogs que mantengo, evaluarlos, ver pros y contras, etc. (Ok ok, lo de mostrar qué navegadores usa la gente es frikandá mía.)

Cufón testing

He recibido algunas visitas a este blog, con palabras claves como “cufon tildes, cufon problemas buscadores”. Para empezar, resulta que es “cufón“, con tilde, como podemos ver en el sitio original (http://cufon NULL.shoqolate NULL.com/). Esto puede ser influyente en nuestras búsquedas; si sabemos lo que buscamos, San Google (http://www NULL.google NULL.com) lo encontrará más rápido.

Pero no me desvío. El propósito de este post es hacer mis propias pruebas con Cufón para que el que llegue buscando sobre el tema, encuentre algo que pueda ayudarle en el tema.

¿Qué es cufón, me dicen? Según los autores (http://wiki NULL.github NULL.com/sorccu/cufon/about):

Cufón aims to become a worthy alternative to sIFR (http://wiki NULL.novemberborn NULL.net/sifr/), which despite its merits still remains painfully tricky to set up and use.

Sencillo, ¿eh? icon biggrin Cufón testing Ahora en serio: sIFR y Cufón son tecnologías que permiten usar prácticamente cualquier fuente en nuestras páginas Web. Ambas técnicas emplean Javascript (http://en NULL.wikipedia NULL.org/wiki/Javascript) para reemplazar el texto de ciertos elementos de la página por el mismo texto usando caracteres definidos por un archivo de fuentes especial generado por las herramientas que proveen estas técnicas.

La tipografía es uno de los puntos más importantes de un producto de diseño, por lo que la decisión de cuál fuente tipográfica usar es tema de estudios. Pero en la Web, los diseñadores por lo general están restringidos a fuentes “seguras (http://en NULL.wikipedia NULL.org/wiki/Web_typography#Web-safe_fonts)“, que se entiende que tienen todos los navegadores y sistemas operativos; y donde la fuente no existe, normalmente se reemplaza por equivalentes parecidos.

Pero si el diseñador es quisquilloso, busca otras soluciones para implementar el diseño. Si el uso de las fuentes “raras” es moderado y están bien definidos los textos, con imágenes se puede resolver el problema, incluso siendo SEO (http://en NULL.wikipedia NULL.org/wiki/SEO)-compatible. Pero para textos libres (definidos por el usuario final, no por el diseñador) y con más contenido, esta solución no sirve.

Es cierto que en CSS existe la directiva @font-face, pero su controversia reside en que el archivo de fuentes puede ser descargado libremente con sólo saber la URL donde reside, violando licencias y misceláneas.

La idea de las técnicas alternativas a empotrar tipografía en un documento Web es limitar el uso de las fuentes por los visitantes, al mismo tiempo de dar flexibilidad a su uso y pulir asperezas entre sistemas operativos y navegadores. Finalmente, creo que Cufón es claramente mejor que sIFR simplemente por ser una solución de sólo Javascript; pues además de este, sIFR necesita de Flash (http://en NULL.wikipedia NULL.org/wiki/Adobe_Flash), y por estos tiempos, es mejor andar seguro, sólo por si las moscas (http://www NULL.apple NULL.com/hotnews/thoughts-on-flash/).

También:

  • Se integra bien con otras librerías de Javascript (http://en NULL.wikipedia NULL.org/wiki/Comparison_of_JavaScript_frameworks), permitiendo control más fino sobre a qué texto se le aplica la técnica.
  • Utiliza técnicas diferentes según el navegador y las capacidades nativas que soporta. Por ejemplo, en Interner Explorer utiliza VML (http://en NULL.wikipedia NULL.org/wiki/VML), usando SVG (http://en NULL.wikipedia NULL.org/wiki/SVG) en los demás.

Al grano

Primero selecciono que fuente quiero usar para el experimento. Quería una algo rara para que se viera bien la diferencia. Me encontré con odstemplik (http://www NULL.fontspace NULL.com/gluk/odstemplik):

odstemplik preview 300x134 Cufón testing
Vista previa de la fuente "odstemplik"

Por cierto, FontSpace (http://www NULL.fontspace NULL.com/) parece un buen sitio para buscar fuentes…

Usando el generador online

Estos son los parámetros que utilicé:

  • Regular typeface: donde está el archivo de fuentes en mi PC.
  • Uppercase
  • Lowercase
  • Numerals
  • Punctuation
  • WordPress punctuation
  • Basic Latin
  • Latin-1 Supplement
  • …and also these single characters: áéíóúñÁÉÍÓÚÑ
    • Para asegurarme de que están los caracteres acentuados propios del Español (http://en NULL.wikipedia NULL.org/wiki/Spanish_language)

Todas las demás opciones las dejé con los valores por defecto. Terminé con un archivo llamado “odstemplik_italic_500.font.js”, donde además del nombre, se indica el estilo (“italic”) y el peso de la fuente (“500″). Luego toca descargar la librería Cufón (http://cufon NULL.shoqolate NULL.com/js/cufon-yui NULL.js) (¡la que hace la magia!) y finalmente, el documento donde armamos la cosa:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <script src="cufon-yui.js" type="text/javascript"></script>
        <script src="odstemplik_italic_500.font.js" type="text/javascript"></script>
        <script type="text/javascript">
            Cufon.replace('h1');
            Cufon.replace('h2');
        </script>
    </head>
    <body>
    <h1>This text will be shown in odstemplik.</h1>
    <h1>Pruebas de texto en Español:</h1>
    <h2>á é í ó ú ñ Á É Í Ó Ú Ñ</h2>
    <!-- Make Internet Explorer behave -->
    <script type="text/javascript"> Cufon.now(); </script>
    </body>
</html>

Dos detalles que se especifican en la documentación (http://wiki NULL.github NULL.com/sorccu/cufon/usage):

  • El tipo de documento (http://www NULL.w3 NULL.org/QA/2002/04/valid-dtd-list NULL.html) (primera línea) debe ser estricto (HTML o XHTML).
  • Las páginas que utilicen Cufón deben utilizar codificación (http://en NULL.wikipedia NULL.org/wiki/Character_encoding) UTF-8 (http://en NULL.wikipedia NULL.org/wiki/Utf-8).

Lo que parece preocupar:

  • cufon tildes“: Si utilizamos Cufón y no trabaja correctamente con las tildes, hay varios aspectos que hay que revisar:
    • El archivo de fuentes original debe incluir todos los caracteres deseados: Desgraciadamente, muchas fuentes libres no tienen todos los caracteres para la mayoría de los idiomas, y en muchos casos, se limitan sólo al alfabeto inglés. Antes de usar el generador, es necesario descargar la fuente y abrirla con un visor que permita especificar un texto cualquiera para la vista previa. Por ejemplo, en la vista previa anterior, además del texto por defecto, agregé las vocales acuentuadas y la «ñ».
    • Seleccionar los conjuntos de caracteres apropiados en el generador: Por ejemplo, creo que las vocales acentuadas del Español entran en el conjunto Latin-1 Supplement; también me aseguré de que estuvieran especificándolos en el último cuadro de texto (el que dice “…and also these single characters“).
    • Verificar la codificación y el tipo de documento.
  • cufon problemas buscadores“: No le veo mucho sentido a la preocupación, esta técnica es perfectamente compatible con SEO, ya que el reemplazo se ejecuta con Javascript. Los motores de búsqueda no están afectados, pues solamente obtienen el código HTML original, sin reemplazos.

Espero que aquellos con problemas con Cufón encuentren aquí algunas respuestas, aún con este sencillo ejemplo. El código completo y funcionando del ejemplo aquí.

Metablogging…

Uno de mis lectores (suena a muchos, ¿eh?) me protestó por mi post anterior. “¿Otro post más sobre Django?” me dijo cuando leyó el título. ¿Qué podría responderle?

Una de mis intenciones con este blog es (como la de muchos blogs personales) tener un espacio donde compartir ideas, pensamientos, experiencias, etc. Sobre todo, cuando estas ideas no caben en 140 caracteres (http://twitter NULL.com/).

Una vez leí (no consigo encontrar el link) que debemos escribir siempre contenido original. Y no es tan fácil. En esta era de Internet y globalización (http://en NULL.wikipedia NULL.org/wiki/Globalization), es muy fácil ser influenciado por noticias externas, y esas noticias querer compartirlas con algunos adornos y opiniones personales. Es difícil crear contenido original, sin cierto talento, al menos para escribir.

No he querido categorizar este blog, por que me parece que encasillo mis ideas. También, de las pocas veces que lo he intentado, en algunas termino con categorías ambiguas; otras termino con un exceso de estas, tanto en ancho (categorías en el primer nivel), como en profundidad (niveles de categorías) – para más metatranca, click aquí (http://en NULL.wikipedia NULL.org/wiki/Tree_%28graph_theory%29). Pues no tengo definido (ni lo tendré) exactamente el tipo de contenidos que se verán por acá.

Mucho de mi “contenido original” será sobre Python (http://www NULL.python NULL.org/)-Django (http://www NULL.djangoproject NULL.com/), pues paso mucho tiempo trabajando con estas plataformas, y encima ¡me gusta (http://pythonology NULL.org/)! ¡es divertido!

python logo master v3 TM flattened 300x101 Metablogging... (http://www NULL.python NULL.org/community/logos/python-logo-master-v3-TM-flattened NULL.png)
Python Logo

django logo negative 300x136 Metablogging... (http://media NULL.djangoproject NULL.com/img/logos/django-logo-negative NULL.png)
Django Logo

(no me pude resistir…)

Quizás es mucho pedir postear “contenido original”. Si realmente se respetara ese principio, quizás Internet no estaría tan atestado de noticias. Muchas de esas “noticias” son realmente ecos y versiones propias de otras fuentes, incluso siendo la versión de la versión de la versión de la primera fuente. Por ejemplo, noticias que por estos tiempos se relacionan con Apple (http://www NULL.apple NULL.com), como el iPhone 4G extraviado (http://gizmodo NULL.com/5520164/this-is-apples-next-iphone) (que ya son dos (http://www NULL.fayerwayer NULL.com/2010/05/parece-que-a-apple-se-le-perdio-otro-iphone-4g/)), o la polémica con Adobe Flash (http://www NULL.apple NULL.com/hotnews/thoughts-on-flash/). Quizás es una técnica SEO (http://en NULL.wikipedia NULL.org/wiki/SEO); pues, a la larga, están enlazando a páginas con alto rango (http://en NULL.wikipedia NULL.org/wiki/Pagerank), y mencionan palabras claves que están “calenticas” en la Red de Redes.

A veces pienso que me excedo con enlaces fuera de este sitio. La intención es no irme por las ramas, y si el visitante quiere profundizar (o no tiene ni p… idea de lo que hablo) puede ir dando click en lo va leyendo… Quizás me excedo etiquetando… ¿pero cuál es el límite para esto? ¿Cuáles son las reglas de la etiquetadera?

Para terminar, el prefijo “meta (http://en NULL.wikipedia NULL.org/wiki/Meta)” es usado para indicar un concepto que es una abstracción de otro concepto, completando o ampliando este último. (¿Ya visitaron Wikipedia (http://en NULL.wikipedia NULL.org/wiki/Main_Page)? ¿Notaron el nuevo look Win-7-like? icon biggrin Metablogging... )

Django Darkness

Porque la Luz ayuda al programador. Y la Oscuridad deja al programador en las tinieblas (redundante, ¿eh?).

Acabo de encontrar un bug (http://en NULL.wikipedia NULL.org/wiki/Software_bug) (no sé si realmente se le pueda llamar así…) en Django (http://www NULL.djangoproject NULL.com/), relacionado con la generación de feeds (http://en NULL.wikipedia NULL.org/wiki/Web_feed).

Generar feeds en Django es tan sencillo como definir nuestros modelos (http://docs NULL.djangoproject NULL.com/en/dev/topics/db/models/#topics-db-models), simplemente definimos una nueva clase, heredando de Feed (http://docs NULL.djangoproject NULL.com/en/dev/ref/contrib/syndication/#django NULL.contrib NULL.syndication NULL.django NULL.contrib NULL.syndication NULL.views NULL.Feed) y sólo se definen los atributos que importan.

Una técnica interesante con los feeds de Django, es que tenemos tres formas posibles de definir el mismo atributo. Por ejemplo, el título del feed:

from django.contrib.syndication.views import Feed

class ExampleFeed(Feed):

    title = 'foo' # Título "cableado".

    def title(self):
        """
        Devolver el título del feed como una
        cadena normal de Python
        """

    def title(self, obj):
        """
        "obj" es el objeto devuelto por get_object()
        Este puede ser usado para generar el título del
        feed en dependencia de un parámetro.
        """

    # ...

Y se entiende perfectamente. Pero quizás, llegamos a meter el delicado piececito, y no hacerlo exactamente así, pues eso sí no queda claro.

La crónica

En mi feed, muesto los elementos correspondientes a la página principal. Por lo tanto, es entendible que pretenda hacer esto:

from django.core.urlresolvers import reverse

class PortadaFeed(Feed):
    title = u'Latest News'
    description =  u'Latest News from my site'
    link = reverse('home')
    # ...

¿No ven el problema? Es normal, a mí me tomó dos días verlo. Estoy llamando a una función durante la inicialización de un atributo de clase. En Python, esto es perfectamente legal. Pero los problemas pueden aparecer en tiempo de ejecución.

Por ejemplo: reverse(). Esta funcioncilla es la encargada de averiguar las URLs asignadas a cualquiera de nuestras vistas en Django. Para averiguarlo, utiliza nuestro módulo URLConf (http://docs NULL.djangoproject NULL.com/en/dev/topics/http/urls/#topics-http-urls) para averiguar esas URLs y que todo quede bien DRY (http://c2 NULL.com/cgi/wiki?DontRepeatYourself).

Siguendo el ejemplo (http://docs NULL.djangoproject NULL.com/en/dev/ref/contrib/syndication/#module-django NULL.contrib NULL.syndication) en la documentación de Django, el feed se integra en la URLConf de esta manera:

from django.conf.urls.defaults import *
from myproject.feeds import PortadaFeed

urlpatterns = patterns('',
    # ...
    (r'^latest/feed/$', PortadaFeed()),
    # ...
)

Acá hay una sutileza, muy importante. Al tiempo que se declara el patrón de URL, se está creando una instancia de la clase PortadaFeed.

Y este es el problema, que me costó tanto trabajo detectar. Al instanciar la clase, el atributo de clase link se inicializa con el valor que retorne reverse('home'). Y se está invocando justo cuando se están inicializando las URLs, cayendo en un círculo vicioso. Y lo más pesado del círculo es que no hay indicación ninguna de donde puede estar el problema.

Estaba usando dos bloques de patrones; es decir, una parte de las URLs con las vistas arriba, luego agregaba la de los feeds en el bloque siguiente. Cuando declaraba el segundo bloque, este simplemente no llegaba a registrar los nombres de las URLs, pero sí se podía acceder al feed. El único error que se veía era NoReverseMatch, cuando intentaba averiguar la URL del feed.

No me molesta tener que usar un método en vez de un atributo de clase. Mi punto es que es muy fácil caer en este error, y pasarse dos días rompiéndose la cabeza porque las excepciones que dispara Django simplemente no ayudan. El punto es documentar el hecho, pues (no sé los demás) yo me leo varias veces la documentación para entender bien lo que me dicen y cómo hacer lo que quiero. Puse un ticket (http://code NULL.djangoproject NULL.com/ticket/13501) para que lo documentaran, que luego resultó estar duplicado. En ese otro ticket (http://code NULL.djangoproject NULL.com/ticket/11712), el problema es algo serio, pues existe un conflicto con DRY (http://code NULL.djangoproject NULL.com/ticket/11712#comment:2), pues intenta usar una URL definida unas líneas antes.

Update: Ya publiqué el código completo en  DjangoSnippets (http://www NULL.djangosnippets NULL.org/snippets/2022/).

DRYing Django Feeds

Este puede que sea uno de mis posts más originales; quiero decir, uno donde prácticamente todo el argumento es creación mía. Un fenómeno que está ocurriendo en Internet es que muchos sitios se hacen eco de noticias conocidas, realmente aportando poco o nada de contenido nuevo sobre la noticia. O compilan varias noticias relacionadas y con eso hacen una historia, pero aparte de la redacción, no hay nada nuevo (a mí también me pasó).

Pero no es el tema de este post. Ahora trataré de introducir un concepto importante (IMHO (http://en NULL.wikipedia NULL.org/wiki/IMHO#IM)) para la programación y cómo trato de resolver un problema (de programación, claro) utilizando este concepto.

Django

Django (http://www NULL.djangoproject NULL.com/) es un framework para programación web (http://en NULL.wikipedia NULL.org/wiki/Web_framework), uno de los mejores hoy en día. Los frameworks (http://en NULL.wikipedia NULL.org/wiki/Software_framework) son una parte importante del desarrollo de muchos sistemas informáticos, pues ayudan en tareas básicas o repetitivas, y crean un ambiente uniforme para el desarrollo abstrayéndose de las peculiaridades de otros sistemas; permitiendo al programador crear y concentrarse en la lógica de la aplicación (a.k.a. “the fun part”).

Este framework está basado en el lenguaje de programación Python (http://en NULL.wikipedia NULL.org/wiki/Python_(programming_language)), y promueve el rápido desarrollo de aplicaciones y diseño limpio y pragmático (o eso entendí). También se enfoca en la mayor automatización posible y en el principio DRY (http://c2 NULL.com/cgi/wiki?DontRepeatYourself).

DRY

No tiene nada que ver con ninguna secadora de ropa. Es un principio a tener en cuenta a la hora de diseñar un sistema (muchos de esos principios tienen nombrecitos graciosos (http://en NULL.wikipedia NULL.org/wiki/List_of_software_development_philosophies)).

En inglés (http://c2 NULL.com/cgi/wiki?DontRepeatYourself):

Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.

Básicamente, todo código o información dentro de un sistema debe estar definido una y sólo una vez en todo el sistema. La duplicación es mala y conlleva a pesadillas de mantenimiento, dando lugar a posibles inconsistencias. Cuando un sistema es completamente DRY, la automatizaión es tarea fácil, pues sólo es necesario definir una vez las transformaciones y el código sólo se reusa.

Por ejemplo, en un sistema que almacena información de usuarios, el nombre de cada uno de estos, está únicamente en una fila de una de las tablas de la base de datos. Tener la en dos o más lugares implicaría posibles inconsistencias: cuando se modifica el de una tabla, es necesario también reflejar el nuevo valor en la otra, pero y si falla algo en el medio?

Uno de los ejemplos claves de DRY en Django, es cómo obtener las URLs de una funcionalidad específica. Por ejemplo, el patrón de la URL para editar la información de un usuario, podría ser así (omitiendo el dominio):

r'^profiles/(?P<user_id>\d+)/edit/$'

Lo que está entre paréntesis es el parámetro que indica el identificador del usuario. Una URL de estas así:

'/profiles/19/edit/'

La cual llevaría a la página de edición correspondiente al usuario con el identificador «19». El problema es, ¿cómo incluir esta dirección en la página generada dinámicamente? El programador ingenuo (como todos al principio, incluyéndome) piensa en esto:

<a href="/profiles/{{ user.id }}/edit/">Editar perfil</a>

Y los problemas comienzan cuando la URL debe cambiar (por ejemplo, en vez de “profiles”, debe ser “users”). Es necesario cambiar la URL en la comfiguració Y en la plantilla. Por lo tanto, el patrón de la URL está duplicado.

En este caso, Django proporciona una función para obtener las URLs de cualquier página del sistema mirando sólo la configuración. Es posible incluso asignarle nombres fáciles de recordar y luego obtenerlas por estos nombres. Por ejemplo, en la plantilla HTML quedaría así:

<a href="{% url user_profile_edit user.id %}"<Editar perfil</a>

A partir de entonces, la URL podrá cambiar todo lo que quiera, pero mientras se use la etiqueta {% url %}, siempre se mostrará la dirección correcta.

El problema

El framework de Django para la generación de feeds (http://en NULL.wikipedia NULL.org/wiki/Web_feed) es sencillo, fácilmente extendible y completo; con entender unos conceptos y reusar un poco de código de otras partes de la aplicación, en un dos por tres estoy generando feeds para cualquier parte del sistema. Incluso facilita enormemente la generación de feeds que dependen de varios parámetros, como categorías o etiquetas.

Que le estaba agregando feeds RSS (http://en NULL.wikipedia NULL.org/wiki/RSS) a un proyecto que tengo en Django, cuando me doy cuenta de que la etiqueta HTML que referencia los los feeds, tiene más que sólo la dirección:

<link rel="alternate" type="application/rss+xml" title="Latest News" href="/feeds/latest/" />

Para el atributo “href” se puede utlizar la misma etiqueta {% url %}, pero no basta. De dónde obtengo el valor para el atributo “type”. Y más importante, el atributo “title”, que describe el nombre del feed dentro de la página actual.

Por ejemplo, sigamos con el título. Si es un feed único para todo el sitio, no hay mucho problema, poner un título genérico no dará muchos problemas. Como mostré antes, Firefox muestra el símbolo de feeds en la barra de direcciones si hay uno o más feeds disponibles en la página actual. Si hay más de uno, al hacer clic en el ícono muestra un menú con los feeds disponibles, usando el atributo “title” de cada uno como texto de las opciones del menú.

Por ejemplo, estando en una página con el contenido filtrado por una categoría, y quiero generar un feed que muestre las últimas noticias dentro de esta categoría, es normal que el título del feed incluya el nombre de la categoría; algo como “Latest News in Computers”, donde «Computers» es el nombre de la categoría. Firefox mostraría dos entradas en el menú de feeds “Latest News” (últimas de todo el sitio) y ”Latest News in Computers” (últimas noticias bajo la categoría «Computers»).

Claro, puedo generar el valor de “title” en la misma plantilla, pero es ahí donde estoy violando el principio DRY. El título del feed se generaría en dos lados, en el código de definición del feed y en la plantilla HTML.

Lo mismo pasa con el atributo “type”. Si se define si se usa Atom (http://en NULL.wikipedia NULL.org/wiki/Atom_(standard)) o RSS, se define en el código, pero también es necesario que se refleje el tipo correcto en la etiqueta “link”.

Mi solución

Aún cuando no estoy completamente feliz con ella, es lo mejor que he podido concebir. Este problema también lo planteé (http://stackoverflow NULL.com/questions/2784659/django-dry-feeds) en Stack Overflow (http://stackoverflow NULL.com/), pero sin respuestas convincentes. Y lo que más me convenció fue crear una etiqueta nueva. También consideré usar un procesador de contexto, pero ese camino tiene muchos inconvenientes.

El sistema de plantillas de Django permite que el programador cree nuevas plantillas y se las incluya al sistema, haciéndolas disponibles a toda la aplicación. Y como básicamente pretendo obtener la URL de un feed más algunos atributos, utilicé la etiqueta antes mencionada {% url %} y adaptarla a mis necesidades.

Aparte del nombre (que tampoco estoy contento con él, pero no se me ocurre otro), la etiqueta {% feed_info %} creada por mi funciona prácticamente igual que su hermana {% url %}. Lo que cambia es el valor de retorno que en este caso genera la etiqueta HTML “link” completa.

El código diferente es (agregado más o menos aquí (http://code NULL.djangoproject NULL.com/browser/django/trunk/django/template/defaulttags NULL.py#L382)):

if 'request' in context:
    request = context['request']
else:
    request = None

feed_instance, feed_args, feed_kwargs = resolve(url)
if not isinstance(feed_instance, Feed):
    raise NoReverseMatch, \
          'feed_info can only reverse class-based feeds'

feed_obj = feed_instance.get_object(request, *feed_args, **feed_kwargs)

feed_data = {
    'url': url,
    'obj': feed_instance,
    'args': feed_args,
    'kwargs': feed_kwargs,
    #'title': html_escape(feed_instance.__get_dynamic_attr('title', obj)),
    'title': html_escape(
        feed_instance._Feed__get_dynamic_attr('title', feed_obj)
        ),
    'type': feed_instance.feed_type.mime_type,
}

if self.asvar:
    context[self.asvar] = feed_data
    return ''
else:
    return mark_safe(
        '&lt;link rel="alternate" type="%(type)s" title="%(title)s" href="%(url)s" /&gt;' \
        % feed_data
    )

Empezamos. Los métodos de Feed que obtienen parámetros también reciben como primer parámetro un objeto request, para dar mayor flexibilidad. Pero aquí aparece un inconveniente, para que sea realmente genérico el contexto de la plantilla debe contener una variable con el objeto request. Esta variable solo está presente si el procesador de contexto (http://docs NULL.djangoproject NULL.com/en/dev/ref/templates/api/#django-core-context-processors-request) está presente en la configuración (http://docs NULL.djangoproject NULL.com/en/dev/ref/settings/#template-context-processors). En caso contrario, si nuestro feed personalizado utiliza datos del request para generarse, este puede no funcionar bien.

Seguimos. El código de la etiqueta {% url %} ya hizo todo el trabajo de averiguar la URL del feed, con parámetros y todo, como debe ser. Hasta ahora, tengo sólo la dirección del feed (el atributo “href” de “link”). Para obtener el valor de los otros dos atributos, necesito tener la instancia de Feed a la cual preguntarle por sus propiedades.

Ahora tengo una ventaja. En Django 1.2 (http://code NULL.djangoproject NULL.com/wiki/Version1 NULL.2Features), los feeds son instancias de subclases de Feed, pero que permiten ser llamadas como funciones, por lo que son usadas también como vistas. En versiones anteriores, me imagino que hubiera sido un poco más difícil.

La ventaja la aprovecho usando resolve(). Esta función, dado una URL concreta, devuelve la vista, y los argumentos que se le pasan a dicha vista. Y la vista retornada, es la instancia de la clase personalizada de Feed. También compruebo si realmente es un Feed de nuevo tipo.

Luego creo un diccionario con toda la información necesaria del feed (URL, título, tipo, la instancia misma y los argumentos que se le pasaron). Por si las moscas…

Finalizando. Si la etiqueta se invocó utilizando as «variable», el diccionario antes creado se incluye en el contexto de la plantilla con el nombre especificado y no se devuelve nada. Por el contrario, si no se especifica en qué variable guardarse esta información, se genera la etiqueta “link” con los atributos “title”, “type” y “href”.

Pero no todo me salió tan lindo. En el código de creación del diccionario con la información del feed, hay una línea comentada. Así es como se supone que funcione, teniendo en cuenta la definición de la clase Feed (http://code NULL.djangoproject NULL.com/browser/django/trunk/django/contrib/syndication/views NULL.py#L24). Ese método “mágico”, __get_dynamic_attr() (http://code NULL.djangoproject NULL.com/browser/django/trunk/django/contrib/syndication/views NULL.py#L52), no aparece en la instancia del feed. En cambio, aparece con el nombre _Feed__get_dynamic_attr. No entiendo eso, pero funciona. Lo que preocupa es que esta forma de llamarse el método cambie… a veces estas magias de Python me confunden…