Microsoft's Internet Explorer browser has no built-in vector graphics machinery required for "loss-free" gradient background themes.
Please upgrade to a better browser such as Firefox, Opera, Safari or others with built-in vector graphics machinery and much more. (Learn more or post questions or comments at the Slide Show (S9) project site. Thanks!)
I framework web sono dei software che permettono di sviluppare e mantenere applicazioni web in modo veloce e semplice.
Questo è possibile grazie all’utilizzo di un architettura software consistente ed all’uso di alcune convenzioni nella metodologia di sviluppo. Lo sviluppatore, attenendosi a tali convenzioni è in grado di produrre, in poco tempo, applicazioni web di alta qualità.
La libertà dello sviluppatore è comunque garantita dalla possibilità di estendere il framework con nuove funzionalità.
Esistono numerosi web framework progettati per sviluppare applicazioni web utilizzando diversi linguaggi di programmazione.
Il framework RubyOnRails (RoR) è stato originariamente ideato e sviluppato da David Heinemeier Hansson.
Il progetto è stato reso opensource nel Luglio del 2004.
Il grande merito di RoR consiste nell’aver introdotto il linguaggio di programmazione Ruby nel mondo dello sviluppo web.
Come altri framework web, RoR basa la propria architettura sul modello (o pattern) MVC (Model View Controller). In questo modello si individuano le tre principali componenti di un’applicazione web.
In generale, il modello (model) rappresenta la componente responsabile di mantenere lo stato dell’applicazione.
La vista (view) espone l’interfaccia utente dell’applicazione web. Ad esempio, un negozio online esporrà, per mezzo di una vista, l’elenco dei prodotti disponibili in magazzino.
Infine, il controller è responsabile della logica di funzionamento dell’applicazione.
Come abbiamo visto, il modello rappresenta la componente responsabile di mantenere lo stato dell’applicazione. Ad esempio, all’interno della tabella di un database si registrano le informazioni relative agli utenti che utilizzano l’applicazione web. Nell’ottica del pattern MVC, tale tabella rappresenta un modello.
I sistemi database più diffusi oggi appartengono alla categoria dei database relazionali.
I database relazionali basano il loro principio di funzionamento su una complessa teoria matematica. In effetti, interrogare un database relazionale può risultare, in certi casi, poco agevole.
Inoltre, la teoria dei database relazionali non si coniuga bene con quella della programmazione orientata agli oggetti.
Per questo motivo, è stata sviluppata delle soluzioni (Design Pattern) che consente di mettere in relazione le tabelle di un database relazionale con le classi di un’applicazione orientata agli oggetti. Il nome di questo design pattern è Object Relational Mapping (ORM).
Le viste (views
) sono responsabili della creazione dell’interfaccia
utente che l’applicazione web espone all’utente.
Ad esempio, in una tipica applicazione web, le viste si occupano di
generare le pagine web contenenti i moduli di inserimento (form
)
dati, i contenuti, i risultati di una ricerca, etc.
In RoR la generazione delle viste avviene in maniera dinamica. Questo
significa che la vista viene generata a partire dalla struttura
della pagina (layout
), “riempita” dalle informazioni dinamiche
generate in tempo reale.
Il framework RoR mette a disposizione diverse tecnologie di generazione dinamica delle viste. Le più utilizzate sono:
ERB permette di iniettare degli snippet di codice ruby all’interno di codice HTMl per generare i contenuti dinamici delle viste.
Builder è una libreria inclusa in RoR che permette di generare documenti XML attraverso un DSL (Domain Specific Language) ruby.
I controller di un’applicazione RoR gestiscono la cosidetta logica della stessa.
In particolare, il controller coordina l’interazione tra l’utente, le viste e i modelli.
Inoltre, il controller è responsabile
Per iniziare a sviluppare un’applicazione web RoR occorrono pochi semplici passi.
Gran parte del processo di installazione è infatti gestito dal package
manager del sistema Ubuntu (apt
) e dal package manager di ruby
(rubygems
).
L’installazione dell’interprete ruby si riduce all’esecuzione del seguente comando:
andrea@ganimoide:~$ sudo apt-get install ruby ruby-dev ri rdoc \ libopenssl-ruby
Per quanto riguarda l’installazione di Rubygems
occorre prima
scaricare e decomprimere il tarball dell’applicazione:
andrea@ganimoide:~$ wget http://rubyforge.org/frs/download.php/45905/rubygems-1.3.1.tgz andrea@ganimoide:~$ tar xvzf rubygems-1.3.1.tgz
A questo punto occorrerà eseguire lo script setup.rb
all’interno
della cartella rubygems:
andrea@ganimoide:~$ cd rubygems-1.3.1 && sudo ruby setup.rb
Una volta installato l’interprete ruby
e il package manager
rubygems
, l’installazione di RoR si riduce all’esecuzione di un solo
comando:
andrea@ganimoide:~$ sudo gem install rails
Il comando gem install rails
risolve ed installa tutti i pacchetti
(gemme) da cui il framework RoR dipende.
Per verificare la corretta installazione di RoR basterà eseguire:
andrea@ganimoide:~$ rails --version Rails 2.3.2
RoR supporta diversi tipi di motori database, tra cui:
Nella slide precedente abbiamo visto come RoR supporti diversi database.
Tuttavia, la configurazione standard del framework prevede l’utilizzo di SQLite3.
Se sul sistema in uso non è presente SQLite3, sarà sufficiente installare le librerie C da apt e quelle ruby da rubygems
andrea@ganimoide:~$ sudo apt-get install libsqlite3-0 libsqlite3-dev [...] andrea@ganimoide:~$ sudo gem install sqlite3-ruby [...]
A questo punto l’ambiente RoR è completamente installato e configurato e possiamo procedere alla creazione della nostra prima applicazione.
Per generare la nostra prima applicazione RoR ci serviamo del comando
rails
:
andrea@ganimoide:~/src$ rails demo create create app/controllers create app/helpers create app/models create app/views/layouts create config/environments create config/initializers create config/locales create db create doc create lib ...
Il comando rails
necessita di un solo argomento: il nome della
cartella all’interno della quale vogliamo generare lo scheletro della
nostra applicazione RoR.
Come si vede, il generatore si occupa di creare una struttura adeguata a contenere le varie componenti della nostra applicazione tra cui le viste, i modelli, i controller, i file di configurazione, i file di log, il database.
Il webserver è la componente che si occupa di raccogliere le richieste
http
proveniente dall’esterno, instradarle verso l’applicazione RoR
e trasmettere le risposte ottenute.
RoR supporta diversi webserver tra cui:
In questi nostri primi esperimenti utilizzeremo webrick
il webserver
integrato in RoR. Questa scelta ci offre notevoli vantaggi in termini
di semplicità d’uso. Infatti, per eseguire il webserver e ottenere
immediatamente un ambiente di sviluppo funzionante basterà eseguire lo script
server
:
andrea@ganimoide:~/src/demo$ ruby script/server => Booting WEBrick => Rails 2.3.2 application starting on http://0.0.0.0:3000 => Call with -d to detach => Ctrl-C to shutdown server [2009-04-06 19:57:24] INFO WEBrick 1.3.1 [2009-04-06 19:57:24] INFO ruby 1.8.7 (2008-08-11) [i486-linux] [2009-04-06 19:57:29] INFO WEBrick::HTTPServer#start: pid=11126 port=3000
Nella precedente slide abbiamo visto come eseguire il webserver
webrick
. Riportiamo l’ultima linea di log della slide precedente:
... [2009-04-06 19:57:29] INFO WEBrick::HTTPServer#start: pid=11126 port=3000 ...
Come si vede, webrick
ci informa che il webserver è in ascolto sulla
porta 3000.
A questo punto, eseguendo un qualsiasi browser (ad esempio firefox
),
basterà visitare l’url:
per vedere la nostra prima applicazione in esecuzione.
In realtà, l’applicazione visualizzerà solo la pagina dimostrativa standard.
Tuttavia, lo scheletro ci fornisce un buon punto di partenza per personalizzare il nostro software.
Per creare il nostro primo controller ci serviremo di un’utile funzionalità di RoR: i generatori.
Per mezzo dei generatori è possibile creare in maniera programmatica parte del codice della nostra applicazione.
Per generare un controller di nome Say
eseguiamo:
andrea@ganimoide:~/src/demo$ ruby script/generate controller Say exists app/controllers/ exists app/helpers/ create app/views/say exists test/functional/ create test/unit/helpers/ create app/controllers/say_controller.rb create test/functional/say_controller_test.rb create app/helpers/say_helper.rb create test/unit/helpers/say_helper_test.rb
Come si vede, RoR si occupa di creare automaticamente il file
contenente il codice del controller
(app/controllers/say_controller.rb
) e una serie di file ad esso
correlati.
Visualizzando il contenuto del file say_controller.rb
ci rendiamo
conto che esso è alquanto minimale!
andrea@ganimoide:~/src/demo$ cat app/controllers/say_controller.rb class SayController < ApplicationController end
Il controller Say
così com’è non serve a molto! Infatti, esso non
implementa alcuna funzionalità.
Per dare vita al nostro controller dobbiamo implementare delle
azioni (action).
Le action
sono le risposte del controller a particolari
richieste.
Al fine di instradare nel modo corretto le richieste verso il controller che può soddisfarle, RoR utilizza alcune convenzioni sugli indirizzi.
Immaginiamo di far puntare il nostro browser al seguente indirizzo:
Tale URL verrà interpretato da RoR come una richiesta verso il
controller Say
. In particolare, al controller viene richiesto di
eseguire l’azione hello
.
Nello specifico, hello
è un metodo di istanza del controller
Say
. La nostra prima implementazione ingenua potrebbe essere:
class SayController < ApplicationController def hello end end
Nella precedente slide abbiamo implementato la nostra prima azione. Ora eseguiamo il browser e visitiamo l’indirizzo
http://localhost:3000/say/hello
Come si potrà verificare, il framework ci segnala un errore nel tentativo di visualizzare la pagina. Lo stesso errore è riportato nell’output della console all’interno della quale abbiamo eseguito il webserver.
L’errore in questione ci segnala l’assenza della vista collegata
all’azione che abbiamo richiesto (cioè SayController#hello
).
Per rimediare a questo problema dobbiamo creare il file hello.erb
nella cartella app/view/say
:
<html> <head> <title>Hello Rails!</title> </head> <body> <p>Hello Rails</p> </body> </html>
Terminata la stesura del file hello.erb
provate a ricaricare la
pagina. Se visualizzate il testo ‘Hello Rails!’ allora avete creato la
vostra prima applicazione RoR!
Ma quella di prima è una semplice pagina HTML statica, direte voi. Vale la pena scomodare un framework come RoR per realizzare semplici pagine HTML?
Ovviamente no, il bello deve ancora arrivare. Proviamo ad aggiungere un pizzico di dinamicità alla nostra precedente pagina.
<html> <head> <title>Hello Rails!</title> </head> <body> <p>Hello Rails</p> <p>Data e ora: <%= Time.now %></p> </body> </html>
Come si potrà verificare, la modifica apportata sopra realizza una vista in grado di visualizzare la data e l’ora attuali.
Come avviene la stampa dinamica di queste informazioni? La risposta è
erb
(Embedded Ruby).
In pratica, RoR è in grado di generare viste con all’interno codice ruby.
Il codice ruby viene inserito all’interno della sequenza di caratteri
<%
%>
Nell’esempio il codice in questione è Time.now
. Provate ad eseguirlo
all’interno di una console ruby.
Normalmente, un’applicazione web è costituita da più di una pagina web. Come gestisce RoR il collegamento tra le diverse pagine?
Creiamo una seconda vista goodbye.erb
nella cartella views/say
:
<html> <head> <title>Ciao ciao!</title> </head> <body> <p>A presto! E' stato un piacere conoscerti!</p> </body> </html>
Ora, associamo la visualizzazione della nuova vista all’azione
SayController#goodbye
:
class SayController < ApplicationController def hello end def goodbye end end
Infine, proviamo a visualizzare il risultato della richiesta
say/goodbye
sul browser visitando il link:
Se tutto è andato per il verso giusto dovremmo visualizzare una pagina di commiato.
Come collegare la vista hello.erb
alla vista goodbye.erb
?
Niente di più semplice:
<html> <head> <title>Hello Rails!</title> </head> <body> <p>Hello Rails</p> <p>Data e ora: <%= Time.now %> <p>Ops.. E' già ora di dirci <%= link_to 'ciao..', :action => 'goodbye' %> </body> </html>
Il metodo link_to
eseguito all’interno della sequenza <%
%>
è un
helper method. Esso rappresenta un modo semplice e veloce di
generare collegamenti ad altre viste attraverso le azioni ad esse
associate.
Nella vista hello.erb
visualizziamo la data e l’ora eseguento il
methodo di classe Time::now
.
Dal punto di vista operativo questa procedura non è errata. In effetti, il risultato è quello voluto.
Tuttavia, dal punto di vista concettuale questa non è una procedura corretta.
Richiamando il metodo Time::now
stiamo vincolando la vista ad un
dettaglio implementativo. Se, infatti, volessimo localizzare la
visualizzazione della data e dell’ora in funzione della provenienza
dell’utente, dovremmo aggiungere ulteriori aspetti implementativi
all’interno della vista.
Questa non è una cosa buona, in quanto le viste dovrebbero occuparsi esclusivamente dell’interfaccia da esporre all’utente.
Spostando la logica di funzionamento sul lato controller, possiamo risolvere questo inconveniente:
class SayController < ApplicationController ... def hello @time = Time.now end ... end
Esercizio: Modificate la vista hello.erb
in modo da utilizzare la
nuova implementazione dell’azione SayController#hello
.
Supponete di avere in mente una nuova, sbrilluccicante, funzionalità
da aggiungere alla vostra prima applicazione RoR. Quale sarà la vostra
prima tentazione? Probabilmente quella di modificare il controller
SayController
aggiungendo nuove azioni!
Quindi, da bravi sviluppatori, vi affretterete a testare il corretto funzionamento della nuova funzionalità scrivendo un po’ di codice di test.
Agireste così? No? Tranquilli, sareste in buona compagnia. Questa è infatti la procedura che seguirebbe il 90% degli sviluppatori al mondo [e che di fatto abbiamo utilizzato ed utilizzeremo anche noi per i nostri semplici esempi].
E se invertissimo il processo? Cioè, se prima scrivessimo i test e dopo andassimo ad implementare la nuova funzionalità?
In tal caso staremmo utilizzando una metodologia di sviluppo nota come TDD (Test Driven Development).
Citiamo alcuni dei vantaggi insiti in questo approccio:
Come si traducono in codice le considerazioni fatte nella precedente slides?
Supponiamo di voler aggiungere alla nostra applicazione una nuova
funzionalità. Quando l’utente richiede l’esecuzione dell’azione
hello
, l’applicazione deve essere in grado di produrre un messaggio
di saluto contenente il nome dell’utente autenticato.
Per far ciò è necessario che il controller acquisisca l’informazione sul nome dell’utente. Nella realtà, quest’informazione sarebbe contenuta all’interno della tabella di un database.
Per prima cosa scriviamo il test.
Cosa ci aspettiamo quando eseguiamo l’azione SayController#hello
?
Ci aspettiamo che essa abbia accesso ad una variabile d’instanza user
contenente il nome dell’utente.
Tale variabile non deve contenere il valore nil
:
class SayControllerTest < ActionController::TestCase test "username is not nil" do get :hello assert_not_nil assigns(:user) end end
Il metodo get
effettua la richiesta di esecuzione dell’azione
hello
.
Il metodo assert_not_nil verifica che la variabile user
non contenga
il valore nil
.
Se proviamo ad eseguire il test attraverso il task test
:
andrea@ganimoide:~/src/demo/rake test
Domanda: Vi aspettate che il test abbia successo?
Ovviamente, il test proposto nella precedente slide fallisce perché dobbiamo ancora implementare la nuova funzionalità.
class SayController < ApplicationController ... def hello @user = 'Utente' @time = Time.now end ... end
Nella realtà il nome dell’utente da assegnare alla variabile d’istanza
user
sarebbe immagazzinato all’interno di una tabella di un
database.
Nel nostro esempio, allo scopo di semplificare la trattazione, assegneremo staticamente un valore alla variabile.
La nostra implementazione è corretta? Eseguiamo nuovamente i test per fare una verifica:
andrea@ganimoide:~/src/demo/rake test ... Loaded suite /usr/lib/ruby/gems/1.8/gems/rake-0.8.4/lib/rake/rake_test_loader Started . Finished in 0.062932 seconds. 1 tests, 1 assertions, 0 failures, 0 errors ...
I risultati del test sono positivi. La nostra implementazione è
corretta: la variabile d’istanza user
contiene un valore diverso da
nil
.