Thinking on hiring me?

Please read

Fernando Guillén

a Freelance Web Developer

cabecera decorativa

software development as an artistic expression

Agosto 11th, 2008

Ruby, script generador de script javascript para precarga de imágenes en tu html

El truco más sencillo para precargar las imágenes de tu web y así no ver el horrible efecto de mouse-over hueco es hacer un script javascript como este:

img1 = new Image();
img1.src="imagen.jpg";

Esto invocará la imagen y el navegador, sino es muy tonto, la dejará en la caché para cuando verdaderamente la necesite.

Bueno, pues es lo que quiero hacer para una web que tiene un montón de background-image definidos en su css.

Para ello me he hecho un script en ruby que me busca todas las imágenes en todos los css y me genera un script de javascript con todo el rollito de los preload.

class Preloator
  def self.generate_script( css_directory )
    # take all .css on directory
    css_file_names = []
    Dir.foreach( css_directory ) do |file|
      if file =~ /.*\.css$/
        css_file_names << css_directory + '/' + file
      end
    end
 
    # take all the images by looking at 'url'
    image_names = []
    css_file_names.each do |file_name|
      self.grep( 'url', file_name ).each do |line|
        image_names << line.scan( /.*\((.*)\)/ ).flatten.first
      end
    end
 
    # generate the script
    script = "<script>\n"
    image_names.each_with_index do |image_name, index|
      script += "    img#{index} = new Image();\n"
      script += "    img#{index}.src = \"#{image_name}\";\n"
    end
    script += "</script>"
  end
 
  def self.grep( pattern, filename )
    matches = []
    regexp = Regexp.new( pattern )
    File.open(filename) do |file|
      file.each do |line|
        matches << "#{filename} #{file.lineno.to_s}: #{line}" if regexp.match(line)
      end
    end
    matches
  end
end
 
puts Preloator.generate_script( ARGV[0] )

Esto genera una salida como esta:

$ ruby etc/preloator.rb public/stylesheets/
<script>
img0 = new Image();
img0.src="/imgs/fondo.jpg";
img1 = new Image();
img1.src="/imgs/fondo_carpeta.png";
img2 = new Image();
img2.src="/imgs/blog_sombra_abajo.png";
img3 = new Image();
</script>;

Seguramente quieras mejorarlo un poquito: meterlo en un .js o invocarlo cuando el document.load.

El script ahorra trabajo si, como en mi caso, tienes más de 20 imágenes en el css.. y escribir el javascript a mano sería un coñazo.

Agosto 8th, 2008

Pesadilla futurista.

Hoy he soñado que todo lo que hacemos se consideraba consumo de recursos de algún extraño servidor.

Es decir, que todas nuestras acciones y movimientos tenían un coste proporcional a la actividad. Mover los brazos airosamente o hacer esfuerzos mentales era más costoso que permanecer sentado con la mente en blanco.

Me he despertado con una gran ansiedad, me daba cuenta que al madrugar estaba ya consumiendo parte de mi limitado ‘ancho de banda‘.

¿Qué ocurriría si me quedaba sin espacio?

Agosto 6th, 2008

Cecilia Molano lleva a Alberto Bueno a la presentación de ‘escrito a lápiz’ en La Ciudad Invisible de Radio 3

Cecilia en Radio3

Podéis escuchar aquí la entrevista que hicieron la gente de La Ciudad Invisible de Radio 3 a Cecilia Molano, la entusiasta directora de escrito a lápiz,  el pasado 1 de Agosto.

Cecilia tuvo la osadía de llevar como compañía a uno de los alter egos de los que se compone el humilde servidor que suscribe este blog: alberto bueno, uno de los autores del primer libro publicado por esta editorial: ‘doce cuentos‘, porque no sólo de programar vive el espíritu.

Si después de escuchar la entrevista te quedas con las ganas de saber que es eso del bautizador de teléfonos… sigue el link.

Agosto 4th, 2008

Ruby on Rails: expirar la caché desde un script ó como activar los Sweepers como Observers

No sé si es que soy yo el liante o es que me da por hacer las cosas raras.

El caso es que me he encontrado con otro dolor de cabeza haciendo algo, en principio, relativamente sencillo.

Se trata de insertar en la BD un modelo desde un script, que más adelante será un rake. Todo iba bien, no es complicado:

m = MiModelo.new( :nombre => 'el nombre' )
m.save

Pero, algo olía mal, pues los listados de este modelo tienen una vista, y esta vista está cacheada, y si insertaba el modelo desde el script la vista no cambiaba, osea: no estaba expirando la caché.

El problema estaba en que todos los engranajes de expiración de cachés estaban en los sweepers y éstos sólo se activan si el código pasa por el application.rb, padre de todos los controladores, concretamente con el método: cache_sweeper, y no encontraba manera de activar los sweeper sin pasar por un controlador.

Estuve dando unas buenas vueltas y preguntando a colegas, resulta que la caché en Rails está altamente integrada con el controlador. Y esto tiene su sentido pues no hay caché sin renderizado y no hay renderizado sin llamada a un controlador. Pero cuando hablamos de expirar cachés no veo tan claro que deba seguir estando integrado con el controlador, pues, como vemos en mi ejemplo del script, pueden existir muchos escenarios donde sea necesaria una expiración de caché sin necesidad de pasar por un controlador.

Googleando por ahí encontré un post que según creo entender también se queja un poco del exceso de integración entre la caché y los controladores.

La primero aproximación que parece ocurrírsenos es la de usar un observer en vez de un sweeper pero el sweeper tiene implícitamente un montón de métodos muy útiles para realizar la tarea de expiración de caché, como son los expire_* y los renderizadores de urls al estilo mi_modelo_url.

Además si empezaba a migrar todos los sweepers a observers empezaba a salirme del camino de como le gusta a Rails que se hagan las cosas.

Pero es que resulta que al parecer a Rails no le gusta que se expiren cachés desde un script y el workarround para conseguirlo no es del todo limpio pero se ha conseguido activar los sweepers desde un script para que se disparen al insertar un nuevo modelo en la BD.

El workarround es obra de Daniel Rodriguez Troitiño y aquí podéis revivir la lucha hasta conseguirlo.

Está funcionando para 2.0.1 y no sé si funciona en otras versiones.

require 'action_controller/test_process'
 
ActiveRecord::Base.observers = [MiSweeper]
ActiveRecord::Base.instantiate_observers
 
MiSweeper.instance.controller = ActionController::Base.new
 
tr = ActionController::TestRequest.new
MiSweeper.instance.controller.request = tr
MiSweeper.instance.controller.instance_eval( '@url = ActionController::UrlRewriter.new(tr, {})' )
 
m = MiModelo.new( :nombre => 'el nombre' )
m.save

Vemos que se activa el sweeper como un observer y luego se inicializa con todo lo necesario para que funcione.

Muchas gracias a Daniel que se lo ha tomado como reto personal ;).

Julio 31st, 2008

Ruby: cliente Pop SSL ó como leer tu cuenta Gmail desde Ruby.

Cómo ya dicen en muchas partes, pero me repito aquí para parecer muy listo y por marcarlo como nota mental, para acceder a tu cuenta Pop en Gmail desde Ruby hay que hacer una ñapa porque la versión actual de Ruby (1.8.6) no admite conexiones Pop bajo SSL.

Si si.. la 1.9 ya tiene el soporte, y dicen por ahí que descargándote el pop.rb de esta versión desde el repositorio y haciéndole un require ya funciona.. pero no, por lo menos a mí no porque me salta alguna dependencia con OpenSSl:

NoMethodError: undefined method `set_params' for #<openssl::ssl::sslcontext:0x210f4d0>;
</openssl::ssl::sslcontext:0x210f4d0>

Y no quería seguir tirando de el hilo.

El workarround que más me ha gustado ha sido el de montar un tunel ssh y hacer la petición desde el Net::POP3 al túnel y que el túnel se la haga llegar al pop de Gmail bajo SSL.

No voy a entrar en detalle porque ya lo hacen en muchos sitios.

Actualizado: acabo de ver un sitio dónde explican como usar la implementación pop.rb de la 1.9 sin el problema que me da a mí.

Los pasos resumidos que este post explica son:

1) Descargarte una implementación de la librería POP3: la versión 13778.

2) Ponerla en tu carpeta lib con este nombre por ejemplo: pop_ssl.rb con esto sobreescribiremos la implementación de esta librería que trae ruby por defecto con una versión mayor.

3) Descargarte este fichero y ponerlo también en lib: pop_gmail_client.rb

class PopGmailClient
  def self.read_account( account_user, account_pass )
    mails = []
    Net::POP3.enable_ssl(OpenSSL::SSL::VERIFY_NONE)
    Net::POP3.start( 'pop.gmail.com', 995, account_user, account_pass ) do |pop|
      pop.each_mail do |m|
        mails << m.pop
      end
    end
    mails
  end
end

4) Requerirlos desde el environment.rb al final del todo con:

require 'pop_ssl'
require 'pop_gmail_client'

Y ya podemos usarlo como queramos:

$ script/console
Loading development environment (Rails 2.0.2)
>> PopGmailClient.read_account( "fguillen.testing@gmail.com", "xxx" )

O desde ruby directamente:

$ irb
>> require 'lib/pop_ssl.rb'
=> true
>> require 'lib/pop_client_gmail.rb'
=> true
>> PopGmailClient.read_account( "fguillen.testing@gmail.com", "xxx" )
Julio 30th, 2008

Ruby on Rails: before_filter y caches_action el orden sí importa.

Estuve un día volviéndome loco con uno de estos tantos poltergeist que todo lenguaje o framework oculta, en este caso RoR.

Aprovecho para señalar mi opinión de que es imposible conocer un lenguaje/framework en unos pocos meses, por muy listo que seas y por muchos libros que seas capaz de leer en ese periodo. El verdadero conocimiento se esconde después de enfrentarte a un gran número de problemas/errores/bugs/y poltergeist que el lenguaje/framework en cuestión esconde, conocerle como si se tratara de conocer el carácter de una persona con la que convives hasta que entiendes como tratarle y entenderle. Es a lo que aveces nos referimos como la intuición.

Siguiendo con el poltergeist que nos ocupa: se trataba de que al activar la caché de acción en determinados controllers no se me estaban ejecutando los before_filters (aunque curiosamente si se ejecutaban los before_filters del padre ApplicationController  no de mis propios Controllers).

Esto no era muy normal y estaba claro de que algo estaba haciendo mal, pues precisamente una de las peculiaridades de la caché de acción es que siempre ejecuta los filtros, a diferencia de la caché de página que no ejectua nada.

Bueno, después de seguir el sabio consejo: si te estás espesando déjalo para otro día, hoy ya he encontrado lo que ocurría y no es ni más ni menos que el orden en el que se declaran los before_filter y los caches_action.

Yo lo tenía así:

class MiController < ApplicationController
  caches_action :show
  before_filter :ejecutar_antes, :only => [ :show ]
end

Y se solucionó cambiándolo a:

class MiController < ApplicationController
  before_filter :ejecutar_antes, :only => [ :show ]
  caches_action :show
end

Es muy fácil de probar:

class MiController < ApplicationController
  before_filter :X1
  caches_action :show
  before_filter :X2
 
  private
    def X1
      p "XXXXXXXXXXXX1"
    end
 
    def X2
      p "XXXXXXXXXXXX2"
    end
end

Si ejecutas :show una vez que ya está cacheada verás como sólo se ejecuta el primer filtro ‘X1′ y no el ‘X2′.

Si conoces algún sitio dónde se indique el órden en el que hay que hacer estas declaración pega el link en un comentario.

Julio 28th, 2008

De casa a la oficina sin camiseta ó la dura vida del freelance en verano

Muchas beneficiosas casualidades se han tenido que dar para que este verano que estoy viviendo se haya hecho posible:

  • Tener un trabajo que te permite organizarte el día atendiendo cada tarea según el clima sea más propicio.
  • Que tu chica tenga amigos que vivan en un chalet estupendo en la sierra al lado de una estación de cercanías.
  • Que te hayan rechazado 2 presupuestos de 2 sendos proyectos que te habrían tenido esclavizado todo el verano.
  • Haber rechazado (aplazado) varias propuestas de trabajo que hubieran requerido recursos que he decidido reservar para proyectos personales.

Las barbas son también una pequeña consecuencia de todo esto, pero quién más tiene que lidiar con ellas está encantada.

El vídeo intenta traslucir en modo humor como entiendo yo que hay que afrontar la vida profesional: sin diferenciarla de la de ocio: totalmente integrada con tus necesidades, apetencias y ritmos. No sólo para ayudar en la inacabable búsqueda de la felicidad sino también para conseguir dar lo mejor de ti a nivel profesional.

Evidentemente hay días verdaderamente duros que se alargan toda la noche, y luego están los otros que te permites la licencia de trabajar desde una hamaca alternando un email con un chapuzón, y no depende de que sean las cuatro de la tarde o de que hoy sea martes sino de las necesidades reales del proyecto que tengas entre manos.

Es cierto, era martes, y eran las cuatro de la tarde, después de comer, día laboral, y yo estaba de relax total, pero en el vídeo no aparece que el sábado y el domingo me los pasé haciendo una replicación de una aplicación en producción pues era el mejor momento para hacerla: fuera del horario de trabajo de los usuarios.

Y lo bueno de todo es que disfruté los 2 momentos: en uno por satisfacer la necesidad de hacer las cosas bien y en el otro por hacer lo que te pide el cuerpo.

Julio 23rd, 2008

Texto vertical con javascript

Ni CSS3 ni SVG ni leches.

<script>document.write( "el texto".replace( /(.)/g, "$1<br />" ) ); </script>

Ya sé que no queda vertical y que es un seudo horizo-vertical.. pero por lo menos funciona. ;)

Julio 19th, 2008

2 horas programando para 4 líneas

Es lo que tiene ruby.

Llevo un par de horas intentando sacar un Float en formato ‘d.ddd.ddd,dd’. Hay muchas cosas en internet para conseguirlo, pero no todas funcionaban bien, y otras funcionaban demasiado bien, con un montón de opciones.

El caso es que hay un helper del ActionView que tiene la función number_to_currency pero es un cabroncete de helper y no podía acceder desde el modelo. También teníamos la gema Currency pero era un pedazo monstruo para la tontada que yo quería.

Al final la gema Scruffy me ha dado la pista y esto es lo que tengo:

class Float
  def en_euros
    parts = sprintf("%01.#{2}f", self).split('.')
    parts[0].to_s.gsub(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1.") + "," + parts[1].to_s
  end
end

Es un parchecito del Float para poder hacer esto:

>> 1234.566.en_euros
=> "1.234,57"
Julio 17th, 2008

Porque confío en el formato iPhone

Disclaimer: Aquí otro post sobre el iPhone de alguien que nunca ha tenido uno y que habla por percepción subjetiva

No quiero hablar del triunfo inmediato de ventas (o por lo menos de deseo de compra) del nuevo juguetito de Apple, sino de por qué a mí me parece que la experiencia de usuario a medio plazo será muy satisfactoria.

Hay multitud de alternativas móviles al iPhone con todas o la mayoría de las funciones que éste ofrece, pero la gran diferencia es que el iPhone es único, y no me refiero a su exclusividad (sin negarla) me refiero a que es UNO y no hay más.

Todo iPhone es igual a otro (más allá de colores y espacio de almacenamiento), misma pantalla, misma interface de usuario, misma capacidad de rendimiento, mismos periféricos integrados, …

Esto puede interpretarse como una limitación comercial, y puede que así sea, pero las ventajas a medio plazo son arrasadoras.

Nunca ha ocurrido esto antes con un dispositivo móvil, nunca antes un compañía ha tenido la suficiente fuerza de asimilar el riesgo competitivo de poner todos los huevos en un sólo cesto, han preferido la diversidad, un extenso catálogo dónde cada cliente encuentre el móvil que le encaje.

Ahora Apple cambia las tornas y saca un solo producto, porque sólo ella tiene la suficiente fuerza hipnotizadora para convencer al cliente de encajar en el móvil y no al revés.

Entonces tenemos el iPhone, y nada más, no hay iPhone XP45, ni iPhone 320, ni iPhone++ (aunque sí hay dos modelos de iPhone y se empieza a romper mi teoría, pero en cualquier caso el anterior ha desaparecido de facto de la faz del mercado y es como si ya nunca hubiera existido).

Desde el punto de vista del mundo de los desarrolladores es como si se les contara un cuento de fantasía: desarrollarás aplicaciones para un solo dispositivo con unas características únicas y conocidas.

Desde el punto de vista del usuario identificará rápidamente cualquier aplicación, periférico o información que tenga que ver con su móvil, con sólo ver la etiqueta iPhone. Una vez encontrado lo que necesita sabrá que encajará perfectamente con su dispositivo, que no habrá ningún tipo de incompatibilidad y que se usará siguiendo unas pautas comunes con el resto de aplicaciones.

El gran fracaso, a mi entender, del desarrollo de tecnología móvil, ya sea aplicaciones o experiencia web, ha sido precisamente la diversidad. Yo nunca me he conseguido descargar una aplicación de Internet que funcione correctamente en mi móvil, sin contar además todas las vueltas que he tenido que dar para, no sólo encontrar la aplicación, sino luego encontrar la versión que se suponía compatible con mi dispositivo.

La gran frustración de los desarrolladores los ha llevado en la mayoría de los casos a desarrollar para el mínimo común denominador generando así productos que nunca explotan (respetan) las características de cada dispositivo en concreto.

Sabiamente dice Yoshida (presidente de los estudios de Sony):

Lo ideal son los juegos adaptados a los controles de la máquina.

Me recuerda esto a mi experiencia MSX cuando era pequeño, y veía que muchos de los videojuegos que tenía no utilizaban la gran capacidad gráfica y de sonido que esta máquina tenía. Los últimos títulos se veía claramente como los habían desarrollado para Spectrum y los habían clonado cutremente a MSX.

Es por todo esto que no confío tanto en el éxito del formato Android. Hace poco estuve en una charla en la que exponían las bondades del SO para móbiles con el que Google quiere revolucionar este mundo, y tuve una pequeña discrepancia con el ponente a propósito de esto mismo: ¿la diversidad de dispositivos y de características de los mismos es un pro o un contra?.

No sólo me parece un contra para el usuario, también para los desarrolladores, para el fabricante de dispositivos y para el mismo desarrollador del SO: cuánto más fácil lo tendrían si tuvieran bien claras las especificaciones de la plataforma sobre la que el SO va a correr.

Sólo creo que Android podrá revolucionar algo si dentro de sus especificaciones incluye una serie muy concreta de especificaciones de hardware.

Ahora bien, espero que Telefónica fracase con sus abusivas condiciones de venta, y si Apple está de acuerdo pues que se vayan a la mierda juntas.

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.