09 diciembre 2008

Una de ActionScript 3: cómo configurar un CellRender correctamente

Bug tonto del dia: Flash (AS3) no es Java...

Este código que intenta cambiar el cellRenderer de un componente List de ActionScript 3 parece funcionar (de hecho compila), pero NO funciona correctamente (sólo renderiza el último componente de la List, los otros no):

playlist_mc.setStyle("cellRenderer", new MyCellRenderer());

Este es el código que sí funciona:

playlist_mc.setStyle("cellRenderer", MyCellRenderer);

¿Que cual era el problema? Pues hay que pasar una CLASE (estilo AS3) y no un OBJETO (estilo Java) al setStyle... Cuanto daño causas Swing XD.

Dejo una buena referencia sobre los cellRenderers de regalo.

30 octubre 2008

GPicView 0.1.10 en Ubuntu Gutsy

GPicView es un rápido visor de imágenes, bastante más rápido que el que incluye Ubuntu por defecto (eog). El problema es que en Gutsy no viene incluido en los repositorios -o yo no he sabido encontrarlo- y hay que instalarlo a mano.

Nos bajamos pues la última versión des de la web de gpicview, en mi caso la 0.1.10.

Primero hay instalar las dependencias para poder compilarlo:
$ sudo apt-get install libgtk2.0-dev

Y luego seguimos el proceso estándar de configure, make y make install (si faltaran más dependencias, configure nos lo indicaría).
$ tar xvzf gpicview-0.1.10.tar.gz
$ cd gpicview-0.1.10/
$ ./configure
$ make
$ sudo make install

Podemos ejecutarlo des de la consola con:
$ gpicview

También encontraremos un nuevo lanzador de la aplicación en el menú "Aplicaciones" > "Gráficos".

Si queremos configurar gpicview como nuestro visor de imágenes por defecto (con los tipos de imagen que tiene actualmente eog):
$ xdg-mime default gpicview.desktop `grep 'MimeType=' /usr/share/applications/eog.desktop | sed -e 's/.*=//' -e 's/;/ /g'`

¡Y listos!

25 octubre 2008

Una de ruby: cuidadín con ||= (o cómo destruir una central nuclear sin querer)

El operador ||= de ruby, también conocido como operador de asignación condicional (conditional assignment) se suele utilizar para inicializar una variable a un valor por defecto si esta es nil o false.

Una muestra de cómo se puede liar parda si este operador no se usa correctamente:
def modificar_central_nuclear(accion, opciones={})
opciones[:avisar_supervisor] ||= false
opciones[:efectuar_operacion] ||= true
puts opciones[:avisar_supervisor] ? "Se avisa supervisor de #{accion}" : "No se avisa supervisor de #{accion}"
puts opciones[:efectuar_operacion] ? "Se realiza accion #{accion}" : "No se realiza accion #{accion}"
end
  • Esta función realiza una acción en una central nuclear recién inaugurada. Se establece como regla de negocio que siempre que se efectúe una operación, se debe avisar a un supervisor. Si no se realiza no hay que avisarlo (por ejemplo, cuando se hacen pruebas no hay que molestarle). En términos de código esto significa que en el hash opciones, los valores de :avisar_supervisor y :efectuar_operacion tendrán siempre el mismo valor en el momento de llamar a la función.
  • También se indica que, como caso especial, si se llama a esta función sin opciones, hay que efectuar la acción pero no hay que avisar al operador.
Veamos primero este último punto:
>> modificar_central_nuclear(:subir_temperatura_core)
No se avisa supervisor de subir_temperatura_core
Se realiza accion subir_temperatura_core
=> nil
Funciona bien. Ahora veamos el primer punto (suponemos q desde fuera de este código se cumple que se indican siempre las dos opciones y que estas tienen el mismo valor):
>> modificar_central_nuclear(:subir_temperatura_core, {:efectuar_operacion => true, :avisar_supervisor => true})
Se avisa supervisor de subir_temperatura_core
Se realiza accion subir_temperatura_core
=> nil
Perfecto, somos unos cracks! Y ahora hacemos lo mismo pero no queremos bajar la temperatura ni avisar al operador (estamos probando que se crean logs recién introducidos p.ej):
>> modificar_central_nuclear(:subir_temperatura_core, {:efectuar_operacion => false, :avisar_supervisor => false})
No se avisa supervisor de subir_temperatura_core
Se realiza accion subir_temperatura_core
=> nil
Ostras!! Parda habemus liada! Hemos subido la temperatura del core y encima sin avisar al supervisor... Evidentemente lo que esperábamos era que no se subiera la temperatura y que no se avisara al supervisor...

Veamos por qué pasa esto:
x ||= 'valor por defecto'
La sentencia anterior asignará el valor 'valor por defecto' a la variable x si:
  1. La variable no existe (la crea y le asigna 'valor por defecto')
  2. La variable existe y su valor anterior es nil
  3. La variable existe y su valor anterior es false
El tercer punto es el motivo por el cual "falla" el uso que hacemos de ||= con booleanos. Nosotros inicializamos opciones[:efectuar_operacion] con el valor false antes de ejecutar la función, pero una vez dentro de esta, el operador condicional por la regla 3 considera que no está inicializada (existe la variable pero su valor anterior es false) y le da el valor a la derecha del ||= (o sea, true).

Resumiendo: nunca de los jamases usar ||= con booleanos, especialmente si el valor anterior puede ser false. En el caso que nos ocupa es preferible hacer esto:
opciones[:efectuar_operacion] = true if opciones[:efectuar_operacion].nil?
El código final quedaría:

def modificar_central_nuclear(accion, opciones={})
opciones[:avisar_supervisor] = false if opciones[:avisar_supervisor].nil?
opciones[:efectuar_operacion] = true if opciones[:efectuar_operacion].nil?
puts opciones[:avisar_supervisor] ? "Se avisa supervisor de #{accion}" : "No se avisa supervisor de #{accion}"
puts opciones[:efectuar_operacion] ? "Se realiza accion #{accion}" : "No se realiza accion #{accion}"
end
Y ahora sí que obtenemos el resultado esperado:

>>modificar_central_nuclear(:subir_temperatura_core, {:efectuar_operacion => false, :avisar_supervisor => false})
No se avisa supervisor de subir_temperatura_core
No se realiza accion subir_temperatura_core
=> nil

13 julio 2008

Instalando Passenger (modrails) con Ubuntu 8.04 (aka Hardy Heron)

Voy a explicar los pasos que he seguido para instalar Passenger en Ubuntu 8.04, puesto que los "oficiales" que explica el instalador de Passenger no funcionan sin antes retocar algunos sencillos aspectos de la configuración de Apache.

Para empezar, doy por hecho que ya tenemos instalado en nuestro sistema los ingredientes previos necesarios, o sea Ruby, RubyGems y Rails. En caso contrario, sólo hay que seguir estos pasos (se explica para Ubuntu 7.10, pero imagino que no cambia casi nada para 8.04).

Paso 0: Evidentemente, debemos tener instalado Apache 2:
$ sudo apt-get install apache2
Paso 0,5: Además, necesitaremos las librerías de desarrollo de este, para que el instalador de Passenger pueda compilar el módulo:
$ sudo apt-get install apache2-prefork-dev
Paso 1: Instalar Passenger, con el "método fácil":
$ sudo gem install passenger
$ sudo passenger-install-apache2-module
Paso 2: Configurar Apache 2:
El segundo comando del paso anterior lanza el instalador del módulo de Passenger para Apache, que busca dónde está este, compila el módulo y lo instala. Además nos indica que debemos añadir las siguientes líneas al fichero de configuración de Apache, cosa que haremos con nuestro editor favorito y con permisos de root (el contenido de las líneas puede variar ligeramente):
$ sudo vim /etc/apache2/apache2.conf
Las lineas a añadir (al final del fichero) son:
# Passenger (modrails) module:
LoadModule passenger_module /usr/lib/ruby/gems/1.8/gems/passenger-2.0.1/ext/apache2/mod_passenger.so
PassengerRoot /usr/lib/ruby/gems/1.8/gems/passenger-2.0.1
PassengerRuby /usr/bin/ruby1.8
Paso 3: Probarlo con una aplicación Rails que sea über-cool.
Creamos nuestra aplicación rails con:
$ rails /home/usuario/my_fantastic_blog
$ cd /home/usuario/my_fantastic_blog
$ rake db:create
$ rake db:migrate
Ahora tenemos 2 opciones: modificar la configuración del sitio por defecto que trae Apache en ubuntu (/etc/apache2/sites-available/default) creamos un virtual host aparte como nos recomienda el instalador de Passenger. Yo he optado por esta última opción (¡atención a poner como DocumentRoot el directorio public de nuestra aplicación Rails!):
$ sudo vim /etc/apache2/sites-available/my_fantastic_blog
Ponemos esto:
<VirtualHost 127.0.0.1:8000>
   ServerName localhost
   DocumentRoot /home/usuario/my_fantastic_blog/public
</VirtualHost>
Y creamos un enlace simbólico que le indica a Apache que el nuevo virtual host está disponible:
$ sudo ln -s /etc/apache2/sites-available/my_fantastic_blog /etc/apache2/sites-enabled/001-my_fantastic_blog
E indicamos que nuestra aplicación estará disponible en el puerto 8000:
$ sudo vim /etc/apache2/ports.conf
Añadiendo la línea Listen 8000 al fichero /etc/apache2/ports.conf de forma que quede tal que así:
Listen 80
Listen 8000
<IfModule mod_ssl.c>
   Listen 443
</IfModule>
Finalmente, recargamos la configuración de Apache y ponemos en el navegador la dirección http://localhost:8000 para ver nuestra flamante aplicación Rails corriendo con Passenger:
$ sudo /etc/init.d/apache2 force-reload

PD
: Con una prueba rápida (y para nada científica :) he comparado Passenger + Apache2 contra 1 único Mongrel (corriendo en el puerto 3000 y con entorno de producción) usando para ello la herramienta httperf y la configuración por defecto tanto de Mongrel como de Passenger y Apache, resultando que: modrails es la leche! XDD. Para ser justos, debería haber utilizado nginx + un cluster de mongrels, pero daba una pereza de configurar.... Se aprecia en los resultados que un único Mongrel simplemente no puede con tantas conexiones, de ahí los errores de timeout. El PC es un Pentium 4 HT @ 2.6 GHz, con 1 GB de RAM.

Resultados para mongrel:
$ httperf --port 3000 --rate 300 --num-conn 2700 --num-call 1 --timeout 5
httperf --timeout=5 --client=0/1 --server=localhost --port=3000 --uri=/ --rate=300 --send-buffer=4096 --recv-buffer=16384 --num-conns=2700 --num-calls=1
Maximum connect burst length: 4

Total: connections 2700 requests 2366 replies 2354 test-duration 13.031 s

Connection rate: 207.2 conn/s (4.8 ms/conn, <=752 concurrent connections) Connection time [ms]: min 1.6 avg 1250.2 max 7995.8 median 704.5 stddev 1265.8 Connection time [ms]: connect 716.3 Connection length [replies/conn]: 1.000 Request rate: 181.6 req/s (5.5 ms/req)
Request size [B]: 60.0

Reply rate [replies/s]: min 197.8 avg 199.9 max 202.0 stddev 2.9 (2 samples)
Reply time [ms]: response 537.9 transfer 0.0
Reply size [B]: header 197.0 content 7557.0 footer 0.0 (total 7754.0)
Reply status: 1xx=0 2xx=2354 3xx=0 4xx=0 5xx=0

CPU time [s]: user 0.68 system 11.69 (user 5.2% system 89.7% total 95.0%)
Net I/O: 1378.5 KB/s (11.3*10^6 bps)

Errors: total 346 client-timo 346 socket-timo 0 connrefused 0 connreset 0
Errors: fd-unavail 0 addrunavail 0 ftab-full 0 other 0
Resultados para Passenger:
$ httperf --port 8000 --rate 300 --num-conn 2700 --num-call 1 --timeout 5
httperf --timeout=5 --client=0/1 --server=localhost --port=8000 --uri=/ --rate=300 --send-buffer=4096 --recv-buffer=16384 --num-conns=2700 --num-calls=1
Maximum connect burst length: 1

Total: connections 2700 requests 2700 replies 2700 test-duration 8.998 s

Connection rate: 300.1 conn/s (3.3 ms/conn, <=3 concurrent connections) Connection time [ms]: min 0.1 avg 0.6 max 8.6 median 0.5 stddev 0.3 Connection time [ms]: connect 0.0 Connection length [replies/conn]: 1.000 Request rate: 300.1 req/s (3.3 ms/req)
Request size [B]: 60.0

Reply rate [replies/s]: min 300.0 avg 300.0 max 300.0 stddev 0.0 (1 samples)
Reply time [ms]: response 0.6 transfer 0.0
Reply size [B]: header 260.0 content 7557.0 footer 0.0 (total 7817.0)
Reply status: 1xx=0 2xx=2700 3xx=0 4xx=0 5xx=0

CPU time [s]: user 1.88 system 6.46 (user 20.8% system 71.8% total 92.6%)
Net I/O: 2308.2 KB/s (18.9*10^6 bps)

Errors: total 0 client-timo 0 socket-timo 0 connrefused 0 connreset 0
Errors: fd-unavail 0 addrunavail 0 ftab-full 0 other 0


Actualización
Acabo de encontrar este enlace donde explica este mismo procedimiento para Ubuntu 7.10 (eso sí, en inglés) junto con una bonita tarea de Capistrano que hace automáticamente un touch del fichero my_fantastic_blog/tmp/restart.txt (el cual reinicia la aplicación Rails) y además elimina el fichero my_fantastic_blog/tmp/public/.htaccess que según el puede causar problemas.

21 junio 2008

Google también se cae...

Pues sí, al Todopoderoso Google también le toca hacer tareas de mantenimiento de vez en cuando, por más servidores y replicación que tengan...

En concreto, el servicio que ha dejado de funcionar (temporalmente, claro) es Adsense (a las 22:36 @ 21-06-08):



Fijaros en la URL: .../system_down.html. Como pillen al que le ha dado al Power sin querer, se le cae el pelo! XD

Y para que quede clarito, te lo dicen en 27 idiomas

15 junio 2008

Una de rails: usando lockdown con JRuby (y Netbeans)

Ahora mismo estoy trasteando con RubyOnRails 2.1 + JRuby + Netbeans 6.1 creando una pequeña aplicación Rails para ver cuan diferente es el desarrollo con ruby nativo (MRI) vs. JRuby. La idea es que esta aplicación funcione en un servidor de aplicaciones Java via JRuby.

Por otro lado, una parte fundamental de las aplicaciones web es la autenticación de usuarios. Para ello he decidido aplicarme el principio de DRY (o no reinventar la rueda) y usar algun sistema ya existente para RoR. El elegido ha sido Lockdown (web anterior) pues su filosofía es simple: cierra el acceso a una aplicación Rails de forma global y permite dar determinados permisos a diferentes grupos de usuarios para realizar tareas concretas.

Y aquí viene el primer problema: el JRuby integrado en Netbeans utiliza un directorio propio para almacenar las gemas, de forma que las que tengamos instaladas en el ruby "nativo" del sistema no las podemos utilizar. Hay que instalar las gemas que vayamos a usar en el proyecto con la herramienta que encontraremos en el menú "Tools -> Ruby Gems" de Netbeans, asegurándonos que tenemos escogida la plataforma "Built-in JRuby" en el momento de la instalación.

Según el web de Lockdown, estos son los pasos para utilizarlo en un proyecto rails con ruby nativo:

$ sudo gem install lockdown
$ cd ~/code/rails/miproyecto
$ lockdown .
Como esto no nos sirve para el desarrollo con Netbeans, aquí van los pasos equivalentes:
  1. Instalar la gema lockdown en el JRuby de Netbeans: Abrir "Tools -> Ruby Gems", escoger la plataforma ruby "Built-in JRuby", ir a la pestaña "New Gems" e introducir en "Search" la palabra "lockdown" + la tecla intro. Seleccionar la gema "lockdown" de la lista de resultados y pulsar el botón "Install".
  2. Abrir una consola e ir al directorio del proyecto rails:
    • cd ~/code/rails/miproyecto
  3. Ahora la triquiñuela: ejecutar el lockdown instalado en Netbeans (averiguando antes dónde está instalado el Netbeans) con este comando:
    • PATH=/opt/java/netbeans-6.1/ruby2/jruby-1.1/bin:$PATH lockdown .
Si todo ha ido bien, veremos la siguiente salida en la consola:

------------------------------------------------------------
Installing Lockdown
create lib/lockdown
create lib/lockdown/session.rb
create lib/lockdown/init.rb
------------------------------------------------------------

------------------------------------------------------------
Modified config/environment.rb by adding:
require "lockdown/init"
------------------------------------------------------------

------------------------------------------------------------
You are now locked down. To open up access to your
application please modify lib/lockdown/init.rb. This is
where you'll add permissions and create user groups.

To modify the contents of your session and to add access
methods, modify lib/lockdown/session.rb.

For the wiki, news, forum and issue tracker please visit:

http://stonean.com/projects/show/lockdown

------------------------------------------------------------

Ahora hay que utilizar el generador que proporciona lockdown para crear sus controladores, modelos, vistas y migrations específicos. En teoría, el generador debería aparecer en Netbeans clicando con el botón derecho sobre el proyecto, opción "Generate..." y entonces bajo el desplegable "Generate:". Como a mí no me ha aparecido, he usado la consola (ojo a utilizar el jruby correcto!):

$ /opt/java/netbeans-6.1/ruby2/jruby-1.1/bin/jruby ./script/generate lockdown -all

Se ejecutan las migrations con: click botón derecho sobre el proyecto, opción "Migrate Database" -> "To Current Version" y se elimina el fichero de índice "public/index.html" creado por defecto por Rails.

Arrancamos el servidor (tecla F6 o icono del "play" en Netbeans), y ya podemos acceder a nuestra aplicación rails con las credenciales admin/password en la página de login que aparecerá.

Podemos ver los usuarios en http://localhost:3000/users, los grupos en http://localhost:3000/user_groups y los permisos en http://localhost:3000/permissions.

Y listo. Ahora sólo queda modificar el fichero lib/lockdown/init.rb creando permisos y grupos de usuarios de forma apropiada.

Para más detalles, consultar la documentación del generador de lockdown.

19 abril 2008

¡Mi primer libro! (o conociendo Bubok.es)

Acabo de publicar mi primer libro. Y he tardado 5 minutos en encontrar editor, escoger la portada y la contraportada y decidir el precio al que quiero venderlo. Además, he escogido publicarlo tanto en formato electrónico como en papel, al "módico" precio de 3.99 € y 11.99 € respectivamente.

Parece demasiado bonito para ser verdad. Pero lo es: el "editor" no es una persona de carne y huesos, cruel, vil y sin escrúpulos (estoy de coña :P), sino el sitio de nueva creación Bubok.es, made in Spain, oigan. Por un precio muy competitivo (0 €, o sea, gratis) permiten publicar libros tanto a autores amateurs como profesionales y darse a conocer de forma muy fácil.

La versión en papel tiene un precio mínimo de venta (para cubrir costes de producción) que varía según el formato, tipo de papel, etc que escojamos para el libro. La electrónica, podemos venderla desde 0 €. En ambos casos se puede poner el precio de venta que se desee al libro, siendo el 80% de las ganancias para el autor y el 20% restante para Bubok.es. Los libros se suben en formato PDF y la portada y contraportada se editan allí mismo con las plantillas proporcionadas o, cosa que me ha parecido muy de agradecer, subiendo una imagen hecha por uno mismo.
Además ofrecen servicios de valor añadido a los autores (esto sí hay que pagarlo) como maquetación, portadas hechas por diseñadores profesionales, publicitación del libro e incluso la creación de pósters y marcadores de libro personalizados. Lo que más me ha gustado es que todo el proceso -publicación, promoción, venta del libro- se gestiona desde su web, la cual es usable a la par que elegante.

Se nota que esta gente empiezan fuerte y con ganas: han conseguido los favores de mismísimo Alberto Vázquez-Figueroa, con 18 libros publicados en el sitio (todos se pueden obtener gratuitamente en su versión electrónica y con un precio muy asequible la de papel). No se cómo lo han logrado pero es una gran baza para ellos y su sitio.

La verdad, no espero que nadie compre mi libro en ninguna de las dos versiones -yo incluido- pero me ha hecho gracia probarlo y creo que esta va a ser una plataforma revolucionaria para los escritores del MundoReal (TM). Como una Operación Triunfo, pero con libros, vaya. Una recomendación para los creadores del sitio -que seguro que ya habéis tenido en cuenta-: aprovechad este Sant Jordi para daros a conocer!!

PD: el libro tan solo es mi proyecto final de carrera que hice en la ETIG, o sea que puede interesar como mucho a 2 personas en el mundo xDD. Estas son fáciles de reconocer: les gusta traducir código fuente de Java a C++, las redes peer to peer (P2P) y conocen un simulador que responde al nombre de PlanetSim.

25 enero 2008

Nuevo coche o ¡Que contento que toooooyyy!!!

El título del post no deja lugar a dudas. Me he comprado un coche. Nuevecito del concesionario.

Sus datos son:
  • Citroën HDi 92 CV Collection
  • Color: Rojo Lucifer nacarado. ¡Como mola el nombre! Y punto.
  • Motor: Dieselón de 92 CV, sobrado para lo que lo voy a usar.
  • Extras: Pack Bluetooh Collection (pintura, llantas, pijadas varias y, ¡oh sorpresa!, Bluetooth) y Supercierre.
  • Fotos de rigor: