Metaprogrammazione e reflection in Ruby

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.

Classi e istanze

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

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.

Guide Ruby

Guida ActiveSupport

Una panoramica sulle funzionalità più importanti di ActiveSupport:...

Guida Ruby On Rails 2

Scoprire le novità di Ruby on Rails 2, memorizzare i dati con...

Guida Ruby e il Web

Un percorso alla scoperta delle potenzialità offerte da Ruby nella...

Altre guide

Newsletter @Ruby

Ogni mercoledì, direttamente nella tua e-mail: articoli, guide e tutorial su Ruby e Ruby on Rails .

Iscriviti alla newsletter

Altre newsletter

Corsi in aula

Nessun corso previsto

Nessun corso previsto