Nel modello MVC la gestione dell'interazione con l'utente è demandata ai Controller, rappresentati dalla libreria ActionController, che si occupa di fornire le funzionalità di alto livello dell'applicazione. Ogni Controller è una normale classe, ed ogni metodo pubblico definito in questa classe corrisponde ad un'azione specifica.
Rails contiene un meccanismo portabile per la creazione di URL user friendly ed effettua una trasformazione predefinita da un URL come http://host/page/show/1 in una chiamata al metodo show
di un oggetto di classe PageController
con un parametro 1. In ogni controller di Rails possono essere inoltre essere inserite informazioni relative a filtri da applicare prima o dopo ogni operazione, che permettono di realizzare in poche righe l'autenticazione, la compressione delle pagine, la gestione del caching e molto altro.
Ad ogni azione definita in un Controller corrisponde una Vista, ovvero ciò che l'utente effettivamente si trova davanti.
Per definire una vista con Rails si usano file con estensione .rhtml che contengono delle direttive scritte in Ruby immerse all'interno di codice XHTML, in modo simile a quanto si fa con PHP, JSP o HTML::Mason. Evidentemente, differenti viste possono far uso delle stesse funzionalità fornite da metodi del controller, ad esempio in un sito di informazione è possibile utilizzare una funzionalità che individua le ultime dieci notizie sia per realizzare l'home page sia per realizzare un feed RSS.
Il modo più semplice di cominciare a lavorare con controller e viste è utilizzare nuovamente a script/generate
.
$ ruby script/generate controller Home
exists app/controllers/
exists app/helpers/
create app/views/home
exists test/functional/
create app/controllers/home_controller.rb
create test/functional/home_controller_test.rb
create app/helpers/home_helper.rb
Come vedete l'output è simile a quello per la creazione del modello ma in questo caso i file importanti sono due: home_helper.rb
e home_controller.rb
. Su primo torneremo più avanti, il secondo è molto simile a quello per il modello:
class HomeController < ApplicationController
end
La superclasse, in questo caso, è ApplicationController
che è stata anch'essa creata dallo script. Il compito di questa classe è di raccogliere funzionalità condivise nell'intera applicazione.
Molto spesso però è possibile associare un controller direttamente ad un modello, per rendere possibili le operazioni CRUD (cioè creazione, lettura, modifica e cancellazione) sugli oggetti relativi. Rails fornisce la possibilità di generare automaticamente un insieme di controller, viste e modello che permettono di effettuare tali operazioni trami le scaffolding.
Per poter cominciare ad utilizzare il nostro sistema ci basta aggiungere una riga di codice al controller Home
, che possiamo trovare in app/controllers/home_controller.rb
:
class HomeController < ApplicationController
scaffold :entry
end
Questa linea può sembrare strana ma in realtà è molto semplice. Come abbiamo già detto, Rails si fonda sul concetto di domain specific language,ovvero di minilinguaggi che servono ad esprimere in modo conciso dei concetti relativi all'applicazione: scaffold
non è altro che un normalissimo metodo, solo che in Ruby:
- possiamo applicare un metodo senza mettere le parentesi, se non c'è ambiguità
- non c'è differenza tra dichiarazioni e parte eseguibile del codice, quindi possiamo chiamare dei metodi dove vogliamo
:entry
, come ogni altra cosa composta da ":" seguita da una parola, è un simbolo, senza entrare nei dettagli potete pensare che un simbolo serve a chiamare una cosa per nome, o a specificare il valore di un'opzione.
Queste peculiarità della sintassi di Ruby fanno sì che sia semplice scrivere questo tipo di metodi che assomigliano a macro o parole chiave.
Il metodo scaffold
non fa altro che generare automaticamente una serie di azioni nel controller che ci permettono di manipolare gli oggetti di tipo Entry.
Per provare usate di nuovo script/server
e dirigetevi su http://localhost:3000/home, dove troverete ad aspettarvi la vostra prima Entry, creata precedentemente da console.
Per orientarvi meglio nel mondo di Rails, metodi come scaffold vengono detti class method, in quanto sono metodi della classe e non di un oggetto. Anche i metodi find
e new
che avevamo visto lavorando con il modello erano metodi di classe. Alternativamente, spesso i metodi come questo che aggiungono "magicamente" funzionalità ad una classe vengono chiamati macro, anche se il termine è inesatto. Tenete a mente queste informazioni se effettuate una ricerca o se dovete chiedere aiuto in mailing list ed altro.
Tornando alla pratica, provate a verificare un po' le funzionalità generate automaticamente per capire quanto lavoro è stato svolto automaticamente da Rails. Potete visualizzare gli oggetti, crearne di nuovi ed andare a modificare quelli esistenti, o cancellare quelli più vecchi. Se create molti elementi verrà addirittura generato un sistema di paginazione che li presenti in schermate differenti per non avere una sola pagina gigante.
$ ruby script/generate scaffold Entry
exists app/controllers/
exists app/helpers/
create app/views/entries
exists test/functional/
dependency model
exists app/models/
exists test/unit/
exists test/fixtures/
identical app/models/entry.rb
identical test/unit/entry_test.rb
identical test/fixtures/entries.yml
create app/views/entries/_form.rhtml
create app/views/entries/list.rhtml
create app/views/entries/show.rhtml
create app/views/entries/new.rhtml
create app/views/entries/edit.rhtml
create app/controllers/entries_controller.rb
create test/functional/entries_controller_test.rb
create app/helpers/entries_helper.rb
create app/views/layouts/entries.rhtml
create public/stylesheets/scaffold.css
Potete notare come siano stati creati molti più file, in particolare un file con estensione per ogni azione, ed un file CSS di default. Anche la classe relativa al modello viene creata se non era stato fatto precedentemente.
Se ora provate ad avviare script/server e ad accedere a http://localhost:3000/entries potrete vedere come sia già possibile creare, modificare ed eliminare degli elementi. I file creati non sono pensati per rimanere stabili fino alla fine del progetto ma per essere una base su cui lavorare.
Il file controller che è stato generato questa volta è decisamente più complesso, e non preoccupatevi se non lo comprendete appieno adesso, nel corso della guida apprenderete tutte le informazioni necessarie a comprenderlo, e scoprirete che in realtà è molto semplice.
Oltre a queste funzionalità elementari le viste, gestite dalla libreria ActionView, offrono meccanismi avanzati per il riutilizzo di codice, tramite l'uso di viste parziali, layout, metodi helper pensati per generare xhtml (ad esempio realizzare un form per dei dati anagrafici in una riga) o dedicati alla creazione di interfacce AJAX-based, sulle quali torneremo più avanti. Definire helper è semplicissimo, basta definire dei semplici metodi all'interno di file specifici, che come abbiamo visto, vengono creati automaticamente quando creiamo un controller.
Per le viste è possibile utilizzare linguaggi di template differenti se lo si desidera, anche se questi potrebbero essere leggermente meno integrati. Questo è un concetto che vale in tutto l'ambiente, e che ritorna continuamente: convention over configuration, significa che finché si seguono le idee suggerite dagli autori del framework non si dovrà configurare nulla, e tutto funzionerà automaticamente, come già visto, ad esempio per costruire l'associazione tra oggetti del database e classi, o per quella tra URL e controller. È però sempre possibile decidere di non seguire le convenzioni, al prezzo di dover specificare esplicitamente quello che si desidera.