Simple Forum plugin for Rails

La semana pasada tenía la necesidad de agregar un foro super simple para un cliente, basicamente querían que los usuarios del sitio puedan dejar comentarios de la campaña, hablen entre ellos y no mucho más. Ya tenía uno hecho todo mezclado en otra aplicación por lo que decidí que era hora de reescribir menos :).

Lo primero que se me pasó por la mente «algo ya debe haber hecho» y si bien es cierto, lo poco que encontré se apoya en Engines lo cual para mi no es aceptable. Simplemente no me gusta Engines :). La mejor opción tal vez es Beast pero integrarlo con otra aplicación no es simple y tener el foro de forma externa no era una opción.

Por lo que decidí empezar un «site project» a partir del código que ya tenía, que al haberlo escrito hace mucho era horrible por lo que el resultado fue casi casi que codeado desde 0.

La idea es que en 3 pasos uno pueda tener el foro andando integrado en el sitio, y creo que mas o menos se logra con algunos «problemas». El principal tema es que al estar los controllers en el plugin, y estos no se recargan en development, es medio molesto usarlo porque depende de que se haga hay que reiniciar el server a cada rato (tira unos stack level too deph»).

Acá está el código para el que quiera probarlo, testearlo y recomendar alguna alternativa. Sobre todo si encuentran errores de gramática/sintaxis en la poca documentación que hice se agradecería :).

Multiupload de imágenes con Prototype

Desde hace unos días que estoy haciendo un widget que soporte upload de múltiples archivos para una aplicación web. No fue fácil el comienzo pero despues de varias horas (unas 8 hasta este momento) ya va tomando forma.

Para poder trackear el upload de cada archivo utilizo apache_mod_upload_progress, un genio «Drogomir» :). Para compilarlo en OSX tuve algunos problemas ya que apache2 esta compilado en x86_64 y el default del apsx es x86 pero googleando se encuentra fácil como pasarle el parámetro al gcc. Lo otro que necesitamos tener instalado es mod_rails y apache 2.2. Cuando termine el código y lo publique estará todo explicado en detalle :).

El segundo problema grande fue el formulario. Para hacer el upload lo que hago es crear un iframe oculto y cambiar el target del formulario a ese frame (de esta menera si no tenemos javascript la aplicación degrada automáticamente al upload de imagenes individuales y el usuario no se entera), pero claro, necesitaba tener múltiples input:file, uno por cada archivo a subir. De ponerlos todos juntos tendríamos un POST super gigante que no era lo que se buscaba ya que no podría trackear cada upload por separado.

La solución fue, cada vez que se selecciona un archivo sacar el INPUT del form y guardarlo en un array. El espacio vacío se reemplaza con un nuevo INPUT y como todo es tán rápido, uno no se da cuenta. El problema llegó cuando terminaba el primer archivo, tenía que volver a agregar el siguiente file al formulario y hacer otra vez el submit. Pero si uno llama $(form).submit() desde javascript, el callback onSubmit no es ejecutado (defecto de las implementaciones de todos los navegadores que probé y parece que no va a cambiar) por lo que no era útil.

La solución finalmente fue simular el click enel botón enviar con un simple $(submit_button).click() que resuelve el problema anterior. Les dejo el video para que lo disfruten :).

httpv://www.youtube.com/watch?v=7PqZg_1Pi1w

has_sitemap plugin

Este fin de semana tenía que agregar a 4 sitios de clientes un sitemap para mejorar la indexación por los motores de búsqueda, así que para hacerlo una vez y rápido, decidí aprender a hacer plugins para Rails.

No es que sea una magia absoluta, de hecho es muy tonto hacer uno. Pero cómo codeas adentro del plugin creo que es importante, porque la gente los usa como cajas negras, y si es un desastre, todo lo de afuera se vuelve un desastre.

El plugin permite agregar sitemaps tal cual lo define el protocolo, mediante un simple helper y una función adicional donde el controller debe generar la data que desea agregar. Lo que se encapsula es básicamente el generador del XML.

Todo se genera dinámicamente y por ahora solo es útil para sitios realmente chicos (< 100 urls) como los que yo necesitaba. El protocolo especifica que se pueden poner hasta 50k de URLs (o 10Mb) por lo que este plugin no es útil, al menos por ahora, para esos casos.

El código está hosteado en http://github.com/Gazer/has_sitemap/tree/master.

Oregano cambia a Git

Hoy terminé de decidir que mover el main repo de Oregano a Git era una buena idea, principalmente porque ya me estaba cansando de hacer branches duplicando directorios :P.

A partir de ahora el source principal estará en GitHub, para todo aquel que quiera hacer un fork o simplemente pullear su propio tree local.

Para migrar el repo desde darcs a git usé la opción que explican acá, un script en ruby que funcionó de los más bien. Trate de usar uno en python que también nombra, pero tenía que instalar algunos módulos y con ruby nada, so, ganó el que menos laburo me dió :).

Lo primero que van a venir a llorar es, de cajón, «¿por qué en GitHub que es privativo? ¿por qué no Gitorious?» o cosas así. La verdad no tengo ninguna razón técnica, ni problemas con ninguno de los servicios. Simplemente en GitHub ya tenía cuenta, ya tenía mi llave pública configurada y ya conocía donde hacer click, so, fue la elección más cómoda simplemente :).

En fin, voy a ir actualizando la documentación, wiki y demás a medida que me acuerde :D.

¡Falta Uno!

Hace un par de años, enojado por lo difícil que era organizar un partido de fútbol con mis amigos, me puse a desarrollar una paginita web pedorra en PHP para organizar partidos. Obviamente, como muchas de las cosas que empiezo, quedó en la nada. Si bien funcionaba, no me calente en ponerlo online ni comentarlo con mis amigos.

Hace dos semanas la idea reflotó, principalmente porque quería sumar experiencia en Ruby On Rails, por lo que decidí subir la apuesta. De eso se trata ¡Falta Uno!.

La idea es simple : te registrar, te relacionas con tus amigos, creas un partido y despues se van anotando. En la página se puede ver quiénes se anotaron y un pequeño board para postear mensajes por si acaso (vale putear, organizar quién juega con quién, etc), así como los datos de la cancha, hora y demás.

Ahora bien, no hace falta que todos los que van a jugar tengan cuenta. ¿Por qué? Simplemente porque si no la cosa no funcionaría :).

Con mi grupo de amigos suele pasar lo siguiente : 3 horas antes de que empiece el partido llega un mail diciendo «me bajo porque me duele la uña», y le sigue un mail con «uhhh, che, falta uno, quien consigue ??».

Este sujeto que aparece a último momento, es muy probable que sea el primo del tio de un amigo de un amigo, y casi seguro que no tendría cuenta y mucho menos relación de amistad con el creador de un partido (para poder anotarte a un partido tenés que ser amigo del creador por ahora).

Entonces?, bien, el sistema permite anotar a un «guest player«, poniendo solo el nombre y ocupando la plaza y así se mantiene simple la creación del partido.

Otro feature que se agrega, que es totalmente opcional y lo hice porque me gusta tenerlo en cuenta, es poder votar como jugaron tus amigos en ese partido. Hay 5 categorías para puntuar entre 1 y 5, y luego entre todos los votos se calcula el promedio por partido, y también un promedio general por usuario.

Este último feature es totalmente opcional y puede ser omitido si no tenés ni tiempo ni ganas de usarlo :).

En fin, el site no creo que tenga mucho éxito, si lo tiene, groso :), pero por lo menos mis amigos y yo vamos a dejar de putear para organizar un picadito.

Algunos datos «técnicos» sobre lo usado :

  • Ruby On Rails 2.0.2
  • Plugins
    • attachment_fu : Para upload de avatars
    • has_many_friends : Relaciones de amistad entre usuarios
    • restful_authentication : No puede faltasi hay un login 🙂
    • will_paginate : Tengo que migrar a usar la versión en gema.
    • calendar_helper : Para armar el calendario de partidos
    • simple_format : Para formatear el texto plano de los comentarios.
  • Server : mongrel/ apache (mod_balancer+ mod_rewrite) / mysql

Ponele un nombre a tu punta de pruebas!

Y si, algún día se iba a dar, y que mejor día que «San Valentín» para demostrarle todo tu amor a tu proyecto favorito 😀 ?. A partir de hoy se le pueden poner nombres a las puntas de pruebas. Nada de adivinar quién es v(1), quién v(2); solo poné «In«, «Out«, y listo!!!

La verdad que no fue trivial ya que en donde necesitaba los nombres ya se había perdido el contexto del Schematic y solo tenía la información que da el Engine (y no soporta cosas lindas como v(1) AS MiNombre, tipo SQL :-P), pero haciendo un par de pases mágicos de punteros entre estructuras, quedó andando. Lo único feo feo feo, es que hay que acordarse que «el último apaga la luz«, que en este caso es liberar la lista de nombre, pero ya veré como mejorarlo más adelante.

Seguro explota en muchos casos, puede fallar :), pero la idea está y abre la puerta a un refactoring (otra vez!!!) del Engine System para ser «Context Aware» del Schematic que se esté simulando.

Espero hacer en estos días cuando complete el soporte para NgSpice, ya que por ahora solo anda con GNUCap.

El screenshot :

Puntas de Pruebas con nombre en oregano

Programando Rails con GEdit

Luego de buscar y buscar opciones para hacer el día a día programando en Ruby on Rails decidí quedarme con GEdit, por sobre mis otras opciones : Aptana Studio, vim+rails.vim, jedit.

Tanto jEdit como Aptana están escritos en Java, por lo que tienen todo lo malo de la JVM corriendo atrás y la verdad que molestaban más de lo que ayudaban. Aptana es un lindo plugin para Eclipse, pero no pienso actualizar la PC solo para poder sacarle el juego :).

Por el lado de vim, por primera vez que yo recuerde, queda chico. Esta muy bien para tocar 3 o 4 boludeces de vez en cuando. Pero para desarrollar a diario se me hace un martirio. El auto-complete de helpers, por decir algo, es super incomodo y yo lo quiero con un simple Tab :). No encontré si tiene la opción de Code Snippets.

Tengo que admitir que mi primer reacción fue : Quiero TextMate en Linux!. Quienes hayan usado TextMate lo entenderán, quienes nunca lo usaron, es como aquellos que no entiende como con un solo botón alcanza para hacer todo :). No vale la pena explicarlo.

No tuve que buscar demasiado la verdad y encontré varias fuentes para configurar GEdit de manera que quede suficientemente cómodo (como estaba acostumbrado con TextMate) para trabajar todos los días. El resultado :

GEdit en modo TextMate

GEdit es una de esas cosas que siempre fue «el editor pedorro ese que viene con GNOME». Me sorprendió que se porte tan bien con varios plugins andando. Esperemos que a la larga no termine siendo un fracaso :).

/ URLs / Lindas / Con / Rails

Unos de los proyectos actuales en los que trabajo es un portal hecho en Ruby on Rails y tenía que mostrar el nombre de la seccion y subseccion en las URLs. La primer versión fue usar las rutas anidades que se incluyen en Rails 2.0 como sigue :

map.resources :sections do |section|
  section.resources :subsections do |subsec|
    subsec.resources :articles
  end
end

Sin embargo para Rails eso no son URls tan lindas como me (nos?) gustaría. Un ejemplo que daría la llamada a section_subsection_article_url sería : /sections/1/subsections/2/article/4, nada cómodo para alguien que quiere entrar a /sections/deportes/notas/yyyyy.

La solución llegó, en parte, al encontrar el plugin resource_hacks, que nos permite agregar un member_path a mano el cual Rails utilizaría para armar las rutas. Con el plugin instalado nuestro ejemplo anterior quedaría como :

map.resources :sections, :member_path => 'sections/:section' do |section|
  section.resources :subsections, :member_path => 'sections/:section/:subsection' do |subsec|
    subsec.resources :articles, :member_path =>; 'sections/:section/:subsection/:permalink'
  end
end

Y ahora si, si entramos a /sections/deportes/ veremos la portada del sitio de Deportes.

Este requerimiento era «importante» ya que el cliente quere manejar en forma dinámica el sitio. No todas las secciones tienen las mismas subsecciones y demás yerbas.

Una vez que tuve todo eso andando, mi problema fue que los helpers _url y _path dejaron de andar alegando que los parametros de entrada no concordaban con lo esperado.

Lo primero que encontré luego de un par de dias de buscar fue que había que redefinir el mítodo to_param de los modelos para que retornen el string que quería mostrar en la URL, ya que por default retornan el id del objeto. Sin embargo esto no alcanzó. La solución llegó cuando se me ocurrió pasar un string a mano con el parámetro :id a los helpers y todo mágicamente empezó a funcionar.

En lugar de :


Llamar a :

 "1") %>

Y todo va de pelos. No se si es un comportamiento esperado, o si los helpers con este hack no serían válidos, pero esto anda al menos hasta ahora. Si a alguien se le ocurre por qué puede pasar esto o alguna mejor solución, será bienvenido 🙂

En los controladores ahora debemos hacer las búsquedas por los strings que nos llegan mas el permalink del artículo, en lugar de utilizar el find por ID, por lo que es altamente recomendable agregar índices en las tablas :). Hay otras tantas optimizaciones que hacer, pero en general, la cosa funciona.

Jugando con OgreODE

Ya hace un rato empecé a desarrollar un jueguito pedorro de autos con la intensión de aprender a usar Ogre. Al principio pensaba no usar un motor de física hasta que me di cuenta que no me daba :P, por lo que empecé a evaluar opciones.

Sin lugar a dudas la mejor opción que encontré Ageia pero no es libre y no hay SDK para Linux (que es lo que buscaba) por lo que recaí en ODE, mediante OgreODE, que todavía no me convence.

Para empezar tiene problemas con Heighmaps : si usamos un mapa con mucha resolución hace agua en la simulación y si usamos uno de baja resolución anda bien, pero el mapa se ve como un Family Game :). Es por eso que decidí hacer el escenario con un Mesh y modelarlo a mano todo. No es perfecto pero la simulación es bastante mejor en los cambios de altura.

El otro problema, tal vez más grave, es la falta de documentación (tanto de ODE como de OgreODE). La poca que hay es vaga y hay que andar adivinando muchas cosas, sobre todo las unidades que esperan los parámetros. Experimentando un poco sale y veré si más adelante colaboro en el Wiki para completar la documentación.

Por el lado de OgreODE tal vez lo más problemático es que se está buscando maintainer y el actual solo hace bug fixes graves pero no agrega nueva features. Por el lado bueno es que tiene mucho Prefabs: clases que encapsulan objetos comunes automatizando la creación de objetos complejos desde código o desde un XML externo. El que estoy usando es el Prefab de Vehicle que el peor problema que tiene ahora es que las curvas de torque del motor no estan bien implementadas y no anda como uno esperaría (básicamente tengo 1 solo cambio en el motor), tendré que extender las clases por mi cuenta o aportar algun parche :).

Los gráficos del demo que se ve al final del post están hechos por mi (se nota ;-)) con Blender. Nada del otro mundo solo para que se aprecie lo importante del demo que es la física. Todavía queda mucho por investigar, solo espero encontrar el tiempo.

httpv://www.youtube.com/watch?v=wTP9I7R4WjA

Finding Boost – Primera parte

Hace varios días que Matías anda pregonando lo bueno que es C++ y lo bueno que es Boost, y trata de evangelizarnos a todos en el LUGFI, donde, creo que no estoy errado, todos odiamos C++ por una u otra razón :).

Los flames en el IRC se hicieron largos, y antes de seguir opinando lo mismo que sostenía me decidí a probarlo un poquito a ver que onda. Quiero dejar en claro que mantengo mi postura. Si bien encontre en boost algunas herramientas muy útiles, no considero que C++ es «programar como se programa en boost», y C++ sigue siendo horrible :). Más fácil/lindo quedará todo con boost, pero no cambia la raíz de mi enemistad con el lenguaje base.

Para probar todo esto decidí reflotar un proyecto que hice hace muchos muchos años llamado GCP (Gazer Compiler Plus, muy original, no?), que no creo haber publicado nunca y que no verá la luz. El proyectito tenía como objetivo implementar un un algoritmo para resolución de ecuaciones utilizando notación polaca. Todo lo que está arriba es para que tenga sentido. Hay un lenguaje definido (básicamente un gwbasic recortado) que compila a un bytecode, y éste último se ejecuta en una máquina virtual de un solo registro.

Lo primero que rescato al pasar de C a C++, es obviamente el uso de la STL. Manejar una lista, una pila y los strings son mucho menos dolorosos. El primer problema que ya me disgustó fue al momento de abrir un archivo y leerlo línea a línea, debiendo utilizar la siguiente sentencia :

getline(file, line);

¿Qué tiene de malo?, que es un hack horrible, porque el método file.getline no soporta string!. Digo yo, costaba mucho tener el prototipo string file.getline() ?

Luego llegú el turno de usar boost. Lo primero que me encontré fue que necesitaba algo para castear de string a int, que no sea atoi 🙂 y encontré boost::lexical_cast, un template muy cómodo y que tira una hermosa exception cuando algo falla para poder manejar el error con un try/catch. Un mini ejemplo :

    int tmp = boost::lexical_cast("125");

El siguiente paso fue reemplazar mi string tokenizer por algo más copado, así que decidí usar boost::tokenizer, y fue mi primer pared. Lo que yo estaba usando era lo siguiente, sacado de la documentación oficial :

    tokenizer tok(line) ;

Claro que todo lo que reluce no es oro, por lo que eso no anduvo. La razón es que al parecer alguien dentro de boost dijo : «separemos por espacios por default, y borremos todo lo que no sea alfanumérico». Lo que yo tenía que separar era algo como «let y = y + 1», por lo que mi signo igual desaparecía mágicamente, lo que tarde como media hora en darme cuenta y recurrí a matias en busca de ayuda. El truco estaba en definir tu propio tokenizer que haga lo que vos quieras, de la siguiente manera :

   typedef tokenizer< char_separator > MyTokenizer;
   MyTokenizer tok(line, char_separator(" "));
   MyTokenizer::iterator token;

Yo seguiré insistiendo que es un comportamiento no esperado para un tokenizer default :), más aún si en la documentación no lo aclaran. Otro problema que encontré, que no me gustó nada, es que me desayuné que los amigos de C++/boost hacen assert y te hacen explotar el programa de onda ;). La «excusa» de matias es que «esa condición es un bug y deberías arreglarlo», en lo que estoy en parte de acuerdo. Pero explotar mi programa no va a hacer que lo arregle más rápido :). La política de manejo de errores «irrecuperables» (en este caso era incrementar un iterador pasado su fin) no es de mi agrado, prefiero sacrificar esa «performance» extra de C++ por la libertad de mi C# de hacer las cosas como a mi me gustan :).

Para la salida con formato estuve usando boost::format, y la verdad que printf no tiene nada que envidiarle. La innecesaria utilización del operador «%» como separador de los parámetros en lugar de una simple coma, es algo que no entiendo. Me hace pensar lo mismo que cuando lei sobre Nullable Types en C#, que utilizan una sintaxis confusa como «int? a;».

Y hasta ahí llego mi aventura por el momento. Para completar el post de mis algoritmos me estarán faltando hacer dos cosas :

  • Reemplazar las funciones con ifs/switchs por unos std::map para mapear los códigos de instrucción
  • Cambiar el array de la tabla de memoria interna por boost::bimap para tener un mapa en dos direcciones, útil poder buscar direcciones de memoria de un símbolo o el símbolo en una dirección de memoria de una manera bonita (y según matias, optimizada ;))
  • Ver boost::spirit, que se supone que puedo definir un parser loco para mi lenguaje y no tener que hacerlo a mano como lo quise hacer yo (que como dije antes, la gracia del proyecto era hacerlo a mano). Lo voy a mirar solamente de curioso que soy.

En lo que llevo de esta experiencia no va mejorando mucho la forma de pensar. Seguramente con bimap levante un par de puntos, pero no creo que mucho más :). El último dato de la noche :

   -rwxr-xr-x 1 gazer gazer 121K jul 27 02:13 ../cpp/gcp
   -rwxr-xr-x 1 gazer gazer  18K jul 27 02:12 gcp

El primero es en C++ (no esta completo el programa, hay cosas que no hace todavía), el segundo en C. Ambos con gcc 4.1.2, -O2, y pasandole un strip por las dudas 🙂 …. Siento que tengo 103kb desperdiciados :D.