lunes, 29 de diciembre de 2008

Código duplicado en Ruby

En mi anterior trabajo, las raras veces que entre reunión y documento y reunión me dejaban trabajar un rato, una de las herramientas imprescindibles para mi en Netbeans era PMD, tanto para detectar errores como para intentar hacer mejor código.

Netbeans da algunas sugerencias de código en Ruby, subraya las variables no usadas, pone en negrita las variables/métodos no definidos,... pero todavía le falta para llegar al nivel de PMD en Java.

He estado buscando a ver si hay algo parecido para Ruby y dentro de PMD he encontrado que tiene un módulo para detectar código duplicado: Copy paste detector que se puede ejecutar desde linea de comando o usando Java Web Start.

Dependiendo del tamaño del bloque repetido a buscar a veces da un

java.lang.ArrayIndexOutOfBoundsException: -1
at java.util.ArrayList.get(Unknown Source)
at net.sourceforge.pmd.cpd.SourceCode.getSlice(SourceCode.java:133)
at net.sourceforge.pmd.cpd.MatchAlgorithm.findMatches(MatchAlgorithm.java:84)


Pero jugando un poco con el tamaño funciona y he encontrado unos cuantos trozos de código a refactorizar :-)

Y como ejemplo, la salida que da al ejecutarlo sobre el directorio rails. Con un tamaño de chunk menor de 60 da la excepción anterior, pero con un tamaño mayor funciona bien, y sólo encuentra duplicados en los test y en ficheros de zonas horarias, así que parece que por lo que respecta a código duplicado, rails está bien hecho :-)


jueves, 11 de diciembre de 2008

Curioso comportamiento de nil.id

En un trozo de código similar a este:

user_id = User.find_by_email(email).id rescue nil
if user_id.nil?
....


Me he vuelto a encontrar otra vez con un curioso comportamiento. En desarrollo funciona bien, pero en producción devuelve un user_id incorrecto en lugar de un nulo cuando el usuario no existe.

Es debido la forma de acceder del objeto nil a su atributo id. En modo desarrollo devuelve una excepción, pero en cambio en modo producción, sólo da un warning

$ ruby script/console
Loading development environment (Rails 2.1.2)
>> nil.id
RuntimeError: Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id
from (irb):1
>> exit
$ ruby script/console production
Loading production environment (Rails 2.1.2)
>> nil.id
(irb):1: warning: Object#id will be deprecated; use Object#object_id
=> 4


La solución es muy simple, o acceder al objeto a traves de []

user_id = User.find_by_email(email)[:id] rescue nil
if user_id.nil?
....


o acceder al id despues de comprobar el nil

user = User.find_by_email(email)
if user.nil?

Es algo trivial, pero a ver si escribiéndolo no me vuelve a pasar... :-p

jueves, 20 de noviembre de 2008

Conferencia Rails 2008 - Impresiones

Este año he salido de la conferencia con la impresión de que ha sido un poco más floja que el año pasado, aunque puede ser algo subjetivo, ya que el año pasado fue la primera a la que iba y muchos de los conceptos eran nuevos para mi

Bueno, en mi opinión las charlas dignas de mención son:


Son dignas de mención más que por su contenido, ya que al final hay cosas que ya habia oido y leido varias veces, por tener ponentes competentes, con capacidad de mantener la atención, que se saben explicar, que se han preparado el contenido, que saben contestar a las preguntas... vamos, los requisitos minimos que debería tener una presentación para ser aceptada

El resto, o no fui o no merecen la pena ser comentadas....

lunes, 13 de octubre de 2008

miércoles, 6 de agosto de 2008

Escalado proporcional de imágenes con attachment_fu

Por defecto attachment_fu (y su predecesor acts_as_attachment), no escalan las imagenes proporcionalmente al pasarles el parámetro :resize_to, sino que simplemente cambian el ancho y alto a los valores del parámetro.

En attachment_fu, para que escale es tan simple como usar un 'geometry string'. En este test se pueden ver varios ejemplos de escalado

http://github.com/technoweenie/attachment_fu/tree/master/test/geometry_test.rb

así que si queremos tener un tamaño máximo de 200 x 200 pondremos

has_attachment :resize_to => "200x200>"


En el caso de acts_as_attachment, no tenemos esa posibilidad, pero el cambio para que escale es muy simple. En el fichero

vendor/plugins/acts_as_attachment/lib/technoweenie/acts_as_attachment/instance_methods.rb

Dentro del método attachment_data cambiamos

thumbnail_for_image(img, attachment_options[:resize_to])

por

size = attachment_options[:resize_to]
if size.is_a?(Array)
size_ratio = size[0].to_f / size[1].to_f
ratio = img.columns.to_f / img.rows.to_f rescue 0
large_width = (ratio > size_ratio or ratio == 0) ? size[0] : size[1]*ratio
large_height = (ratio < size_ratio or ratio == 0) ? size[1] : size[0]/ratio
size = [large_width, large_height]
end
thumbnail_for_image(img, size)

Y ya escala proporcionalmente. Este trozo de código podría ser más elegante, tal y como esta hecho en

http://github.com/technoweenie/attachment_fu/tree/master/lib/geometry.rb

Pero bueno, ese código lo tengo hecho hace tiempo y funciona :-)

miércoles, 30 de julio de 2008

foreign key migrations

Hoy acabo de darme cuenta por n-esima vez, y no escarmiento, de que a veces es importante leer antes de ponerse a hacer las cosas.

Me he instalado el plugin Foreign Key Migrations y me daba este error en el db:migrate

rake aborted!
undefined method 'name' for #
C:/projects/f2f/community/vendor/plugins/foreign_key_migrations/lib/red_hill_consulting/foreign_key_migrations/active_record/connection_adapters/table_definition.rb:16:in 'column'
C:/projects/f2f/community/vendor/plugins/foreign_key_migrations/lib/red_hill_consulting/foreign_key_migrations/active_record/connection_adapters/table_definition.rb:11:in 'primary_key'
C:/projects/f2f/community/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb:96:in 'create_table'


Al principio he pensado que era culpa de que era cosa de que lo he instalado en Rails 2.1.0 y después de perder un buen rato depurando y buscando donde estaba definido el metodo name de TableDefinition, la solución era tan simple como leerse la página del plugin:

Dependencies

  • RedHill on Rails Core (redhillonrails_core).

Y efectivamente, despues de instalar RedHill on Rails Core ya funciona.... que penita....

viernes, 18 de julio de 2008

Exportar a excel desde ruby

Normalmente, la exportación a Excel la resuelvo simplemente creando un fichero csv con FasterCSV

csv_string = FasterCSV.generate do |csv|
# Aquí la generación del csv
end
send_data csv_string, :filename => 'nombre.xls'


Pero me he encontrado en una aplicación que el cliente pide directamente un Excel para evitar la pantalla de importar CSV. He encontrado estas alternativas:

  • Librerias Win32OLE, pero esta opción no me vale ya que el servidor es Linux
  • La gema Spreadsheet-Excel , que genera formato Excel 95
  • Generar una tabla HTML y ponerle las cabeceras de excel
  • Generar un XML para Excel 2003 tal y como cuentan en la wiki de rubyonrails
Al final la solución que he elegido es la última, practicamente ha sido un copy-paste del ejemplo añadiéndole un nuevo estilo en negrita:

xml.Style 'ss:ID' => 'Header' do
xml.Font 'ss:Bold' => '1'
end

Y aplicar ese estilo a las celdas de la cabecera

xml.Cell 'ss:StyleID'=>'Header' do


El único problema que me he encontrado es que OpenOffice no reconoce un .xls en formato XML, así que para abrirlo con Calc, hay que renombrarlo a .xml. Con Office se abre sin problemas.

martes, 8 de julio de 2008

Tag Cloud Widget

No me gusta nada como se muestran por defecto los tags, simplemente en un listado, me gustá más la opción de mostrar un Tag Cloud

Y en Compender he encontrado la solución que ya he puesto en este blog :-)

http://www.compender.com/2007/12/simple-tag-cloud.html

viernes, 27 de junio de 2008

Error 501 Syntactically invalid HELO argument(s) en envío de mails

Enviando una newsletter me he encontrado con que un servidor rechazaba los mails con el mensaje de error:

Error 501 Syntactically invalid HELO argument(s) en envío de mails

Investigando un poco parece ser que el problema es que el nombre de servidor que tenía definido en /etc/hosts tenía un _ y tal y como cuentan aquí no cumple el estandar RFC2821

To promote interoperability and consistent with long-standing
guidance about conservative use of the DNS in naming and applications
(e.g., see section 2.3.1 of the base DNS document, RFC1035 [22]),
characters outside the set of alphas, digits, and hyphen MUST NOT
appear in domain name labels for SMTP clients or servers. In
particular, the underscore character is not permitted. SMTP servers
that receive a command in which invalid character codes have been
employed, and for which there are no other reasons for rejection,
MUST reject that command with a 501 response.


Lo curioso es que practicamente ningún servidor hace esa comprobación, ya que sólo me ha pasado con un dominio.

Y para acabar una demostración del problema:

telnet mailin.blueyonder.co.uk 25
Trying 195.188.53.99...
Connected to mailin.blueyonder.co.uk.
Escape character is '^]'.
220 exim9.blueyonder.co.uk ESMTP Exim 4.68 Tue, 17 Jun 2008 07:54:52 +0100
helo prueba_1
501 Syntactically invalid HELO argument(s)
8 Tue, 17 Jun 2008 07:46:24 +0100
helo prueba
250 exim13.blueyonder.co.uk Hello prueba

domingo, 15 de junio de 2008

ImageScience y FreeImage en x86_64

Hace un tiempo tuve que hacer un pequeño tratamiento de imagenes, consistente en reescalar las imagenes subidas por los usuarios al servidor. Despues de leer sobre las alternativas disponibles, que son básicamente las que aparecen aquí (RMagick, Mini-magic y ImageScience), me decidí por ImageScience.

Un pre-requisito es instalar FreeImage, pero al instalar la versión 3.10 en Slicehost (Dual-Core AMD Opteron de 64 bits)

uname -a
Linux pre.beruby.com 2.6.18-xen #1 SMP Tue Feb 12 06:40:50 UTC 2008 x86_64 GNU/Linux

da un error de compilación

Source/FreeImage/PluginBMP.cpp: In function 'BOOL LoadPixelDataRLE4(FreeImageIO*, void*, int, int, FIBITMAP*)':
Source/FreeImage/PluginBMP.cpp:227: error: no matching function for call to 'MIN(int&, long int)'
Source/FreeImage/PluginBMP.cpp:282: error: no matching function for call to 'MIN(int&, long int)'
make[1]: *** [Source/FreeImage/PluginBMP.o] Error 1


Como siempre, googleando un poco encontré un parche con la solución que consiste simplemente en cambiar la definición de un par de variables desde DWORD y int a long.

Parece que en la última versión de CVS de FreeImage ya esta solucionado el error, pero bueno, esta solución tambien es valida :-)

lunes, 9 de junio de 2008

indice TIOBE de lenguajes de programación

Hace un tiempo que todos los meses miro el indice TIOBE. El método que utilizan puede ser discutible, pero es una forma de saber las nuevas tendencias y ver hacia donde se mueven los lenguajes de programación.

Una conclusión clara es que los lenguajes dinámicos poco a poco van ganando terreno, entre ellos Ruby (que me da de comer actualmente) y Python, al que le echaré un vistazo en cuanto tenga tiempo...

También mirando este índice te enteras de lenguajes como Lua, que está subiendo con fuerza, sobre todo en el mundo de los juegos

Y finalmente como curiosidad han quitado ColdFusion de la lista desde el mes de Mayo, ya que no lo consideran un lenguaje, sino un framework asociado al lenguaje CFML /CFScript y se están planteando lanzar una lista similar basada en frameworks de desarrollo (ASP.NET, ColdFusion, Ruby on Rails, Django, Groovy...). Estaremos atentos a ver que sorpresas nos da esta nueva lista.

martes, 3 de junio de 2008

Problemillas con simple-rss

Me he encontrado con un par de problemillas utilizando simple-rss. El primero es que no se lleva muy bien con los RSS que no están en UTF-8.

La solución es bastante simple y consiste en añadirle una llamada a Iconv en el método initialize

encoding = check_encoding(@source)
@source = Iconv.conv(UTF_8,encoding,@source) if encoding != UTF_8


Donde check_encoding es una copia del método check_encoding de REXML::Encoding. Este método lee el atributo 'encoding' de la cabecera del xml que estamos parseando.

En mi caso no he podido usar directamente una llamada a ese método porque en la versión de ruby que tengo en el servidor (ruby 1.8.6 (2007-03-13 patchlevel 0)) ese método no es correcto

TODO: Actualizar la versión de ruby del servidor...

El otro problema lo he tenido guardando en memcached un objeto SimpleRSS, que devuelve el error:

TypeError: singleton can't be dumped


El error es debido a que memcached hace una llamada a Marshal.dump para serializar, y en simple-rss tenemos esta línea, que extiende la clase Hash

def item.method_missing(name, *args) self[name] end


Haciendo que no sea serializable:

>> item = Hash.new
=> {}
>> Marshal.dump(item)
=> "\004\b{\000"
>> def item.method_missing(name, *args) self[name] end
=> nil
>> Marshal.dump(item)
TypeError: singleton can't be dumped
from (irb):4:in `dump'
from (irb):4
>> item.singleton_methods
=> ["method_missing"]

Así que he tenido que comentarla. El único efecto que tiene es que en lugar de acceder a los elementos usando item.title hay que utilizar item[:title].

viernes, 30 de mayo de 2008

Leer log binario de mysql

Me he encontrado con un error en producción de esos que eres incapaz de reproducir en local, que no deja nada en el log de rails y la única opción posible era revisar el log de base de datos. Investigando un poco he encontrado que mysql guarda un log binario con todas las queries ejecutadas. Y la forma de leer ese log binario es con mysqlbinlog.

En mi caso sabía la hora en la que ocurrió el error, y por fecha el fichero donde estaba el log era mysql-bin.000029, así que ejecutando

/usr/local/mysql/bin/mysqlbinlog --start-datetime="2008-05-28 16:41:00" --stop-datetime="2008-05-28 16:43:00" /var/log/mysql/mysql-bin.000029 >db.sql

He recuperado un fichero de texto con todas las queries ejecutadas y he podido investigar que había pasado...

jueves, 29 de mayo de 2008

Hola

Aquí empiezo mi blog.

Hace un tiempo ni me planteaba que algún día haría un blog, porque tenia un aburridísimo trabajo con nombres rimbombantes como 'project manager' o 'software engineer' en una multinacional que podría ser perfectamente la de dilbert (creo que no hacen falta más comentarios) y no tenía nada interesante que contar.

Hará un año que volví al mundo del desarrollo, trabajando en ASPgems con Ruby on Rails y reencontrándome con algunos de mis antiguos compañeros de Gran Via Internet/qarana. Hoy he mandado un mail a la lista de desarrollo de la empresa y Dani me ha dicho 'Necesitas un blog'. Y tiene razón. El objetivo de este blog va a ser guardar esos trozos de código, utilidades y todo lo que en algún momento uso en el trabajo y pienso 'esto me hará falta en algún otro momento' y que acabo guardando en un fichero de texto en el ordenador y luego nunca más lo encuentro....

Y si además es útil para alguien más aparte de mi, pues mejor que mejor :-)