Fernando Guillén

un Desarrollador Web Freelance

cabecera decorativa

nada por aquí y nada por allá ó el desarrollo de software como expresión artística.

Archive for the ‘how to’ Category

Thursday, September 18th, 2008

Port Forwarding: accediendo a las máquinas de una intranet desde una sóla máquina pública.

Las redes de los clientes son celosas y normalmente sólo se puede acceder a las máquinas de su red desde dentro de la red misma. Lo que viene a llamarse una intranet.

Pero muchas de mis tareas de mantenimiento requieren acceso a varias máquinas de una intranet y desplazarme al cliente no lo veo una opción factible ni acorde con nuestros tiempos.

Una de las soluciones que más me ha gustado es pedirle al cliente un sólo acceso, una sola máquina con IP pública.

Para dar seguridad se puede configurar para que sólo se pueda acceder desde la IP estática de tu oficina, y, para ofrecer flexibilidad, que también se pueda acceder desde la IP estática de tu servidor en un datacenter. Pero bueno todo este párrafo te lo puedes saltar si no hay mucho inconveniente con la seguridad.

La máquina con la IP pública mola que sea linux, más que nada porque si no lo es ya no hace falta que sigas leyendo pues el resto del artículo sobreentiende esta premisa.

Lo que vamos a hacer es lo que se ve en la ilustración.

port forwarding

Aquí me ves a mí con más pelo y sin barba deseando acceder a todos los servicios de las máquinas de la intranet 10.10.10.* y sin embargo sólo pudiendo acceder a la máquina con la IP pública 205.205.205.205.

Lo que necesito tiene un nombre y este es Port Forwarding, y la implementación de esto más sencilla que he visto se hace mediante IP Tables.

Va a ser más rápido escribir los comandos que hay que ejecutar para que esto funcione que toda la charla previa que te he contado.

Mi máquina con acceso público tiene la distribución de linux Gentoo así que los ejemplos son para esta distro pero no deberían variar mucho para otras distros.

Instalamos iptables (si no está ya)

# emerge iptables

Le decimos al kernel que permita ip-forwarding

# echo 1 > /proc/sys/net/ipv4/ip_forward

Flasheamos toda la configuración de iptables que haya por defecto

# iptables -F
# iptables -t nat -F

Permitimos el forward desde iptables (eth0 es la interface pública)

# iptables -A FORWARD -i eth0 -j ACCEPT
# iptables -A FORWARD -o eth0 -j ACCEPT

Esto hace que las ips se enmascaren para no liar a los routes y saltarse posibles filtros de seguridad por IP

# iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

Y aquí por fín las reglas del Port Forwarding

# iptables -t nat -A PREROUTING -p tcp -i eth0 --dport 213306 -j DNAT --to-destination 10.10.10.21:3306
# iptables -t nat -A PREROUTING -p tcp -i eth0 --dport 2180 -j DNAT --to-destination 10.10.10.21:80
# iptables -t nat -A PREROUTING -p tcp -i eth0 --dport 2280 -j DNAT --to-destination 10.10.10.22:80
# iptables -t nat -A PREROUTING -p tcp -i eth0 --dport 2221 -j DNAT --to-destination 10.10.10.22:21
# iptables -t nat -A PREROUTING -p tcp -i eth0 --dport 2322 -j DNAT --to-destination 10.10.10.23:22

Guardamos la configuración de iptables actual para que no haya que reescribirla al reiniciar

# /etc/init.d/iptables save

Ponemos el servicio de iptables para que arranque al inicio

# rc-update add iptables default

Creo que esto también hay que hacerlo

# vim /etc/sysctl.conf

Añadir/descomentar las siguientes líneas:

net.ipv4.ip_forward = 1
net.ipv4.conf.default.rp_filter = 1

Y ya debería funcionar. Si me voy a mi máquina en mi oficina y accedo a:

$ mysql -h205.205.205.205 -P 213306

Me encontraré con la mysql de la máquina 10.10.10.21.

Si accedo con un navegador a:

http://205.205.205.205:2280

Me encuentro con el Apache de la máquina 10.10.10.22.

Si no te funciona revisa la linkografía:

Thursday, September 4th, 2008

Ruby, sustituyendo matches de una regex en un String con matches de la misma regex

Madre mía que título bueno me ha quedado :)

Esto es una nota mental y puede que si no sabes de que hablo no te interese y si sabes de que hablo ya lo sepas.

Intento sustituir una parte de un String por otra parte que se encuentra en el mismo String.

Es decir, tengo esto “me gusta el heavy y no me gusta el country” y quiero obtener esto otro “me gusta el country y no me gusta el heavy”.

Se puede hacer así:

>> "me gusta el heavy y no me gusta el country".gsub( /me gusta (.*) y no me gusta el (.*)/, 'me gusta \2 y no me gusta el \1' )
=> "me gusta country y no me gusta el el heavy"

Donde \1 y \2 son las ocurrencias de los (.*).

El ejemplo puede parecer un poco tonto, pero esta utilidad me ha venido muy bien para quedarme con el contenido de una etiqueta html:

>> "<body>contenido</body>".gsub( /.*<body[^>]*>(.*)<\/body>.*/mix, '\1' ).strip
=> "contenido"

Que se puede solucionar de muchas otras maneras pero esta me ha parecido la más sencilla.

Escribo esta nota mental por lo poco intuitivo que es el uso de ‘\1′ como cadena sustitutiva pues se supone que las cadenas entre comillas simples no se interpretan…

Otra cosa es que google siempre me llevaba a soluciones como esta:

>> "me gusta el heavy y no me gusta el country".gsub( /me gusta (.*) y no me gusta el (.*)/, "me gusta #{$2} y no me gusta el #{$1}" )
=> "me gusta country y no me gusta el el heavy"

Y aunque parece que funciona no es así porque los contenidos de $1 y $2 los ha cogido del gsub anterior y no de éste:

>> "me gusta el musical y no me gusta el flamenco".gsub( /me gusta (.*) y no me gusta el (.*)/, "me gusta #{$2} y no me gusta el #{$1}" )
=> "me gusta country y no me gusta el el heavy"
>> "me gusta el musical y no me gusta el flamenco".gsub( /me gusta (.*) y no me gusta el (.*)/, "me gusta #{$2} y no me gusta el #{$1}" )
=> "me gusta flamenco y no me gusta el el musical"
Saturday, August 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.

Thursday, August 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í.

Thursday, August 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!

Wednesday, August 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.

Wednesday, August 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.

Thursday, August 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¡¡

Monday, August 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' }
Monday, August 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.

un Desarrollador Web Freelance is proudly powered by WordPress
Entries (RSS) and Comments (RSS).