di: Sandro Paganotti 13 Gennaio 2009
Questo articolo è una sorta di percorso illustrativo all'interno di un'area molto blasonata e forse, in realtà, poco conosciuta e sperimentata di Ruby: la metaprogrammazione. Il termine 'meta-programmazione' si compone del prefisso 'meta' e della parola 'programmazione' e significa «scrivere programmi che creano o manipolano altri programmi (o loro stessi)».
Chi ha già esperienza con Ruby on Rails non troverà difficile far corrispondere alla definizione appena esposta i generatori; infatti invocando:
ruby script/generate model User
lo script Ruby generate produrrà tutto il codice necessario a definire il modello User nell'applicazione che stiamo realizzando. I generatori sono un ottimo esempio di metaprogrammazione esterna cioè di programmi che, una volta eseguiti, generano altri programmi.
In questo articolo però andremo ad approfondire la metaprogrammazione interna (detta anche riflessività o reflection), cioè quella caratteristica che permette ad alcuni linguaggi di programmazione di ispezionare e modificare a runtime il proprio codice.
Ruby è per sua natura orientato e predisposto all'introspezione. Osservando le API (ad esempio nella classe Object), è possibile notare tutta una serie di metodi che ci consentono di ispezionare il contenuto dell'oggetto che stiamo creando/manipolando, facciamo qualche esempio:
a = Array.new # [] a.methods # ["send", "delete_if", "index", ... a.class # Array Object.constants # ["Signal", "FalseClass", "FloatDomain...
La vera potenza di questo linguaggio però può essere percepita solamente comprendendo a fondo il modo in cui Ruby struttura e collega classi ed oggetti: partiamo da un modello molto comune:
Figura 1. Schema classe/istanza
In questo schema è rappresentata una classe, il quadrato, ed un oggetto istanziato (utilizzeremo sempre linee blu per specificare l'istanziazione), il cerchio. La classe contiene al suo interno le variabili di classe (quelle che cominciano con la doppia chiocciola: @@) ed i metodi di cui l'oggetto può usufruire. L'oggetto conterrà invece soltanto le sue variabili di istanza (quelle che cominciano con la chiocciola: @).
Facciamo subito un esempio e supponiamo che la classe dello schema sia User, così definita:
class User
def initialize(name,surname)
@name,@surname = name,surname
end
def full_name
"#{name} #{surname}"
end
end
Istanziamo la classe:
sandro = User.new('Sandro','Paganotti') # istanzazione
sandro.instance_variables # ["@surname", "@name"]
sandro.full_name # "Sandro Paganotti"
Le due stringhe passate come parametri al costruttore vengono memorizzate all'interno dell'oggetto (nelle variabili @name e @surname) mentre il metodo full_name che manipola queste stringhe (concatenandole) è in realtà memorizzato all'interno della classe User e viene invocato dall'oggetto.
Guida ActiveSupportUna panoramica sulle funzionalità più importanti di ActiveSupport:... |
Guida Ruby On Rails 2Scoprire le novità di Ruby on Rails 2, memorizzare i dati con... |
Guida Ruby e il WebUn percorso alla scoperta delle potenzialità offerte da Ruby nella... |
Ogni mercoledì, direttamente nella tua e-mail: articoli, guide e tutorial su Ruby e Ruby on Rails .
Iscriviti alla newsletter