Por algún motivo, todos los desarrollos que empiezo con ruby necesitan utilizar imagemagick... Y aunque no es algo nuevo, pero es algo que necesito hacer una y otra vez (buscando en google antes), hoy lo voy a dejar por escrito.

Image magick

Mi plataforma: osx 10.6 (Snow Leopard). Aunque estoy usando rvm para gestionar mis rubies, esta instalación la voy a hacer sobre la versión del sistema (rvm system)... lo que implica usar sudo (si usas rvm con otra versión, omítelo)

Lo malo de ruby con imagemagick es que necesita compilar código nativo para hacer la interfaz entre ambos, lo que a su vez significa que se necesitan los ficheros de cabecera de imagemagick para que el compilador C se quede contento (que tiempos cuando java dominaba el mundo y podías hacer copy&paste incluso entre distintos operativos y plataformas...)

Para instalar imagemagick y sus cabeceras utilizo macports

sudo port install libxml2
sudo port install ImageMagick +no_x11

... tras la segunda línea es un buen momento para tomar un café.

Por último, instalamos la gema rmagick. También tarda lo suyo...

sudo gem install rmagick

Para hacer la prueba de que todo hay ido bien...

$ irb -rubygems -r RMagick
irb(main):001:0> puts Magick::Long_version
This is RMagick 1.15.3 ($Date: 2007/01/20 15:45:29 $) Copyright (C) 2007 by Timothy P. Hunter
Built with ImageMagick 6.3.2 02/23/07 Q8 http://www.imagemagick.org
Built for ruby 1.8.5 (2006-12-25 patchlevel 12) [powerpc-darwin8.0.0]
Web page: http://rmagick.rubyforge.org
Email: rmagick@rubyforge.org
=> nil

Hasta otra!

(Con agradecimientos a este otro blog en inglés que contiene la información crítica y a la guía de instalación de rmagick que descubrí demasiado tarde)

El API java.lang.management permite, desde java 1.5 (y estamos ya camino de la versión 7) explorar varios parámetros de la máquina virtual en la que se ejecuta un programa java.

Coffee and Milk

No es una api muy extensa... partiendo de la clase ManagementFactory podemos obtener las instancias capaces de darnos los datos que nos interesan.

En mi caso, necesitaba comprobar que al lanzar una herramienta, los parámetros pasados a la máquina virtual eran los correctos (varios valores para los típicos -Xmx y similares). Esto se consigue muy fácilmente con:

ManagementFactory.getRuntimeMXBean().getInputArguments();

Durante mis breves experimentaciones he llegado a dos conclusiones:

  1. El parámetro -server no es muy fiable. En Snow Leopard aparece como -Xserver, pero en ubuntu no aparece...
  2. La memoria máxima no coincide con el valor esperado de -Xmx (aunque está cerca). Esto es verdad para Runtime.getRuntime().maxMemory() y para su análogo en la ''management api'' ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getMax()

...y esto es lo que he descubierto hoy. ¿Qué APIs/librerías has descubierto tú que te han sacado de un apuro?

Este fin de semana iba en un avión de una de esas líneas de bajo coste que alguna gente tanto odia (a mi me parecen útiles, pero es otro tema...).

Barricade, Vichy France

El avión iba lleno hasta los topes y para los últimos en embarcar no quedaba espacio dónde poner el equipaje de mano... yo tuve suerte, pero fui el último (o de los últimos). Para los demás, la opción era guardar el equipaje de mano en la bodega y recogerlo en la cinta... como si lo hubiesen facturado.

Alguna gente que llegó después tenía un problema... debía conectar con otro vuelo al aterrizar y no tenía tiempo para pasar por la recogida de equipaje.

Lamentablemente, la reacción de alguno de los afectados no me sorprendió en absoluto:

Si no hay espacio no es mi problema y si el avión no despega, pues no despega.

¿Es eso negociación?

Unos minutos antes, otros pasajeros (conocidos entre si) habían intercambiando sus maletas para que el que tenía un enlace no tuviese problemas y el otro, con más tiempo, simplemente tenía que esperar por su maleta al aterrizar.

¿Cómo de difícil hubiese sido preguntar a algún desconocido si podría hacer el mismo favor?

Y ahora... si aplicamos lo mismo a las empresas... ¿qué tenemos?

Tengo la sensación de que nuestra cultura iguala "negociar bien" con "conseguir todo a cambio de nada"... algo falso y peligroso.

¿Tenéis experiencias similares o yo me rodeo de bichos raros? ¿Pasa lo mismo fuera de España?

Estoy de reenganche con mis proyectos tras venirme de vacaciones (la primera vez que descanso un mes entero, se hace duro). Además, me he actualizado la versión de OSX a Snow Leopard ...

Oh, sorpresa, las pruebas de uno de los proyectos fallan con un místico Invalid memory access for location.

Mi conclusión, después de buscar mucho en google, es que se trata de algún tipo de bug en la gestión de memoria que ocurre al combinar la nueva versión de Java en Snow Leopard con las clases compiladas por el compilador de JDT (yo uso Eclipse 3.5 todavía... quizá con el nuevo Eclipse 3.6 esto no pase...).

Yo lo he solucionado haciendo que -Xms y -Xmx coincidan y poniendo suficiente PermGenSpace (aunque eso es otra guerra), tanto para Eclipse como para el lanzador de Junit.

Lo "bueno" de todo esto es que desde línea de comandos todo funciona (ya que no se usa el compilador de JDT).

Otras solución que he leído (pero no probado) es ejecutar java en modo interpretado (bastante más lento).

Tras leer a JMBeas en agile-spain insistir en la necesidad de un cambio cultural entre los técnicos y enumerar cosas como TDD , Pair Programming y otras sutilezas técnicas me ha venido un pensamiento a la cabeza...

Si volviese a nacer... pediría tener las orejas más grandes, para saber escuchar a los clientes. Y un sacacorchos, para sacarles lo que realmente quieren (y tenerlos enganchados para que dediquen el tiempo que ello requiere).

Sé que no tiene mucho que ver con el comentario de Beas, pero es curioso como funciona el cerebro... cualquier cosa puede disparar una chispa. En este caso, es que me cansa que echemos las culpas de todos los males del mundo a la (falta de) habilidades técnicas.

¡Ah! Y que sí, que tenemos que mejorar técnicamente y no asumir que con un curso de 40 horas de Cobol uno es programador senior...

En las películas siempre hay un buen número de gente ansiosa y/o desesperada por quedarse con una herencia.

Por algún motivo, eso no pasa en el mundo del software.

Pero estoy divagando... ¿Estás intentando rake rails:freeze:gems con una versión antigua de rails y obtienes undefined method `manage_gems' for Gem:Module como respuesta?

Respuesta corta: Edita framework.rake y comenta la línea Gem.manage_gems ... al parecer, ya no es necesaria

En mi caso, he heredado una aplicación rails 2.1.0 que quiero actualizar a 2.1.2, que es la versión 2.1.x soportada por Hoptoad (obviamente, con 2.3.5 no va). La aplicación tiene un par de años y, en este tiempo, rubygems ha cambiado (¿dónde están mis 2 años de garantía?).

Para localizar la línea que hay que eliminar, es útil la opción --trace de rake:

rake rails:freeze:gems VERSION=2.1.2 --trace

Llamadme anticuado, pero cuando java dominaba el mundo estas cosas no pasaban... ;-)

Hoy muchos afortunados estarán reunidos en el Code Retreat de Segovia practicando, compitiendo y picándose con el de al lado para ver quien es el más rápido, más hábil...

Por varios motivos no he podido estar... así que como premio de consolación os dejo mi versión del Conway's Game of Life

Pese a haberlo repetido muchas (pero muchas) veces, todavía hay varias meteduras de pata al teclear ... pero estoy bastante satisfecho. La primera prueba era de casi 15 minutos... y esta es la mitad (incluyendo un pequeño refactoring).

Es la primera vez que utilizo una herramienta para BDD:http://behaviour-driven.org/ al realizar una implementación y me ha creado una sensación agridulce.

  • Es estimulante saber exactamente cuanto queda para terminar un proyecto
  • Los criterios de aceptación están a un nivel tan alto que hay ocasiones en las que se pierde el ritmo... y eso que este ejercicio es muy pequeño. A mi me ocurre al pasar del caso de prueba para celdas que deben morir al celdas que deben vivir... hay un montón de código que escribir y ningún criterio de aceptación que sirva de guía.
  • Para mi gusto, el código resultante es malo. Quizá sea sobreingeniería, pero yo veo al menos una clase Cell (que no aparece al guiarse sólo por los criterios de aceptación). Posiblemente con necesidades más complejas la clase Cell aparecería.

Y sí, ya sé que no sólo de BDD vive el hombre... y que en un proyecto real habría varios niveles de pruebas más.

Ahora me queda pendiente hacer la prueba en Java, con funciones de autocompletar en el propio IDE (con ruby funciona muy, muy mal...)

Bueno... ¿y los demás qué? ¿qué tal habéis pasado el día en Segovia?

Actualización

Como algunos han preguntado, las canciones que suenan son ハッピィケンキュウジョ y Night Out - Instrumental

Hace poco que he empezado a evolucionar una aplicación escrita hace más de un año usando ruby on rails.

La aplicación funciona sin problemas en Ubuntu Karmic (en concreto, esta imagen de amazon) con la última versión de ruby disponible mediante apt-get.

El problema

El problema es que en mi máquina local la cosa no iba tan bien. El motivo es una diferencia en el patchlevel de las versiones:

ruby ruby 1.8.7 (2009-06-12 patchlevel 174) [i486-linux]
ruby ruby 1.8.7 (2010-01-10 patchlevel 249) [i686-darwin9]

Yo uso MacPorts:http://www.macports.org/ para instalar ruby en mi mac y, aunque hay varias versiones de ruby disponibles, sólo está patchlevel más reciente.

La solución

Como siempre, google trae la respuesta. Joe Homs cuenta como crear una fuente local para MacPorts que contenga la versión anterior que queramos.

No es complicado:

  • Crea una carpeta para la fuente local.
mkdir /opt/macports-local-repo
  • Descarga la revisón a tu repositorio
cd /opt/macports-local-repo/
svn co http://svn.macports.org/repository/macports/trunk\
/dports/lang/ruby@62943 lang/ruby
  • Informa a MacPorts de que hay una nueva fuente. Para eso, modifica /opt/local/etc/macports/sources.conf y añade una línea con el protocolo file://. En mi caso:
file:///opt/macports-local-repo/
rsync://rsync.macports.org/release/ports/ [default]
  • Actualiza el índice de MacPorts
sudo portindex /opt/macports-local-repo/
  • Comprueba que todo ha ido bien
port search --exact ruby
ruby @1.8.7-p174 (lang, ruby)
    Powerful and clean object-oriented scripting language

ruby @1.8.7-p249 (lang, ruby)
    Powerful and clean object-oriented scripting language

Found 2 ports.
  • ¡Y a instalar!
sudo port install ruby @1.8.7-p174

Bonus

MacPorts permite activar y desactivar versiones, así que no hay problema en tener unas cuantas versiones instaladas, dependiendo del proyecto:

sudo port installed ruby
The following ports are currently installed:
  ruby @1.8.7-p72_2+thread_hooks
  ruby @1.8.7-p174_2+darwin+thread_hooks (active)
  ruby @1.8.7-p249_0+darwin+thread_hooks

Y para cambiar de una versión a otra...

sudo port activate ruby @1.8.7-p249*
--->  Deactivating ruby @1.8.7-p174_2+darwin+thread_hooks
--->  Activating ruby @1.8.7-p249*
AbelBook:~ amuino$ sudo port installed ruby
The following ports are currently installed:
  ruby @1.8.7-p72_2+thread_hooks
  ruby @1.8.7-p174_2+darwin+thread_hooks
  ruby @1.8.7-p249_0+darwin+thread_hooks (active)

Hace algún tiempo que tengo esa idea en la cabeza... la primera vez que la comenté con alguien fue tomando un café con Leo Antoli y poco después, en una conversación en la lista de Agile Spain.

La mayor parte del material sobre agilismo hace referencia a mejorar el equipo de desarrollo. Mejorar sus skills técnicos. Mejorar su comunicación con el cliente. Mejorar la calidad del producto. Mejorar la calidad del proceso.

Aunque todo eso está muy bien, hay algo que chirria... ¿cómo podemos mejorar los procesos y la comunicación con el cliente sin el cliente?

Todo para el pueblo pero sin el pueblo

Hay una cierta prepotencia subyacente en que el Agilismo se esté vendiendo a los roles técnicos. Nosotros sabemos mejor que nadie como funciona esto.

El agilismo de guerrilla, el volar bajo el radar son cool. Hace que los técnicos puedan reconfortar sus egos al sentirse mejores que esos estúpidos jefes de proyecto que ponen fechas imposibles.

Pero, en realidad... ¿quien tiene la sartén por el mango?

Si no han cambiado mucho las cosas: el que paga... es decir, el cliente.

Los técnicos pueden ser super cool, adoptar todas las buzzwords e implantar las tecnologías y herramientas más cutting edge del sector. Pueden esforzarse mucho. Pueden hacer demos y montar un entorno de CI (¡y yo les animo a todo ello!).

Pero si el cliente no juega a lo mismo, si no está realmente implicado en el porqué de esas demos (detectar defectos e introducir los cambios cuanto antes)... entonces un día, poco antes de la fecha de entrega, llegará la orden de "hay que cambiarlo todo".

Y el equipo técnico perderá la sonrisa.

¿Y si fuese al revés? ¿Y si el cliente fuese el que exigiese una forma de trabajo ágil?

Pasarían dos cosas beneficiosas:

  • Los parásitos del sector desaparecerían. Los malos profesionales no podrían sostener el ritmo. Es mucho más difícil entregar un producto funcionando cada 1 o 2 semanas que entregar papelotes para que parezca que sabemos lo que hacemos.
  • El cliente se convertiría, automáticamente, en un aliado. No habría que buscar un Product Owner (lo que parece ser la mayor queja en la comunidad ágil española).

Entonces... ¿porqué seguimos vendiendo agilismo a los chicos de la cueva?

Solr logo Sigo encontrando problemas al procesar volúmenes de información importantes con ruby y su ecosistema. Estamos hablando de algo más de dos millones y medio de registros... no es para tanto ¿no?

En este caso el síntoma es la muerte de ruby por falta de memoria durante la reconstrucción del índice de solr (un servidor para búsquedas de texto) con acts_as_solr (un plugin de rails para utilizar solr con active record).

AbelBook:triptance amuino$ rake solr:reindex
(in /Users/amuino/dev/triptance/svn/trunk/triptance)
Clearing index for City...
Rebuilding index for City...
ruby(7890,0xa0398720) malloc: *** mmap(size=2097152) failed (error code=12)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug
ruby(7890,0xa0398720) malloc: *** mmap(size=2097152) failed (error code=12)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug
ruby(7890,0xa0398720) malloc: *** mmap(size=1052672) failed (error code=12)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug
ruby(7890,0xa0398720) malloc: *** mmap(size=1052672) failed (error code=12)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug
rake aborted!
failed to allocate memory

Un poco de google me descubre que no soy el primero, aunque sí que soy muy raro...

Como tengo bastante más soltura con java que con ruby, vuelvo a ejecutar la tarea con jruby y a analizar el consumo de memoria con jconsole. Un beneficio añadido es que la JVM me permite limitar la memoria y puedo reproducir los errores mucho más rápido (ruby sólo falla cuando el operativo no puede asignar más memoria... después de asignar los 64Gb de memoria virtual).

Tras mucho ensayo y error, parece que el consumo de memoria está estabilizado...

Estos son los cambios:

  • Leer los modelos como :readonly
  • Evitar crear un nuevo array con la versión solrizada de los modelos
  • Organizar el código para que todo siga funcionando

Es decir:

acts_as_solr/lib/class_methods Línea 204 y siguientes...

   def rebuild_solr_index(batch_size=0, &finder)
      finder ||= lambda { |ar, options| ar.find(:all, options.merge({:order => self.primary_key})) }
      start_time = Time.now

      if batch_size > 0
        items_processed = 0
        limit = batch_size
        offset = 0
        begin
          iteration_start = Time.now
          items = finder.call(self, {:limit => limit, :offset => offset, :readonly => true})
          items_processed += items.size
          last_id = items.last.id if items.last
          offset += items.size

          items.collect! { |content| content.to_solr_doc }
          if items.size > 0
            solr_add items
            solr_commit
          end
    
          time_so_far = Time.now - start_time
          iteration_time = Time.now - iteration_start         
          logger.info "#{Process.pid}: #{items_processed} items for #{self.name} have been batch added to index in #{'%.3f' % time_so_far}s at #{'%.3f' % (items_processed / time_so_far)} items/sec (#{'%.3f' % (items.size / iteration_time)} items/sec for the last batch). Last id: #{last_id}"
        end while items.nil? || items.size > 0
      else
        items = finder.call(self, {})
        items.each { |content| content.solr_save }
        items_processed = items.size
      end
      solr_optimize
      logger.info items_processed > 0 ? "Index for #{self.name} has been rebuilt" : "Nothing to index for #{self.name}"
    end

De nuevo, espero que esto le ahorre a alguien un poco de tiempo

Esta es la línea de comandos final...

jruby --server -J-Xmx1024m -w -S rake solr:reindex CLEAR=true BATCH=500

Y este, el consumo de memoria...

Consumo de memoria después de modificar acts_as_solr

Cerrar