Thinking on hiring me?

Please read

Fernando Guillén

a Freelance Web Developer

cabecera decorativa

software development as an artistic expression

Archive for the ‘programando’ Category

Lunes, Diciembre 15th, 2008

Yo escribo porque me da la gana…

Yo escribo porque me da la gana, pero publico por dinero.

Nabokov, que no tengo ni idea de quién es pero lo que dice es una gran doctrina que se puede extrapolar a un buen número de profesiones que parten de la vocación y el entusiasmo, como es algunas veces el desarrollo web, dónde como resultado de que nos gusta lo que hacemos parece ser que no deberíamos cobrar por hacerlo.

Lunes, Diciembre 15th, 2008

Ruby on Rails: sin olvidarse de las foreign keys

Esto de Ruby on Rails es tan agradable y ligero que se nos olvidan hasta las buenas costrumbres en la mesa.

¿Cuántas veces te has ocupado de la intregridad referencial desde que trabajas con Rails?, si eres como yo, hasta ahora ninguna. Y es que Rails ofrece un potente sistema de validación que impide que llegue a la base de datos ningún modelo que no cumpla las reglas que has definido.

Pero aún así, es muy peligroso no indicarle a la BD unas mínimas normas de estructura para que no empecemos a tener registros huérfanos o que hagan referencia a otros registros no existentes.

Hoy comenzaba un nuevo proyecto y cuando he acabado las migraciones me he puesto a investigar a ver que encontraba para poder crear claves foráneas en mis tablas, y he encontrado a la gente de RedHill que tienen bastantes plugins interesantes, y entre ellos el RedHillonRails Core, que permite lo que estamos buscando y alguna cosilla más. También tienen el Foreign Key Migrations que extiende del core pero que lo que aporta no me gustaba ya que obliga (permite) indicar los índices directamente donde declaras el campo. Yo prefiero indicar todos los índices en una migración todos juntos.

El plugin es muy sencillo, lo instalas y ya puedes hacer cosas como esta:

add_foreign_key( :payments, :user_id, :users, :id, :name => :fk_payment_user )

Funciona a la perfección.

Miércoles, Septiembre 17th, 2008

Idea: La conversación detrás de la ponencia

El turno de preguntas en una conferencia siempre es un momento de tensión, por una parte están los nervios y la ansiedad que te invade mientras dudas en si levantar la mano o no para exponer esa duda que te está rumiando desde el principio por si resultará interesante y por si sabrás exponerla con claridad frente al todo el auditorio. Por otra parte está la mala suerte del orador y de toda la platea cuando se le da el turno a un voluntario que expone una pregunta (o varias) de dudoso interés y que agota todo el tiempo disponible para preguntas.

Pero analizando este tema me doy cuenta de otra cosa y es el enfoque completamente anticuado, en este vanagloriado mundo web 2.0 en el que vivimos, que se le da a las ponencias de ser un 99% unidireccional.

Están las mesas redondas que intentan solventar este problema y también el turno de preguntas pero las unas acaban convirtiéndose en micro-charlas también unidireccionales y sobre el turno de preguntas ya he expuesto mi opinión.

Mi propuesta intenta ofrecer una bidireccionalidad ponente/público a las charlas y además una moderación mucho más ágil y democrático al turno de preguntas.

Durante una charla siempre es de agradecer tener algún conocido al lado con el que compartir alguna opinión, comentario o chascarrillo del tipo “vaya rollo” o “este tío mola” o “esto me recuerda a..“. Cuánto mejor estaría poder compartir estos pensamientos directamente y en tiempo real tanto con el resto de oyentes como con el ponente mismo.

Las preguntas que vayan surgiendo se añadirían al mismo pull público de la ‘conversación’ y se podrían votar para cuando el ponente lo decida poder moderarlas y elegir las que considere más oportunas contestar.

Lo que propongo es una especie de mini-digg con una vida muy limitada y un contexto muy concreto: la charla. Donde todo el mundo pueda exponer, anónimamente o no, sus comentarios y percepción de cada porción de la charla y además sugerir preguntas que puedan ser revisadas y votadas por el resto de oyentes.

La conversación

No se está inventando nada nuevo, los programas del corazón hace tiempo que tienen implementado un sistema muy parecido donde se puede ver la entrevista del ‘famoso’ de turno y, debajo de él, mensajes de apoyo o crítica enviados en tiempo real por los oyentes.

aqui hay tomate

Este sistema tiene serios problemas de moderación, generador de ruido y caldo de cultivo para trolls pero la diversión está asegurada si detrás del ponente podemos ver en una pantalla gigante cual es el estado de ánimo y la opinión de el resto de oyentes mientras al mismo tiempo atendemos respetuosamente a la oratoria.

Puede que un ponente no esté de acuerdo con que en su charla se implante este sistema, pero si de verdad tiene interés en su público debería agradecer conocer en todo momento la temperatura de la platea, al más puro estilo DJ.

El sistema de preguntas y de comentarios se podría separar y usar uno u otro pero creo que los dos son complementarios y todo debería pertenecer a la misma ‘conversación‘.

Si no hay posibilidad de ofrecer la ‘conversación‘ en una pantalla gigante tampoco importa, en las charlas suele haber accesos wifi así que podríamos estar con nuestros portátiles atendiendo a la ‘conversación‘ a la vez que escuchamos al ponente. Lo que quiero decir es que no hay necesidad de que este sistema sea ofrecido oficialmente por la organización del evento, siempre podrá participar el que quiera de una manera extraoficial.

El histórico de comentario y preguntas podría enriquecer la documentación de la ponencia para consulta en el futuro.

El modelo de negocio no está para nada claro, pero a quién le importa cuando una idea es útil y divertida.

En esta idea me voy a poner a trabajar en cuanto tenga un momento, si algún grafista de los presentes quiere participar con la estética será bienvenido.

¿Qué te parece? ¿Conoces algo parecido? ¿Tienes alguna sugerencia o crítica?

Sábado, Agosto 30th, 2008

Ruby, sanitizando tus títulos en 2 líneas.

Ayer me acosté super contento, había conseguido escribir una función para sanitizar strings, o como dicen por ahí: crear un SLUG (que todavía no he encontrado la definición exacta).

Si no sabes de que hablo se trata de convertir un “Hola mundo!, qué tal?” en un “hola-mundo-que-tal” para las URLS bonitas y todo eso.

No había sido fácil pues Ruby se lleva mal con los caracteres no-ASCII y el castellano tiene muchos, había que hacer un pequeño malabarismo con la gema Unicode.

Al final mi función se veía así:

require 'unicode'
def to_slug( sentence, length = 64 )
  return if sentence.blank?
 
  wrong = ['á','é','í','ó','ú','ä','ë','ï','ö','ü','à','è','ì','ò','ù','ñ','ç','º','ª','_']
  right = ['a','e','i','o','u','a','e','i','o','u','a','e','i','o','u','n','s','o','a','-']
 
  sentence = sentence[0..length-1]
  sentence = Unicode.downcase( sentence )
 
  for i in 0..wrong.size-1
    sentence.gsub!( wrong[i], right[i] )
  end
 
  sentence.gsub!( /[^a-z0-9-]/, '-' ) # not letters of numbers
  sentence.gsub!( /-{2,}/, '-' )      # 2 or more '-' together becoming 1 '-'
  sentence.gsub!( /^-|-$/, '' ) unless sentence.size == 1 # '-' at begging or at end
  sentence
end

Estaba super orgulloso hasta que me despierto por la mañana y cambiando la pregunta a Google me encuentro con un… ‘inombrable’ que me hace esto:

require 'unicode'
def to_slug
  str = Unicode.normalize_KD(self).gsub(/[^\x00-\x7F]/n,'')
  str = str.gsub(/\W+/, '-').gsub(/^-+/,'').gsub(/-+$/,'').downcase
end

Exactamente (casi) lo que yo tenía pero en 2 líneas.

¡Así es Ruby!

Al final he hecho algún cambio y cogido lo bueno de uno y de otro y le he añadido soporte para STOPWORDS:

STOPWORDS = [
  'de','a','que','no','tiene','en','para',
  'por','le','la','lo','las','los','el',
  'una','un'
]
 
def to_slug( length = 64, drop_stopwords = false )
  return "" if self.length == 0
 
  str = Unicode.normalize_KD(self).gsub(/[^\x00-\x7F]/n,'').downcase
 
  # stopwords
  if drop_stopwords
    STOPWORDS.each do |stopword|
      str.gsub!( /\s#{stopword}\s|^#{stopword}\s/, ' ' )
    end
  end
 
  str = str.gsub(/[^A-Za-z0-9]/, '-').gsub(/^-+/,'').gsub(/-+$/,'').downcase
  str = str[0..length-1]
end

Por su puesto que se recomienda completar la lista de STOPWORDS con las que quieras.

Jueves, Agosto 28th, 2008

Ruby, el ‘print’ necesita que hagas un flush del $stdout

No conseguía hacer un indicador progresivo de un proceso, o lo que es lo mismo: el típtico ‘punto… punto… punto…‘ para indicar que el proceso sigue en marcha.

Si utilizada puts o p se me generaba un salto de carro al final de cada ‘punto‘. Y si utilizaba ‘print‘ simplemente no salía nada hasta que cancelaba el proceso… o terminaba.

Estaba claro que ‘print‘ estaba insertando los ‘puntos‘ en un bufer y había que ‘flusearlo‘.

La solución:

print '.'; $stdout.flush

Mira aquí y aquí.

Jueves, Agosto 28th, 2008

Ruby on Rails, no se me está inicializando el estado de las tablas al ejecutar un test

Estaba yo haciendo unos testecitos y veo que no se me estaban borrando los datos de la base de datos al ejecutar cada test lo cual me estaba generando unos poltergeist horribles.

Un comportamiento de Rails en el que confiaba como era este: “La base de datos vuelve al estado original en cada test“, me estaba traicionando.

Bueno el problema era que como mis tests no usaban fixturas no las estaba cargando y resulta que es la carga de las fixturas lo que borra la bd y no la propia suite de tests.

Solución:

fixtures :mimodelos

Aunque no use las fixturas es lo que he tenido que hacer para que la tabla se borrase con cada test.

¡Qué cosas!

Miércoles, Agosto 27th, 2008

Ruby, extraer la parte html del body de un email con parte html y part texto plano

Normalmente cuando se escribe un email en formato enriquecido el propio cliente de email se encarga de generar dos partes dentro del email, una con el texto en formato html (enriquecido) y otra en formato texto plano por si el cliente de correo con el que finalmente el destinatario lo abra así lo quiere.

Bien, para una aplicación en la que estoy trabajando se requiere que dado un email con varias partes (html y texto plano) pueda extraer sólo la parte html.

No he encontrado nada en la clase TMail::Mail de Ruby que me ofrezca esta funcionalidad.

No parece difícil en un principio, pero en realidad si profundizas un poco si que se vuelve lioso. Si cogemos el body del Mail lo tenemos todo junto y no sabemos donde cortar, ni siquiera si es requerido cortar. Si empezamos a recorrer las partes del Mail podemos empezar a rastrear los content_type de cada una pero esto puede resultar engañoso pues la parte con el body en formato enriquecido tiene el mismo content_type que un fichero html adjunto en el email.

Al final lo que me ha quedado es este método:

def body_html
  result = nil
  if multipart?
    parts.each do |part|
      if part.multipart?
        part.parts.each do |part2|
          result = part2.unquoted_body if part2.content_type =~ /html/i
        end
      elsif !attachment?(part)
        result = part.unquoted_body if part.content_type =~ /html/i
      end
    end
  else
    result = unquoted_body if content_type =~ /html/i
  end
  result
end

Es un método que extiende la clase TMail::Mail para ofrecer el método .body_html que devuelve la parte html o nil si no hay ninguna parte de texto enriquecido.

Puede parecer liosa pero en realidad está inspirada en el método .body de la propia clase TMail::Mail.

Podéis descargaros el parchecito de TMail::Mail y hacer un require del mismo, o también podéis mirar directamente el repositorio donde lo he subido junto con unos pocos tests.

Cualquier comentario es bienvenido.

Miércoles, Agosto 27th, 2008

Ruby, uso de caracteres no ASCII en la consola IRB del Mac OS X

Mis versiones:

  • Mac OS X 10.5.4
  • Ruby 1.8.6 patchlevel 114
  • Rails 2.1.0

Tengo problemas para introducir caracteres no ASCII ( caracteres especiales, acentos y ñs ) en la consola IRB de Ruby así como también en la consola script/console de Rails.

Gracias a las lista ror-es lo he podido solucionar.

Resumo aquí a mi manera el post con la solución:

Instalamos la versión universal de la librería readline mediante MacPorts:

$ sudo port install readline +universal

Si te dá error de:

-bash: port: command not found

Asegúrate que tienes instalados los MacPorts y que tienes esto en tu .bash_profile:

export PATH=$PATH:/opt/local/bin
export MANPATH=$MANPATH:/opt/local/share/man
export INFOPATH=$INFOPATH:/opt/local/share/info

Instalamos la extesión Ruby para readline. Vigila la versión que te bajas, debe coincidir con tu versión de Ruby exactamente:

$ ruby --version
ruby 1.8.6 (2008-03-03 patchlevel 114) [universal-darwin9.0]

v1_8_6_114

$ cd /tmp
$ svn co http://svn.ruby-lang.org/repos/ruby/tags/v1_8_6_114/ext/readline/ readline

Aplicamos un parchecito:

$ curl http://pastie.textmate.org/pastes/168767/download | patch readline/extconf.rb

Y compilamos, necesitarás tener instaladas las OS X developer tools:

$ cd readline
$ ruby extconf.rb
$ make
$ sudo make install

Si el script/console te sigue dando problemas asegúrate de tener bien la $KCODE:

$ script/console
Loading development environment (Rails 2.1.0)
>> puts $KCODE
UTF8
=> nil

Gracias de nuevo a Daniel Rodriguez Troitiño por la solución.

Jueves, Agosto 14th, 2008

Seudo soporte PNGs transparentes en IE6

Disclaimer: soy desarrollador web pero del lado oscuro: el de la parte servidor y, aunque me toca tocar de todo, el CSS, y el diseño web en general, no son mi especialidad. Así que si ves alguna página de algún maquetador especializado en la que se contradice algo de lo aquí digo hazle caso a él.

La pesadilla del maquetador web tiene nombre y éste es Internet Explorer 6. El Navegador más uebón de todos los navegadores. Y pese a que tiene más de 7 años, que está intentando ganar el récord a software con más agujeros de seguridad y que la versión 7 hace 2 años que está disponible, aún sigue siendo el navegador más usado… después de FireFox ;..)

Una de las mayores cerraduras de esfinter que nos encontramos al probar nuestra flamante web en un IE6 es que éste no soporta PNGs transparentes. :O

Bueno, sí que los muestra, pero en vez de la resultona transparencia aparece un desagradable tonillo gris con transparencia nula.

Existen muchos sitios donde te explican como hacer para que el IE6 soporte los PNGs transparentes.. pero la cruda realidad es que no hay manera, olvídate. Cambia todos los PNGs transparentes por PNGs no transparentes o JPGs o incluso GIFFs, pero no te metas en el lío de intentar conseguir que el IE6 te lea bien los PNGs transparentes..

Lo puedes hacer sólo para el IE6 con el hack de los comentarios condicionales para IE y cargando CSSs específicos para este navegador.

Si después de lo dicho te empecinas en hacerlo, allá tú, y aquí tienes la manera de acercarte lo más posible a conseguirlo.

Existen 2 hacks diferentes que tenemos que usar dependiendo de si el PNG transparente está incluido en la página mediante un <img> o mediante un background-image.

PNG transparente metido en una <img>

Éste es el más fácil y el soporte es casi total. A mi no me ha dado ningún problema.

Es muy fácil porque se utiliza un javascript que han hecho una gente muy simpática llamado pngfix.js.

Es una mala bestia de script en 30 líneas que selecciona todas los elementos <img> que contienen un PNG y los sustituye por elementos <span> con una imagen de fondo que es la misma PNG. Usa para ello el hack del IE6 para PNG en background-image que vemos a hora a continuación.

Para activarlo no hay más que descargarse el pngfix.js y llamarlo desde el HTML con:

<!--[if lt IE 7]>
  <script defer type="text/javascript" src="pngfix.js"></script>
<![endif]-->

Así sólo se activará si el navegador detectado es una versión anterior al IE7.

PNG transparente como background-image

Éste funciona siempre y cuando no queramos usar el background-repeat y/o el background-position. Si queremos usar estos atributos estamos jodidos y volvemos al punto del principio: olvídate.

Es decir sólo funciona en caso de que la imagen de fondo no queramos que se repita o que se coloque en una posición que no sea la 0,0.

El truco, o hack, fué ofrecido por la propia gente de IE6 que, diéndose cuenta de la carencia de su motor de renderizado, rebuscaron y rebuscaron y dieron con una posible solución: usar un filtro propietario llamado AlphaImageLoader que es capaz de cargar PNGs transparentes vía CSS.

Está claro que no vamos a usar este truco (chapuza) en todos los navegadores así que lo que hacemos en un CSS que sobrescriba los valores normales y los sustituya por los que necesita el IE6.

Así que generamos un CSS que sólo lo lea el IE6 y lo cargamos desde el html tal que así:

<!--[if IE 6]>
  <style type="text/css" media="all">@import "/stylesheets/ie6.css"</style>
<![endif]-->

En este CSS vamos a hacer todos los ajuste que el IE6 necesite. Entre ellos el del AlphaImageLoader.

Cogemos todos los elementos CSS que contengan un background-image y ponemos cosas coma ésta en nuestro ie6.css:

.clase_css{
  background-image: none;
  filter: none !important;
  filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="/imgs/imagen.png");
}
#id_css{
  background-image: none;
  filter: none !important;
  filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="/imgs/imagen.png");
}

Y repito: si requieres que la imagen se repita estás perdido, lo máximo que puedes hacer es que la imagen se estire con:

#id_css{
  background-image: none;
  filter: none !important;
  filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="/imgs/imagen.png", sizingMethod="scale" );
}

O si la imagen es más grande que el elemento que la contiene puedes cortarla:

#id_css{
  background-image: none;
  filter: none !important;
  filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="/imgs/imagen.png", sizingMethod="crop" );
}
Los links dejan de funcionar

O cuando parece que hay luz al final del tunel viene otro tren y nos aplasta.

Con el truco del AlphaImageLoader resulta que determinados links dejan de funcionar. También hay <p> que ya no se pueden seleccionar o formularios que no se dejan rellenar.

Ni el problema ni la solución está muy clara pero el workarround que se propone por ahí es buscar los componentes que fallan y ponerles un

position: relative

Con esto se suele arreglar bastantes cosas:

a {
  position: relative;
}

Para las demás deberá ir haciendo pruebas.

Los elementos no se estiran para contener su elementos hijos

Otro tren que pasa es que puede que los divs no crezcan hasta soportar todos los elementos que contienen, en caso de que ocurra esto hay que jugar con:

height: 100%;

y

height: auto !important;

Actualizado:

Parece que hay un script que intenta solucionar todos estos problemas el solito, se llama SuperSleight. Lo he probado pero ya tenía todo el tinglado montado y me funcionaba un poco mejor.

PD: hazte de la campaña SaveTheDevelopers o a la de EndIE6 y activa el script que ofrecen en tus páginas para incitar a que la gente que sigue usando IE6 se actualice .. porfavor¡¡

Lunes, Agosto 11th, 2008

Ruby on Rails: definir el tamaño de un índice MySQL en las migraciones

Revisando el log de tus queries te puedes encontrar que si estás buscando registros por un campo VARCHAR te salte un warning de “long key length“.

Esto es porque el índice que has puesto en este campo VARCHAR es muy grande, aunque MySQL soporta índices de hasta 1000 bytes si son muy grandes pueden ralentizar las búsquedas.

La solución está en limitar el tamaño del índice a un número bastante pequeño.

En MySQL esto se indica así:

ALTER TABLE mi_tabla ADD INDEX ( campo_del_indice(4) )

ó también:

CREATE  INDEX `nombre_del_indice` ON mi_tabla (`campo_del_indice`(4))

El problema es que si estamos usando ‘migrations‘ de Rails y queremos ser puristas nos encontramos con que la sintaxis estándar de las migraciones de Rails no soporta el campo ‘longitud’.

Como bien dice Jaime Iniesta existe un parche que da soporte a las migraciones para admitir el campo ‘length’.

Yo sin embargo he preferido no tocar el código de mis gemas y meter este fichero (rails_migraions_hack.rb) en el ‘/lib‘ de mi aplicación:

module ActiveRecord
  module ConnectionAdapters # :nodoc:
    module SchemaStatements
      def add_index(table_name, column_name, options = {})
        column_names = Array(column_name)
        index_name   = index_name(table_name, :column => column_names)
 
        if Hash === options # legacy support, since this param was a string
          index_type = options[:unique] ? "UNIQUE" : ""
          index_name = options[:name] || index_name
          index_length = options[:length] ? "(#{options[:length]})" : nil # only mysql
        else
          index_type = options
        end
        quoted_column_names = column_names.map { |e| quote_column_name(e) }.join(", ")
        # execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{quoted_column_names})"
        sql = "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{table_name} (#{quoted_column_names}#{index_length})"
        p "SQL: #{sql}"
        execute sql
      end
    end
  end
end

Y cargarlo con un:

require 'rails_migrations_hack'

En mi environment.rb.

Funciona perfectamente y vemos como saca por pantalla las querys de las creaciones de índices con cosas como esta:

"SQL: CREATE  INDEX `idx_forums_forums_nicetitle` ON forums_forums (`nicetitle`(4))"

Es interesante consultar toda la discusión en la lista de ror-es.

Actualizado (2008-08-13):

Al parecer no estan cubiertas todas las posibilidades.

En el caso de estar creando un índice compuesto el atributo length se lo asigna al último campo y esto puede no ser lo adecuado:

add_index :tabla, [:campo1, :campo2], :name => 'idx_tabla1', :length => '10'

lo que intenta es hacer:

CREATE  INDEX `idx_tabla1` ON tabla (`campo1`, `campo2`(10))

Si el campo varchar que queremos limitar es el primero la hemos cagado..

Habría que intentar modificar la sintaxis del length para que coja algo como esto:

add_index :tabla, [:campo1, :campo2], :name => 'idx_tabla1', :length => { :campo1 => '10' }

a Freelance Web Developer is proudly powered by WordPress
Entries (RSS) and Comments (RSS).

Creative Commons License
Fernando Guillen's blog by Fernando Guillen is licensed under a Creative Commons Attribution-NoDerivs 3.0 Unported License.