Planet Sourcesense

March 11, 2010

Matteo Vaccari

Lazy proxy in Ruby

I’m a total newbie when it comes to Ruby evaluation tricks, so when I learned this today I felt it was a good thing to share :-)

The problem: speeding up a Rails application. When all is said and done, you need to cache page fragments in order to speed up an application significantly. For instance: you start with

class ProductsController < ApplicationController
  def category
    @products = Product.find_by_category(params[:id])
  end
end

...

<div id="products">
  <% for product in @products do %>
    <!-- some complicated html code -->
  <% end %>
</div>

and then add fragment caching in the view with

<% cache "category-#{params[:id]}" do %>
  <div id="products">
    <% for product in @products do %>
      <!-- some complicated html code -->
    <% end %>
  </div>
<% end %>

OK, this speeds up view rendering. But we are still executing the query in the controller, to obtain a list of products we are not even using. The standard Rails solution to this is

  class ProductsController < ApplicationController
    def category
      unless fragment_exist? "category-#{params[:id]}"
        @products = Product.find_by_category(params[:id])
      end
    end
  end

This is nice enough. But one things is worrying me, is there might be a race condition between the “unless fragment_exists?” test and the call to “cache” in the view. If the cron job that cleans the cache directory executes between the two, the user will see an error.

I thought to myself, wouldn’t it be nice to give the view a lazy proxy in place of the array of results? The lazy proxy will only execute the query if it is needed. The controller becomes:

class ProductsController < ApplicationController
  def category
    @products = LazyProxy.new do
      Product.find_by_category(params[:id])
    end
  end
end

The LazyProxy magic is surprisingly simple:

class LazyProxy < Delegator
  def initialize(&block)
    @block = block
  end

  def __getobj__
    @delegate ||= @block.call
  end
end

The block given to the constructor is saved, and not used immediately. The Delegator class from the standard library delegates all calls to the object returned by the __getobj__ method. The “||=” trick makes sure that the result of @block.call will be saved in an instance variable, so that the query is executed at most once.

So the idea is that the view will be given a lazy proxy for a query. If the fragment exists, the view code will not be evaluated and the proxy will not be used. No query. If the fragment does not exist, the lazy proxy is used and a query is executed. There is no race condition, for there is no test to see if the fragment exists.

What do you think?

Update One additional advantage of the lazy proxy is that you no longer need to make sure that the fragment key is the same on both view and controller.

by matteo at March 11, 2010 03:54 PM

March 08, 2010

Piergiorgio Lucidi

Sourcesense contributes to IKS project

I was involved with my collegue Tommaso Teofili in supporting IKS to complete their survey dedicated to CMS products. Especially Tommaso is following this project very thoroughly!

by Piergiorgio Lucidi at March 08, 2010 01:11 PM

March 07, 2010

Matteo Vaccari

Prima lezione del corso di Tecnologia e Applicazioni Internet

Summary: first lesson with my new class. Teaching TDD, letting the students get a glimpse of how skilled they are in programming; which is unfortunately not much.

Per il secondo anno insegno Tecnologia e Applicazioni Internet all’Insubria. Il mio obiettivo per questo corso è di insegnare come sviluppare applicazioni web, applicando le pratiche tecniche di Extreme Programming. In particolare, vorrei insegnare Test-Driven Development e i principi di Object-Oriented Design, per come li capisco. Il mio meta-obiettivo per questo corso è fare in modo che lo studente diventi il doppio più bravo a programmare.

Per questo corso uso Java e non Rails. Il motivo di questa scelta è che Rails, per quanto sia una spanna sopra a tutti i web framework in Java, è purtuttavia un framework e in quanto tale è una stampella, una gruccia, che ti permette di stare in piedi ma certo non ti aiuta quando vuoi imparare a camminare da solo, men che meno a correre. Per imparare a camminare da soli bisogna imparare a programmare a oggetti.

In Aula

Per la prima lezione ho spiegato il TDD da solo, senza la complicazione delle servlet. Mi sono sforzato di pensare a un esempio che fosse piccolo a sufficienza per fare una demo di fronte agli studenti, in un pomodoro o poco più. Ho deciso di fare un “calcolatore a riga di comando”, ovvero un programma che presa una stringa come “2 + 3″ come argomento sulla riga di comando, stampi “5.0″ su standard output.

Il primo test che ho scritto:

@Test
public void twoAndThreeIsFive() throws Exception {
	Calculator calculator = new Calculator();
	double result = calculator.add(2, 3);
	assertEquals(5.0, result, EPSILON);
}

Abbastanza semplice da far passare. Ma non era sufficiente, perché dalla riga di comando gli argomenti arrivano come stringhe e non come interi già parsati. Per cui ho scritto un secondo test che ha fatto emergere una classe Parser

@Test
public void willParseTwoAndFive() throws Exception {
	Calculator calculator = new Calculator();
	String result = new Parser(calculator).calculate("2 + 5");
	assertEquals("7.0", result);
}

Perché creare una seconda classe a questo punto? Non sarebbe bastato mettere il metodo “parse” nella classe Calculator? Avrei potuto, però in questo modo il metodo “add” sarebbe diventato un metodo ad uso interno della classe. Come avrei fatto a testarlo? Avrei dovuto buttare via il test su add, oppure tenere add come “public” anche se in realtà serve solo internamente. Oppure usare qualche brutto trucco come dare ad “add” visibilità protected oppure package.

Invece, tenendo il Calculator come classe a sè che si occupa solo di fare conti, mentre Parser si occupa di leggere e scrivere stringhe, posso tenere “add” come metodo pubblico di Calculator. Martin direbbe che ho applicato il “Single Responsibility Principle.” Per me è stato decisivo pensare “se no mi tocca testare un metodo privato”.

Poi non ero ancora soddisfatto. Nel TDD quello che facciamo è sviluppare un isola felice di codice a oggetti, che però a un certo punto si deve scontrare con la realtà procedurale del mondo esterno. In questo caso il “mondo esterno” è il main, che deve creare e invocare i nostri oggetti. Per me è fondamentale che il main non contenga nessuna logica, ma soltanto la creazione di un certo numero di oggetti, collegati insieme. Se faccio restituire il risultato a Parser#calculate, poi al main resta la responsabilità di invocare System.out.println() per stampare.

Il mio obiettivo è ridurre al minimo la logica nel main, in modo che il main, che è per sua natura più difficile da testare unitariamente, sia così semplice da risultare ovviamente corretto. O comunque, per essere sicuro che il main se fallisce, fallisce sempre, e se funziona, funziona sempre. In questo modo posso essere ragionevolmente certo che se il mio main contiene un errore, me ne accorgerò. Gli errori di cablaggio, come li chiama Hevery, sono facili da trovare.

Allora ho applicato il principio “Tell, don’t Ask,” e ho passato l’OutputStream come collaboratore alla Parser#calculate.

@Test
public void willParseTwoAndFive() throws Exception {
	Calculator calculator = new Calculator();
	OutputStream stream = new ByteArrayOutputStream();
	new Parser(calculator, stream).calculate("2 + 5");
	assertEquals("7.0\n", stream.toString());
}

In questo modo è come se dicessi a Parser, “questa è la tua stringa da calcolare, questo è lo stream dove devi scrivere il risultato, adesso arrangiati, non ne voglio sapere nulla.”

Un pattern che si può riconoscere in questo design è una versione embrionale di collecting parameter. In generale cerco di evitare di avere metodi che restituiscono dati. Di solito è più efficace dire agli oggetti di fare cose, piuttosto che chiedere dati. Questo è il principio “tell, don’t ask“.

Possiamo anche vedere l’oggetto Parser come un adapter: adatta l’interfaccia del Calculator, basata su numeri, alle necessità di main, che lavora con stringhe.

Tutto ciò, beninteso, non significa che per programmare bisogna ad ogni piè sospinto cercare nel manualone dei pattern uno o più pattern da ficcare dentro al nostro codice. Al contrario, quello che ho fatto io è stato di scrivere il codice che mi sembrava più appropriato per risolvere il mio problema, e poi, ragionandoci sopra, ho riconosciuto dei pattern in quello che avevo scritto.

In laboratorio

La seconda parte della lezione si è svolta in laboratorio. Ho proposto un semplice esercizio, di scrivere un programma che concatena le righe di due file a una a una, un po’ come fa il comando paste(1) di Unix. Ho visto subito che per la maggior parte degli studenti questo esercizio era troppo difficile, per cui sono subito passato a suggerire come primo test una versione semplificata del problema.

@Test
public void pasteLinesFromArrays() throws Exception {
	List a = Arrays.asList("aa", "bb");
	List b = Arrays.asList("xx", "zz");
	List result = new ArrayList();

	Concatenator concatenator = new Concatenator();
	concatenator.concatenate(result, a, b);

	assertEquals(Arrays.asList("aaxx", "bbzz"), result);
}

I miei studenti sono al terzo anno di Informatica triennale. Nel nostro corso di laurea, il linguaggio di programmazione di riferimento è Java. Purtroppo, ho dovuto osservare che per la grande maggioranza dei miei circa 40 studenti, scrivere il codice che fa passare questo esercizio è un problema difficile. E nessuno (mi pare) è stato in grado di estendere il codice per fare passare anche il secondo test:

@Test
public void listsCanBeOfDifferentLength() throws Exception {
	List a = Arrays.asList("a");
	List b = Arrays.asList("b", "c");
	List result = new ArrayList();

	Concatenator concatenator = new Concatenator();
	concatenator.concatenate(result, a, b);

	assertEquals(Arrays.asList("ab", "c"), result);
}

Non so che cosa pensare. Questi esercizi mi sembrano di un livello di difficoltà paragonabile al famoso “problema” FizzBuzz, che viene usato nei colloqui di lavoro per scremare quelli che non sanno programmare per niente da quelli che forse sono capaci di fare qualcosa. Al terzo anno mi aspetterei qualche cosa di più. Sto cercando di ricordare me stesso al terzo anno di università. Sono sicuro che sarei riuscito a risolvere questo problema.

Ma non importa. Venerdì prossimo continuerò con esercizi di questo tipo. Piano piano miglioreremo. Sono sicuro che, alla fine del corso, gli studenti che avranno continuato a frequentare raggiungeranno l’obiettivo di diventare (almeno) il doppio più bravi a programmare.

by matteo at March 07, 2010 04:12 PM