soledad penadés
repeat 4[fd 100 rt 90]

Archive for June, 2007

20070627 Superminigallery: a gallery with ruby, rmagick and builder

Imagine you have been in a very nice place for holidays. You took a lot of pictures and want to show them to your family and friends, but you don't feel like using services like flickr or programs like iPhoto. You just want to put them in your own server and give the url to your friends.

What can you do? Well, you could do like me and create a little script to generate an HTML file, with thumbnails and even watermarked images (just in case some creepy individual decides to use your stuff without asking first).

Superminigallery thumbnail

Requirements

This script requires a couple of gems to be installed: RMagick and builder (but if you've done some stuff with Rails you might already have them). RMagick is used for dealing with the images and builder is used for generating the XHTML. This is because I didn't want to write any html by hand, with their less than and greater than signs, attributes, etc.

Using it

  1. Create a folder in your computer. For example: holidays.
  2. Then you copy there the pictures you want to show to the world.
  3. Open a terminal and cd to that directory. E.g.
    cd ~/Desktop/holidays
  4. Execute the script! E.g.
    ruby ~/code/superminigallery.rb
  5. Wait until it finishes

When it finishes you'll find there's an output folder in the holidays folder. That's where the index.html file, as well as all the thumbnails and watermarked images are. Simply upload the contents of this folder to your host and let everybody know about it!

Ok, but show me the code

The first lines act like a configuration area. You can change the output folder name, so that it is called superoutput, gallery, whatever you like (as long as it is a valid path name).

You may change the sizes of the generated pictures; these sizes are defined in the versions variable. Each pair means [width, height]. For example, the thumbnails are 300 pixels wide and 150 pixels high.

output_path = 'output'

versions = {
  'thumbnail' =>  [300,150],
  'big'       =>  [1024,768]
}

You can also configure which EXIF tags need to be retrieved. Since their names are too obscure for non-technical savvy people I decided to create this hash for storing the key (Exif tag) and the nice name to show with the value. So instead of showing DateTimeOriginal, it will simply output Taken.

exif_fields = {
  'Taken'     =>  'DateTimeOriginal',
  'Camera'    =>  'Model',
  'Exposure'  =>  'ExposureTime', 
  'Shutter Speed' =>  'ShutterSpeedValue'
}

There are way more tags you could show, but they can be confusing for normal people and only entertain geeks, so it's better to keep them down to a minimum.

Declare the builder object, and initialize it with the XHTML header.

x = Builder::XmlMarkup.new(:target=>xhtml, :indent=>1)

x.instruct!
x.declare! :DOCTYPE, :html, :PUBLIC, "-//W3C//DTD XHTML 1.0 Strict//EN", "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
x.html( "xmlns" => "http://www.w3.org/1999/xhtml" ) {

Now it would be amazing to have some styling in the page so that it doesn't look so ugly. We can put an style tag inside the head, and use the text! method for adding literal text to the builder object:

x.style( "type"=>"text/css" ) { x.text! "
      body{
        font-family:georgia,serif
      }
     
      h1,h2 {
        margin-top: 0;
      }
      …
      "
}

(the means there's more code but I have reduced it for clarity purposes)

Now, we need to create the output directory. I haven't bothered with outputting error messages if the directory already exists or anything. It will always try to create it:

begin
    FileUtils.mkdir output_path
  rescue
  end

We need to create a Magick::Draw object for watermarking the images, and define its parameters:

draw = Magick::Draw.new
draw.gravity = Magick::CenterGravity
draw.pointsize = 64
draw.font_family = "Helvetica"
draw.font_weight = Magick::BoldWeight
draw.stroke = 'none'
draw.fill = "#ffffff99"

Basically we are saying: use Helvetica bold 64pt, painting it with white (ffffff) and some transparency (99 for alpha channel). If you don't have Helvetica installed in your system, replace it with your favourite font.

(But since 2007 is Helvetica's 50th anniversary, you should do everything possible to use Helvetica!)

Now we open the current directory (where the script was executed) and find all files with jpg and JPG extensions, and sort them. That's because sometimes the images don't get listed in alphabetical order, and us humans like to see things in sequential order. Specially because they usually are numbered incrementally, and older numbers mean older images, so IMG001 should appear before IMG100.

Dir['*.jpg','*.JPG'].sort.each do |f|

Read each file into a Magick::Image object:

img = Magick::Image.read(f).first

And for each version…

versions.each do |k,v|

… create the version filename by appending the version name to the filename, like big_IMG_1234.jpg, and the output filename, by prepending the output path to the version filename:

version_file =  k + '_' + f
output_img_path = File.join(output_path, version_file)

If the version is 'thumbnail', we'll add the image metadata to the builder object. Note how you don't need to open or close tags, but just include things in blocks or parenthesis to get the mark up done.

if(k=='thumbnail')
        x.div('class'=>'picture') {
                x.h2(f)
                x.a('href'=> version_file.sub('thumbnail_', 'big_')) {
                        x.img('src'=>version_file)
                }
                x.dl {
                        x.dt('Dimensions')
                        x.dd(img.columns.to_s + ' x ' + img.rows.to_s)

                        exif_fields.each do |title, field|
                                key = "Exif:#{field}"
                                if img[key]!=nil
                                        x.dt(title)
                                        x.dd(img[key])
                                end
                        end
                }
        }
end

Resizing the image is as simple as

version = img.crop_resized(v[0], v[1])

crop_resized returns another Image object which we store in the version variable.

Now, if we are dealing with the 'big' version, we'll add the watermark that we prepared at the beginning. That is done with

if(k=='big')
        draw.annotate(version, 0, 0, 0, 0, "(c) soledadpenades.com")
end

You can replace my (c) soledadpenades.com with your text, of course!

And for writing the resulting image to disk:

version.write output_img_path

Very very important: do not forget to call the Garbage Collector. For some reason which I still haven't been able to elucidate, the RMagick gem leaks memory furiously. So if you forget to do a

GC.start

as I did with the first version of the script, your computer will mostly hung if you make it generate a lot of thumbnails. If you look at the current processes with top or a similar tool, you'll find a ruby process eating more and more memory with each picture it processes.

And finally, we just need to output the generated XHTML to index.html:

File.open(File.join(output_path, 'index.html'), 'w+') do |file|
  file.puts xhtml
end

Here's the result and here's the source code. With only 120 lines of code (excluding the license text :D), it's way easy to modify to suit your tastes.

Don't tell anyone but…

I must confess I got the inspiration for this from herotyc's jGallery. But he used a bash script and I thought there should be a way of doing the same with ruby :-)

20070626 Software budgets

The web team, composed exclusively of interns, made a request to their project manager:

– We'll need a php editor, the trial license for Ultraedit in this machine has expired and we can't be disconnecting the network cables each time someone starts Dreamweaver so that it doesn't detect we are all running the same license.

– Ok, just download something in emule and try to crack it. Don't know, Zend Studio or something. It's ok, as long as you have space in your computer for installing it… Just make sure you download it in your home computer, since we have a very limited network connectivity and we can't afford to use the office's network for downloading large files. We don't have budget for development tools either.

Some years later, different country:

– I'm afraid I'll need Flash CS3 for doing this…

– Ok, no worries. Just let me know which package you need and I'll buy it.

(10 minutes later)

– You can find the software in this folder. License key is there as well.

* sole is trying to pick her jaw, which got stuck in the floor, with no luck.

20070625 Delicatessen: a wordpress plugin

I just gave the finishing touches to my first public wordpress plugin: Delicatessen. Sounds yummy, doesn't it?

If you're a veeeeeeeery curious person (just like me) you'll love this plug-in. It should satisfy your innermost desires and much more. Well, not that much but at least it will allow you to find who's linking you in del.icio.us, since there's not an easy way of finding that information in del.icio.us itself.

Hope you enjoy it!

20070622 Señales de que tu PHP necesita una refactorización

Dado que a bastante gente le gustó el Signs your php needs refactoring y unas cuantas personas insinuaron la necesidad de una traducción al español, he decidido actuar proactivamente y hacerlo yo misma. Y sin más dilación, sigue el artículo en castellano.

Recientemente, he tenido que trabajar con una aplicación en php que no sólo me ha dado más de un dolor de cabeza, sino que además me ha obligado a hacer uso de toda mi paciencia. Y mientras estaba en ello, pensé Esto es buen material para un artículo, de modo que nadie más haga lo mismo en el futuro, y que nadie más tenga que sufrir lo mismo que he tenido que sufrir yo.

Así que aquí están las señales de que tu PHP necesita una refactorización seria, ya mismo:

Usa variables globales

Esto tiene dos interpretaciones. En primer lugar, tenemos esa forma de programar en la que una serie de variables se pone en el ámbito global, cada función hace un

global $varA, $varB;

etc, y procede entonces a hacer lo que quiera que deba hacer con las variables. Esta es la peor; montones de errores extremadamente difíciles de trazar plagan los programas escritos con esta técnica, más que nada porque las variables globalizadas acaban sobreescribiendo variables que no deberían haber sobreescrito. Es también muy complicado poder hacer un seguimiento de quién está modificando qué, porque no se usan llamadas a funciones para acceder a las variables, sólo declaraciones global. No hay visibilidad en absoluto.

Y luego tenemos la segunda interpretación, en la que las variables que ya son globales de por sí ($_REQUEST, $_POST, $_GET) son usadas (y se abusa de ellas) a lo largo y ancho de toda la aplicación. Te puedes encontrar, por ejemplo, modelos de datos accediendo directamente los arrays $_POST o $_GET, lo cual no sólo introduce complejidad extra en su lógica (por ejemplo: ¿qué ocurre si decidimos enviar los datos con GET en lugar de con POST?) pero además también complican el hacer pruebas unitarias a los diferentes componentes de la aplicación. No puedes simplemente enviar un array de datos a un modelo: ha de ser enviado con POST o GET.

Insisto, esta forma de hacer las cosas proporciona mala visibilidad, ya que no se puede realmente distinguir quién está cambiando qué.

La fiesta de los corchetes

Cada vez que me encuentro cosas como ésta:

$variable['field']['subfield']['subsubfield1'] = 'value1';
$variable['field']['subfield']['subsubfield2'] = 'value2';
$variable['field']['subfield']['subsubfield3'] = 'value3';
$variable['field']['subfield']['subsubfield4'] = 'value4';

… sólo tengo un adjetivo en mente: asqueroso.

Es más fácil y rápido hacer algo tal que esto:

$variable['field']['subfield'] =
  array('subfield1'=>'value1', 'subfield2'=>'value2', 'subfield3'=>'value3', 'subfield4'=>'value4');

Puedes indentarlo con un par de valores por línea, al gusto. La clave está en que si en algún momento necesitas cambiar 'field' o 'subfield', no necesitas hacerlo cuatro veces (incluso si existen cosas como Buscar y Reemplazar). ¡He encontrado casos en los que la Fiesta de los Corchetes ha contaminado veinte e incluso más líneas!

Otro ejemplo de abuso de corchetes se da cuando se recuperan resultados de una base de datos en forma de arrays y son accedidos a saco. Entonces una acaba encontrándose con código como el siguiente para acceder al primer elemento:

$prop1 = $res['results'][0]['property1']
$prop2 = $res['results'][0]['property2']

Más fácil y corto:

list($prop1, $prop2) = array_shift($res['results']);

¿Me podría alguien explicar por qué esa obsesión de la gente con las Fiestas de Corchetes?

Todo es un array

Los objetos se crearon por alguna razón. El hecho de que crear arrays es muy sencillo en php y no tienes que reservar memoria primero y todo eso, al contrario que en otros lenguajes, no quiere decir que debas estructurar todos tus datos con arrays.

El problema de usar arrays como contenedores estructurados de datos es que todo lo que hay dentro es público y no hay forma de usar esas fantásticas funciones set/get que se encargan de impedir que datos incorrectos entren en el array. Por tanto, ¡usa objetos!

Esa es una razón más para pasarte a php5: puedes tener propiedades realmente privadas, de modo que nadie modificará los datos internos de tu clase, tal y como ocurre con los objetos de php4.

Código duplicado

Esto sí que me saca de quicio. Ya no sé ni cuántas veces he leído el código de una aplicación y me he encontrado las mismas tres líneas copiadas y pegadas de función en función y de fichero en fichero. Y me pregunto: ¿les habrán enseñado el concepto de función?

Si hay algo que tiene un aire remotamente familiar a algo que has hecho un poco antes, ¡lo más probable es que esté pidiendo a gritos unos toques de refactorización! Y si necesitas cierta funcionalidad en dos lugares separados, no copies y pegues el código, ¡haz una función!

No sólo el código final acaba siendo más corto, sino que además es más fácil entender cinco líneas de código, que se pueden expandir a cien cuando miras dentro de cada función, que encontrarse de golpe con cien líneas de código y tratar de entender qué está ocurriendo ahí.

El switch interminable

Hablando de bloques de cien líneas, también me he encontrado repetidamente con switch's de trescientas líneas. Y no se lo desearía ni a mi peor enemigo. ¡Es crueldad y explotación mental y debería ser perseguido criminalmente!

Obviamente, los cientos de líneas deberían ser divididos en funciones, de modo que no haga falta usar media hora en un switch para hacerse una idea de qué hace.

Tienes que modificar demasiados ficheros

Si tienes que modificar demasiados ficheros para añadir o modificar incluso la funcionalidad más simple, hay algo que no funciona en tu diseño. Esto suele ocurrir cuando las diferentes partes de la aplicación están demasiado acopladas y cada una necesita saber demasiado acerca de las demás.

Lo peor de esto es que tiende a manifestarse cuando la aplicación se hace grande y está además en modo producción-a-tope, de modo que se piden nuevas funcionalidades constantemente, y la mayoría de programadores acaba mintiéndose a sí mismos: Bueno, no pasa nada por cambiar un par de docenas de ficheros para añadir esta funcionalidad nueva, ya lo simplificaré después. Y todos sabemos perfectamente que ese después nunca llega.

Valores fijos en el código

Las constantes se crearon por alguna razón. Es malo encontrarse cosas tal que

if($result==4) {
// bla
} else if($result==5) {
// bla bla
}

donde determinados valores tienen un cierto significado, pero es aún peor encontrarse cosas como

if($result=='row1') {
// bla
} else if($result=='row2') {
// bla bla
}

ya que por ejemplo, si tecleas algo mal y en lugar de row1 escribes rowl, PHP no se quejará porque es una comparación perfectamente válida. Lo que tú necesitas es usar constantes, de modo que cada vez que intentes usar una constante que no ha sido definida, PHP se queje y tú lo arregles inmediatamente.

Además, aunque parece sencillo distinguir entre "1" (el número uno) y "l" (L minúscula), no es tan sencillo para personas con problemas como dislexia, o simplemente, tú mismo, cuando estés cansado. Y los errores introducidos por culpa de no usar constantes son terriblemente difíciles de localizar; la mayoría de ocasiones hay que pasarse horas yendo línea por línea y tratando de averiguar por qué no funciona. Es mucho más fácil y preventivo definir constantes y dejar de preocuparse de tener que actualizar todos los valores puestos a mano en el código si hace falta cambiarlos luego.

Inconsistencias en la interfaz

Es algo de lo que no se suele hablar mucho, pero creo que es realmente preocupante en PHP. No estoy segura de si es algo inducido por el lenguaje en sí (no hay más que echar un vistazo a las funciones de cadenas), pero hay una extraña tendencia en las aplicaciones php que las lleva a no seguir convención alguna en lo que respecta a argumentos de funciones y tipos de valores devueltos.

A veces las funciones devuelven valores booleanos (true o false) o valores que pueden hacer de booleanos (0 ó 1), otras veces usan valores de retorno al estilo C (0 para éxito y 1 o más para los errores), que son obviamente lo contrario a usar el booleano true para éxito y el booleano false para error, ya que en PHPlandia 0==false y 1==true (a menos que uses los operadores === y !==, más estrictos, pero desafortunadamente ese no es el caso de la mayoría de desarrolladores en php)

En otros casos, las funciones no devuelven nada, pero en cambio usan un array pasado por referencia para escribir resultados en él, algo tal que esto:

function my_function(&$return_array)

Esta práctica es algo que realmente me desagrada, porque a menos que acabes de ver la definición de my_function, es imposible distinguir si

my_function($my_array)

está usando $my_array como un parámetro de entrada o de salida.

Es mejor ponerse de acuerdo en un estilo para devolver valores, y usar ese estilo consistentemente en todas las funciones que escribas. Tal que: siempre devolveremos un objeto con dos propiedades, error que es booleano y representa si la función falló o no, más results que contendrá datos si necesitamos devolver algo.

Con eso, sabes que siempre puedes verificar el valor de $res->error en lugar de tener que verificar primero si existe o no, y entonces ver el valor en sí.

Ya captas la idea…

(Nota: en los comentarios de la versión en inglés se recomienda usar excepciones en lugar de esta solución. Lamentablemente, es sólo aplicable si usas php5)

Conclusión

Algunos de estos problemas prácticamente desaparecerían si tuviéramos un editor de php verdaderamente bueno. Aunque Eclipse cumple su labor decentemente con los plugins para php, la mayor parte del tiempo y debido a la forma en que php funciona, todo queda patas arriba y la funcionalidad de autocompletado es absolutamente inútil porque Eclipse no puede encontrar las clases. Refactorizar es algo que casi depende sólo de tu memoria a corto plazo (creo que ví algo parecido a esto en algún sitio…); otros problemas dependen de si la gente programa estando drogada (recordemos el switch de 300 líneas) o de la competencia en PHP y diseño de software en general.

¡Y ahora es el momento de volver a tu aplicación y mirarla con ojos críticos!

20070615 Extracting data with Hpricot

For those (few) of you which haven't heard about it, Hpricot is a nice library for parsing HTML in ruby, created by the even nicer _whytheluckystiff, author of Poignant's Guide to Ruby, Camping and other ruby gems (may you excuse the pun? it was impossible to avoid it).

Since I saw one demonstration by Rob McKinnon at certain LRUG meeting, I have been willing to try Hpricot, but I hadn't seen an application for it yet. No more! I found myself today wanting to extract data from a table in a web page and suddenly I thought: this is a job for Hpricot!. More specifically, I wanted to extract these EXIF tags, and I simply couldn't accept the mere thinking of entering that data manually. It needed to be automated!

Getting it

Getting Hpricot is very easy:

sudo gem install hpricot

(if you're picky you can try more exotic ways of installing in its homepage).

gem install hpricot

if you're in windows, of course.

Understanding it is easy as well, specially if you have used jquery before. It's all about writing selectors for looking for things, so it helps a lot if the HTML document is well marked. Otherwise, you might have to end up doing lots of workarounds or extra code that could be avoided simply by having a class or id specified in the relevant elements.

Inspecting & traversing

So, once I got the library installed, I took a look at the page source code with Firebug. It is specially useful for this kind of jobs because it helps you to visualize the hierarchy of elements in the page, including classes and id's, so you don't have to traverse manually the HTML tree to gather the data you need.

What I was looking for was the table which contained the relevant data. In this case, we're lucky and even if the table hasn't got an id attribute which would make it uniquely identifiable in the whole document, it still has class="inner", which happens to be used only once in it, thus acting effectively as an element identifier.

Firebug in action!

Note how Firebug is showing the tree path for the selected table. If we didn't have the class attribute, we would need to use a selector like "/html/body/blockquote/table/tbody/tr/td/table", but it will be something as simple as "/table.inner".

Hands on Ruby

Ok, so this is where we write a few lines of code which do a lot ;-)

First come the usual series of requires:

require 'rubygems'
require 'hpricot'
require 'open-uri'

Rubygems is required in order to load hpricot, and open-uri is required in order to directly read data from a URI. open-uri comes with ruby, so we don't need to install anything else.

Now we need to get the HTML file. It is as simple as

doc = Hpricot(open("http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html"))

but since I was doing lots of tests and didn't want to overload that guy's server, I simply saved the document as EXIF.html and loaded it with this instead:

doc = open("EXIF.html") { |f| Hpricot(f) }

At this point we have the HTML document in the doc variable, so what are we waiting for?
We initialize a rows variable for holding the data that we'll extract:

rows = []

And now comes the real fun!

(doc/"table.inner//tr").each do |row|
    cells = []
    (row/"td").each do |cell|
       
        if (cell/" span.s").length > 0
              values = (cell/"span.s").inner_html.split('<br />').collect{ |str|
              pair = str.strip.split('=').collect{|val| val.strip}
              Hash[pair[0], pair[1]]
            }
           
            if(values.length==1)
              cells < < cell.inner_text
            else
              cells << values
            end
           
        elsif
            cells << cell.inner_text
        end
    end
    rows << cells
   
end

Ok, not that fast. I'll elaborate a little more on the juicy bits.

(doc/"table.inner//tr").each do |row|

This is the key for reaching the main data. It's like saying I'm looking in doc for all the rows (the tr's) which are contained in a table whose class equals 'inner'. When we use a / it means we want an immediate child. // means a child below the element. As I said before, it's all about selecting and traversing the tree.

With the last line of code, we get returned the content of each tr into the row variable. We can continue extracting data from within row, and that's exactly what we do with

(row/"td").each do |cell|

That one provides us with all the td elements immediately below the current row.

When we reach the td elements, all that is left is to extract the data for each cell and push it into the cells array, which will be pushed into the rows array. But we don't just copy the cell data as it is; some cells contain notes, and some of those notes contain lists of values. I think we can all agree that those lists of values are commonly called Hashes, and they undoubtedly deserve an special treatment!

if (cell/" span.s").length > 0

So that's why I'm checking for the existance of an span with class == s inside each cell. If we find one, there's a note in this row, and probably there's one hash with values. I would say this is the funniest part of all:

values = (cell/"span.s").inner_html.split('<br />').collect{ |str|
  pair = str.strip.split('=').collect{|val| val.strip}
  Hash[pair[0], pair[1]]
}

I'm making use of the fact that each invoked function is returning another object, so that I can chain them consecutively instead of doing a series of assignments. And it reads like this: Take the html inside the span with class s, split it where you find a br, and for each of those split parts remove the surrounding whitespace and split it again where you find a =, so we get a pair of key-value values, remove the whitespace for those pairs as well and put them in a new Hash.

At the end we finish with an array of rows and cells, where certain cells occasionally contain a Hash with the constants used by the row EXIF tag.

It's also interesting to note that the first row is unusable, because it corresponds to the th elements, so we'll simply do a

rows.shift

and it's gone. And to top it all, we could output the rows array to a yaml file, so that we do not need to run this each time we need the list of EXIF tags.

Arrays in ruby have a lovely method called to_yaml which dutifully generates a version of the array in yaml syntax. And it's very easy to output that to a file:

File.open('hexif.yaml', 'w') { |f|
  f << rows.to_yaml
}

And you're done! I hope you liked this small Hpricot tutorial/introduction… and if you have any suggestion or improvement please let me know!

Of course, you can get the complete source code here: hexif.rb. It is a ridiculous 61 lines, including some commented lines and white spaces. Come on get it and do something cool!