Archive for August, 2006

Uploading files to Rails models

Tuesday, August 29th, 2006

Riassunto: ecco come aggiungere un’immagine a un modello in una applicazione Rails

Just a summary of what it takes to add an image to a model in a Rails application. This is to remind myself of the steps it took to make it work.

There are two plugins out there: file_column and acts_as_attachment. The second is more full-featured, and requires a separate table for each attachment. So I chose to use the former, for simplicity. I can always change my mind later.

Using file_column is straightforward, unless you wish to change the default place where image files are saved; in which case you must add options that are not well documented. The default location is not good, because it chooses a different directory for every model: public/people, public/places, public/foobar, and so on. This is not good when deploying with capistrano, because you will need to make all of these dirs symbolic links. Much better to have a single root for all uploads: public/uploads/people, public/uploads/places, …

Suppose you want to add an “image” file attachment to a model “Foobar”. First you must add an “image” column to the foobars table:


  $ script/generate migration AddImageColumnToFoobars
  ...

edit the migration file


class AddColumnImageToFoobars < ActiveRecord::Migration
  def self.up
    add_column :foobars, :image, :string
  end

  def self.down
    remove_column :foobars, :image
  end
end

and execute the migration


  $ rake migrate
  ...

Time to do a little functional test. Open up test/functional/foobar_controller_test.rb and write something like


  def test_update_image
    post :edit, :id => 2, :foobar => {
      :image => upload(Test::Unit::TestCase.fixture_path 
          + '/files/animal.jpg', 'image/jpg'),
    }
    assert_redirected_to :action => 'page', :name => old_content.name
    updated_foobar = Content.find(2)
    assert_not_nil updated_foobar.image, "what? no image?"
    assert_equal '2/animal.jpg', updated_foobar.image_relative_path
  end

Run the test. Does it fail? Good! Failure is progress.

Now modify the Foobar model


class Foobar < ActiveRecord::Base                                                       
  file_column :image, 
    :root_path => File.join(RAILS_ROOT, "public", "upload"), 
    :web_root => "upload/"
end

The :root_path option says we want to save all uploads beneath the “public/uploads” directory. The :web_root is needed so that the proper url for the pictures can be computed. A bit redundant, but I see how this can be handy for apps that are deployed on multiple servers.

Try the functional test again. It should work now.

Now modify the views/foobar/_form.html adding


<%= file_column_field "foobar", "image" %>

You must also modify the form tag in views/foobar/edit.rhtml and new.rhtml adding multipart encoding, this way:


<%= form_tag({:action => 'update', :id => @foobar}, 
        {:multipart => true}) %>

otherwise, upload will not work.

At last, you can show the image in your Foobar pages:


<%= image_tag url_for_file_column(@foobar, "image") if @foobar.image %>

The “if” part is needed in case no image has been uploaded yet.

The finishing touch is to add validations to the model:


class Foobar < ActiveRecord::Base                                                       
  file_column :image, 
    :root_path => File.join(RAILS_ROOT, "public", "upload"), 
    :web_root => "upload/"

  validates_file_format_of :image, :in => ["gif", "png", "jpg"]
  validates_filesize_of :image, :in => 15.kilobytes..1.megabyte
  validates_image_size :image, :min => "1200x1800"
end

Conclusion

The “file_column” plugin is simple and it works beautifully, even if the docs could be better. Be sure to consult the rdoc within the source files.

If you need to run searches on your uploaded files, such as, for instance, looking for all pictures in “landscape” format, then file_column does not help. The other solution, acts_as_attachment could be the right choice.

The Good Life

Thursday, August 24th, 2006

Summary: The Good Life by Jay McInerney is not one of his “great” novels.

Ho appena finito di leggere The Good Life di Jay McInerney. E’ la storia di Luke, un esperto finanziario di Wall Street di grande successo, sposato a Sasha, una donna estremamente bella e sofisticata; e di Corrine, una autrice di sceneggiature di scarso successo, mamma di due gemelli e sposata a Russel, un editore di modesto successo.

Riassunto

Quello che segue è un riassunto senza troppi spoiler.

Luke è in crisi di mezza età, e si è preso un “sabbatico” dal suo lavoro per riprendere i contatti con sua moglie e sua figlia Ashley, che a 13 anni è in piena adolescenza e non ne vuole sapere.

Russel è in crisi di mezza età; ha un’amante che soddisfa i suoi desideri sessuali e per il resto quando è a casa si dedica in parte ai gemelli, ma soprattutto al suo grande hobby, la cucina (immaginatevi che palle.)

Corrine è in crisi perché Russel è distante (a letto legge i manoscritti); perché non riesce a completare la sceneggiatura a cui sta lavorando; perché i gemelli sono nati con la fecondazione artificiale; perché è convinta di essere una cattiva madre…

La vicenda si svolge a New York dal 10 settembre 2001 fino al Natale dello stesso anno. Il giorno dell’attacco aereo Luke si salva per caso. Incontra Corrine; cominciano a frequentarsi.

IMHO

Prima di tutto mettiamo in chiaro che si tratta di un libro di un autore di prima classe. I personaggi escono fuori dalla pagina, si capisce che l’autore descrive sempre solo persone che conosce e situazioni che ha vissuto. Questo per me è importantissimo: scrivere significa avere delle storie da raccontare.

Dieci anni fa, quando lessi The Last of the Savage, continuavo a pensare a quel libro anche dopo mesi che lo avevo finito. E’ probabilmente uno dei 10 romanzi più belli che ho letto (non che questo significhi gran che) e sicuramente il più bello di McInerney.

Venti anni fa, quando lessi Le mille luci di New York (Bright Lights, Big City) mi fece lo stesso effetto. Un romanzo perfetto, un colpo di fulmine. Lo regalai a tutti i miei amici.

Purtroppo questa cadenza di un grande romanzo ogni dieci anni non ha funzionato; almeno per me, il colpo di fulmine con questo romanzo non c’è stato. Sarà perché non riuscivo a identificarmi, a “tifare” per nessuno dei personaggi? Ma neanche in The Last of The Savage potevo. Però in quella storia c’era un respiro epico che in questo libro non c’è.

The Good Life ha dei buoni momenti, e dei momenti tediosi. Fra i momenti migliori c’è il contrasto fra la New York sofisticata e il Sud rurale degli Stati Uniti (che era un’elemento chiave di Last of the Savage) Fra i momenti più noiosi, le conversazioni degli amici sofisticati di Russel e Corrine (fatta eccezione per Washington, che è fichissimo) e gli eventi mondani di Sasha e Luke.

Un osservazione carina la fa Jim Crespi, amico di Russel e regista di Hollywood negli anni ’70: in quegli anni i produttori di Hollywood si resero conto che non erano più capaci di fare i film, e si affidarono a dei giovani (Scorsese, Altman, Coppola, Cimino…) che ribaltarono il cinema. Poi un anno vado a vedere “Pulp Fiction”, e mi ritrovo a vedere un film dove tutto è fatto per finta, esagerato e ironico. Mentre il cinema dei ’70 era tutto sull’onestà e la verità, quello che va adesso è un cinema di strizzate d’occhio e di scherzi. (Non ho sottomano il libro, se no metterei la citazione esatta.) Nel romanzo Jim scompare sotto le torri.

In conclusione…

Se non hai mai letto McInerney, comincia dai due che ho elogiato qui sopra. Non te ne pentirai. (Statisticamente, questo autore piace molto di più ai maschi. Non so perché.) Se conosci McInerney, questo è un buon libro: ma non è uno dei suoi libri “A”. Mettiamolo insieme ai suoi libri “B”, come Riscatto (Ransom) o Tanto per cambiare (The Story of My Life).