Le applicazioni Rails utilizzano il noto approccio Model-View-Controller, o MVC, un metodo per organizzare il codice nei software che interagiscono con delle persone diventato ormai comune anche per la realizzazione di applicazioni web. Esso permette infatti di raggiungere un ottimo isolamento tra il codice che gestisce i dati e quello che li presenta all'utente, permettendo di estendere e modificare facilmente un'applicazione in qualsiasi momento.
Usando il modello MVC, un'applicazione viene divisa concettualmente in tre parti: modello, viste e controller. Il modello rappresenta gli oggetti del dominio applicativo, siano essi Utenti o Prodotti o Messaggi, insieme a tutte le regole relative ad essi, ad esempio il fatto che un Reparto abbia un solo Direttore e che il Direttore debba essere laureato e così via.
La libreria ActiveRecord
Il layer che si occupa di gestire il modello in Rails è una libreria chiamata ActiveRecord, che permette l'accesso a numerosi database basati su SQL, da Oracle a PostgreSQL a SQLite. Grazie a questa libreria è possibile stabilire un'associazione tra classi scritte in Ruby e tabelle presenti nel database in modo da non dover mai usare SQL (rimane comunque possibile utilizzarlo per effettuare operazioni particolari, a costo di sacrificare la portabilità dell'applicazione su diversi DBMS). Se una tabella Products contiene dei campi relativi a prezzo e disponibilità, questi verranno resi accessibili come attributi della classe Product, automaticamente, e tramite questa classe sarà possibile eseguire ricerche, inserimenti e cancellazioni dei record nel database.
Per far sì che questa mappatura tra database ed oggetti funzioni è sufficiente seguire alcune semplici convenzioni, senza la necessità di descrivere esplicitamente le corrispondenze. Il concetto chiave in questo caso, applicato sistematicamente anche nel resto del framework, è compreso nell'acronimo DRY, che sta per 'don't repeat yourself' ('non ripeterti'). Quello che si intende è che ogni informazione deve essere espressa, per quanto possibile, una sola volta, quindi niente file XML dedicati ad associare tabelle e classi, via le istruzioni per il caricamento dei file, e nessuna descrizione del modello in forma di codice, in quanto è già presente una rappresentazione dello stesso nel database.
Ad esempio, supponiamo di voler creare un'applicazione molto semplice in stile del.icio.us, nella quale poter inserire dei link ed un piccolo commento. Avremo bisogno quindi di un database per contenere ogni elemento e di un'interfaccia in HTML che ci permetta di visualizzare gli elementi presenti e di inserirli tramite una form appropriata, più, naturalmente, il codice che si occupa di elaborare i dati inviati ed inserirli nella tabella appropriata.
Creiamo dunque un database di nome minidel_development
, ed una tabella chiamata entries
con 4 campi: id
, url
, comment
e created_on
, con un codice simile a questo (il codice è per MySQL, ma è facilmente portabile su altri RDBMS):
CREATE TABLE entries (
id INT NOT NULL AUTO_INCREMENT ,
url VARCHAR( 100 ) NOT NULL ,
comment TEXT NOT NULL ,
created_on DATE NOT NULL ,
PRIMARY KEY ( id )
);
Tutto quello che dobbiamo fare per manipolare oggetti di questo tipo è creare un modello, ovviamente dopo aver creato un'applicazione Rails. diamo quindi il comando Rails minidel
ed entriamo nella cartella minidel
appena creata.
Una breve nota sulla configurazione del database: potete indicare alla libreria ActiveRecord quale DBMS usare e con quali parametri, tramite il file database.yml
nella directory config
. Aprendolo troverete tre spezzoni di questo tipo:
development:
adapter: mysql
database: minidel_development
username: root
password: segreta
host: localhost
Se avete seguito le istruzioni alla lettera questo andrà bene, altrimenti provvedete a cambiare le opzioni sostituendo i valori appropriati. Come abbiamo già detto precedentemente, il file contiene un blocco di codice per ogni ambiente di lavoro, cioè environment, production e test, ma per noi sarà sufficiente usare quello di sviluppo.
Abbiamo già visto che esiste una directory script
contenente molti piccoli programmi utili. Uno dei più interessanti è script/generate
che serve a creare alcuni file con contenuti predefiniti per diversi scopi. Usiamo dunque questo script per creare il codice del modello:
$ ruby script/generate model Entry
exists app/models/
exists test/unit/
exists test/fixtures/
create app/models/entry.rb
create test/unit/entry_test.rb
create test/fixtures/entries.yml
Potete notare che viene inizialmente verificata la presenza di alcune directory, e poi vengono creati tre file: gli ultimi due sono relativi alla scrittura di test e per ora li ignoreremo, entry.rb
invece contiene il codice della nostra classe, che è semplicemente questo:
class Entry < ActiveRecord::Base
end
il codice definisce una classe chiamata Entry
, che è una sottoclasse della classe Base
contenuta nel modulo ActiveRecord
. L'equivalente in Java sarebbe una cosa come
public class Entry extends activerecord.Base {
}
Non abbiamo alcun bisogno di aggiungere altro, tutte le informazioni sono già disponibili nel database, e sarà ActiveRecord a occuparsi di creare tutte le funzionalità al posto nostro. Per sperimentare useremo un altro utilissimo script, ovvero script/console
, il quale ci permette di avere una piccola shell interattiva nella quale usare direttamente il codice Ruby. Guardate questa piccola sessione di prova:
$ ruby script/console Loading development environment. >> Entry.find_all => [] >> entry=Entry.new => #<Entry:0x3991690 @new_record=true, @attributes={"created_on"=>nil, "url"=>"", "comment"=>""}> >> entry.url = "http://www.html.it" => "http://www.html.it" >> entry.comment = "finalmente hanno aggiunto una guida su rubyonrails!" => "finalmente hanno aggiunto una guida su rubyonrails!" >> entry.save => true >> Entry.find_all => [#<Entry:0x397ea90 @attributes={"created_on"=>"2006-06-11", "url"=>"http://www.html.it", "id"=>"1", "comment"=>"finalmente hanno aggiunto una guida su ruby on Rails!"}>]
Le linee contrassegnate da >>
, che per facilitare la lettura abbiamo segnato in rosso, sono quelle in cui scrive l'utente, mentre quelle indicate da =>
mostrano il risultato dell'istruzione.
Inizialmente proviamo a cercare tutti gli elementi del database, usando il metodo find_all
della classe Entry
, e ci viene restituito un Array
vuoto, indicato con "[]
". Attenzione, find_all
è un metodo, ed in realtà nella maggior parte dei linguaggi andrebbe richiamato con delle parentesi alla fine, ma in Ruby, quando è ovvio che si sta chiamando una funzione, queste possono essere omesse.
Poi creiamo un nuovo oggetto, usando il metodo new
, e lo assegniamo alla variabile entry
, in seguito ne impostiamo gli attributi url
e comment
, ed infine lo salviamo con il metodo save
. A questo punto, proviamo di nuovo a cercare tutti gli elementi, e correttamente, ci viene restituito un oggetto identico. Provate a fare degli esperimenti con la console, per uscire è sufficiente che usiate l'istruzione exit
.
Per ora non vi preoccupate dello strano formato in cui viene mostrato l'oggetto, in seguito spiegheremo anche quello, ma guardate come i campi del nostro oggetto vengano aggiornati. In particolare si può notare che dopo il salvataggio il nostro oggetto ha ottenuto magicamente anche un valore corretto per created_on
, ed infatti ActiveRecord è in grado di individuare alcuni attributi particolari e gestirli automaticamente, risparmiandoci molte righe di codice.
Un discorso speciale va fatto per l'attributo id
, il quale viene gestito magicamente da Rails ma non viene in genere mostrato all'utente. Questo attributo infatti è pensato per essere usato solo da ActiveRecord, e noi dovremo preoccuparci generalmente solo di usarlo come chiave di ricerca, come vedremo più avanti.