Metodi che sembrano keyword

Vedi tutte le pagine e le modifiche recenti o scarica i sorgenti nella pagina


Ruby è pieno di pseudokeyword, cioè di quelle parole che in qualche modo sembrano far parte della sintassi, e che spesso in altri linguaggi sono appunto delle keyword, ma che in ruby sono semplicemente dei metodi.

Il primo e più ovvio esempio è quello dei metodi d’accesso attr() e gli altri:
 class C
  attr_accessor :var
 end
In realta’ attr_accessor e’ un normalisimo metodo, che prende in input un simbolo e tramite eval() definisce altri metodi. Questo metodo assomiglia ad una keyword perche’ * funziona in modo dichiarativo * non usa parentesi

Puo’ sembrare stupido ma la differenza tra molte keyword e dei normali metodi e’ nel loro aspetto dichiarativo.

Cosa si intende per dichiarativo? Semplicemente che ci si limita a indicare cosa di desidera, senza curarsi di quello che accade dietro le quinte. Un tipico uso dichiarativo di una keyword e’ il controllo d’accesso, ad esempio in java:
 public void mio_metodo(){...

Noi non sappiamo, ne ci interessa, il meccanismo per cui mio_metodo() diventa privato. Ci limitiamo a dichiararlo tale, e java si occupa dei dettagli.

In ruby scriveremmo:
 class C
  def foo
  end
  private :foo
 end
Questo e’ ancora un metodo “classico”, altri usi lo rendono ancora piu’ simile ad una keyword:
 class C
  private
   def privatissimo ... end
  public
   def metodo ... end
  protected
   def protetto ... end
 end
public/private/protected sembrano delle keyword, ma in realta’ non lo sono affatto! Non dimenticate, infatti, che la dichiarazione di una classe (o di un modulo) in ruby e’ semplicemente un altro pezzetto di codice che viene eseguito come tutto il resto. Infatti possiamo avere anche questo:
 >> class C
 >>  puts self
 >> end
 C
 => nil
Dunque, public/private/protected sono in realta’ dei normali metodi. Ma come funzionano? Beh, dicono a ruby di trattare ogni definizione di un metodo che li segue in una certa maniera. A come fanno a sapere quando un metodo viene definito? Facile, usano method_added() Questo metodo viene chiamato ogni volta che viene definito un metodo, e gli viene passato il nome del metodo stesso. Un esempio banale:
 >> class Class
 >>  def method_added(id)
 >>   puts 'aggiunto il metodo %s' % id
 >>  end
 >> end
 aggiunto il metodo method_added
 => nil
Notate come method_added() sia stato chiamato subito dopo essere stato definito :) Andando avanti:
 >> class C
 >>  def foo
 >>  end
 >> end
 aggiunto il metodo foo
 => nil
Chiaro il concetto?

A questo punto possiamo provare a definire una pseudo keyword anche noi.

La pseudo keyword ci permettera di dichiarare dei metodi come metodi di test (non che la cosa sia molto utile, perche’ il modulo Test::Unit fa gia’ molte piu’ cose e meglio.

Quello che vogliamo e’ questo:
 class Esempio 
    Test 'Test 0. vuoto'
    def foo
    end

    def metodo # non un test
    end

    Test 'Test 1. This test will fail'
    def this_fail 
      assert 10 < 5;
    end

    Test "Test 2. This test will pass", :this_pass 
    def this_pass 
      assert 10 > 5
    end

 end

 run_tests(Esempio)
E ci aspettiamo un output del genere:
 Test 0. vuoto: true
 Test 1. This test will fail: false
 Test 2. This test will pass: true
Anzitutto definiamo un metodo assert (potremmo anche usare quelle definite in Test::Unit::Assertion ):
 def assert(condition)
  raise 'assertion failed!!' if not condition
  true
 end
Notate che anche assert e’ una keyword in altri linguaggi :) Ora definiamo un meccanismo semplice per associare una descrizione ad un metodo:
 def Test(descr)
  @is_test=desc
 end
In questo modo, quando useremo Test() potremo avere l’informazione contenente la descrizione disponibile. Ora definiamo method_added():
 class Class
  def method_added(id)
    UT[id]=desc if @is_test
    @is_test=false
  end
 end
Cosa significa quell’UT ? Semplice, salviamo la descrizione riguardo il metodo in un Hash globale, nel caso il metodo sia dichiarato come test, poi resettiamo la variabile a false. La nostra “test suite” manca ancora di un meccanismo per eseguire i test:
 def run_tests(klass)
    obj=klass.new
    for i in obj.methods.sort
        run_method obj, i
    end
 end

 def run_method(obj,name)
    if desc=UT[name]
        retval= obj.send(name) rescue false
        print desc,": ",retval, "\n" 
    end
 end
Finito! Visto quanto e’ stato semplice? E considerate che questo meccanismo e’ comunque molto potente, ci sono persone che hanno costruito interi framework su di esso :)
Updated on December 29, 2005 19:58 by Ruby Fan (151.37.147.178)