Ruby On Rails, noto anche attraverso lโacronimo RoR o con lโabbreviazione Rails, รจ un Web framework rilasciato sotto licenza Open Source (MIT License) e disponibile gratuitamente per il libero utilizzo; esso รจ stato realizzato in linguaggio Ruby con lโobiettivo di velocizzare il lavoro di sviluppo delle applicazioni mettendo a disposizione strutture e librerie pronte allโuso. Uno dei principi cardine che su cui รจ stato basato il lavoro di realizzazione di RoR รจ relativo allโeliminazione delle ripetizioni (metodologia DRY, acronimo di "Do not Repeat Yourself"), per cui, una volta definito uno specifico elemento, tale operazione non dovrร essere eseguita una seconda volta; questa caratteristica diventa particolarmente interessante quando si ha la necessitร di riutilizzare porzioni di codice in diverse componenti progettuali o, addirittura, nella condivisione di tali frammenti di sorgente tra vari progetti.
Rails รจ un framework di tipo full-stack, ciรฒ significa che esso prevede un livello di integrazione tale fra i diversi componenti che non sarร necessario impostare manualmente i collegamenti tra di essi; in linea generale รจ possibile affermare che un framework full-stack viene concepito per coprire tutti i livelli di una Web application, dalla gestione della logica applicativa fino allโinterazione con sorgenti esterne dโinformazioni come per esempio le basi di dati.
RoR รจ stato concepito basandosi sullโarchitettura MVC (Model-View-Controller), una struttura progettuale, o "pattern", appositamente pensata per fornire agli sviluppatori uno schema di tipo generale attraverso il quale risolvere razionalmente le problematiche ricorrenti nellโorganizzazione di unโapplicazione. Il pattern architetturale MVC consiste in pratica nel gestire in modo indipendente gli elementi (dati) associati alle funzionalitร (logica applicativa), da quelli legati alla presentazione (interfaccia utente) e dalle componenti che controllano e adottano le funzionalitร stesse (ancora logica applicativa). Nello specifico, il paradigma MVC prevede la presenza di:
- un Model (modello) che mette a disposizione i metodi attraverso i quali avere accesso alle informazioni manipolabili dallโapplicazione;
- una View (vista) il cui compito รจ quello di rendere visibili le informazioni che fanno riferimento al modello e di consentire lโinterazione con i dati; a partire dalla release numero 2, RoR supporta il MultivVew per la generazione di viste in formati diversi sulla base dal medesimo sorgente, per cui dallo stesso codice sarร possibile ottenere una View in HTML, una in XML, CSV e cosรฌ via (disponibile anche il supporto per il formato dโinterscambio JSON);
- un Controller (controllo) il cui compito consiste nellโintercettare le istruzioni inviate tramite le Views alterando lo stato di modelli e viste.
In Rails il componente Model รจ gestito tramite un apposito modulo denominato ActiveRecords e utilizza una rappresentazione del dato legata al paradigma per oggetti definendone proprietร e relazioni, alle Views fa invece riferimento il modulo ActionView, mentre i Controller sono gestiti tramite modulo ActionController. Il ruolo di tali moduli verrร approfondito nel corso di questa trattazione facendo riferimento alla loro utilitร a livello pratico.
Un altro elemento caratterizzante di RoR sta nel fatto che esso รจ stato dotato di unโarchitettura basata su convenzioni (principio della Convention Over Configuration) per cui, dato un determinato modello che fa riferimento ad una classe, vi sarร sicuramente un elemento omonimo su cui tale classe andrร ad agire o con cui dovrร interagire; per operare al di fuori delle convenzioni, quindi in modo non previsto da queste ultime, sarร invece necessario operare in manualmente sulle configurazioni relative agli elementi che interagiscono con classi e modelli. ROR รจ inoltre multipiattaforma, ciรฒ significa che รจ in grado di funzionare dando luogo ai medesimi comportamenti e risultati indipendentemente dal sistema operativo di riferimento.
Nei prossimi capitoli verranno quindi analizzate le procedure necessarie per la creazione e lโinizializzazione di un ambiente operativo mirato alla realizzazione di un progetto Rails.
Installazione di RoR su Windows
Per utilizzare RoR sui sistemi operativi della Casa di Redmond avrete bisogno innanzitutto di Ruby, infatti il framework lavora e funziona sulla base del linguaggio di programmazione e del suo ambiente; per Windows gli sviluppatori hanno messo a disposizione un apposito installer che permette di automatizzare lโintera procedura dโinstallazione di Ruby, nel caso specifico di questa trattazione รจ stata utilizzala la relase Ruby 1.9.3, cioรจ lโultima disponibile al momento corrente anche se non ancora stabile. Una volta scaricato lโinstaller, questo potrร essere lanciato come un qualsiasi altro eseguibile attraverso un semplice doppio click sul file "rubyinstaller-1.9.3-p125.exe", naturalmente, se utilizzate una versione differente del package il nome dellโapplicazione potrebbe cambiare, ma il prefisso sarร sempre "rubyinstaller-" seguito dal numero di versione. La prima operazione che ci verrร richiesta in installazione sarร quella relativa allโaccettazione della licenza dโutilizzo, lette le clausole potremo procedere e passare al setup vero e proprio.
In questa fase ci verrร richiesto il percorso alla cartella che conterrร tutti i file necessari per il funzionamento di Ruby, inoltre avremo la possibilitร di scegliere se:
- installare il supporto per Tcl/Tk, "Tcl" (Tool Command Language) o "tickle", รจ un linguaggio scripting che viene comunemente utilizzato per prototipizzare applicazioni anche dotate di GUI (Graphical User Interface o Interfaccia Grafica Utente) basate su interpreti (gli engine dei linguaggi) ed effettuarne i test; "Tk" รจ invece estensione che mette a disposizione un set di tools per realizzare GUI anche in combinazione con linguaggi quali Ruby;
- aggiungere gli eseguibili Ruby alla propria PATH di sistema, in questo modo il linguaggio sfrutterร le variabili dโambiente di Windows in modo che la sua installazione sia disponibile da qualsiasi percorso allโinterno del file system; si tratta di unโopzione molto utile da punto di vista pratico, tenete conto perรฒ che se gestite altre installazioni di Ruby sullo stesso terminale queste potrebbero essere influenzate dalla selezione di tale voce.
- permettere di lanciare gli scritps Ruby con un semplice doppio click sui file ".rb" e ".rbw" o semplicemente digitandone il nome da Prompt; anche per questa associazione vale la stessa avvertenza specificata nel punto precedente.
Selezionate le voci di nostro interesse e clickato su "Install", lโeseguibile non farร altro che portare la procedura a conclusione senza alcun intervento dellโutilizzatore; terminata lโinstallazione potremo clickare su "Finish" per abbandonare il Wizard di setup. Ora apriremo il Prompt di Ms Dos e procederemo con lโinstallazione di RoR come RubyGem; le RubyGems fanno riferimento ad un gestore di librerie pacchettizzate per Ruby che rappresentano uno standard (Gems) per la distribuzione di applicazioni scritte in Ruby. Per installare una "Gem" bisognerร lavorare su un Pc connesso ad Internet e lanciare il comando "gem install" seguito dal nome del programma che si desiderato; per cui, nel caso specifico di RoR, dovremo digitare la seguente istruzione:
gem install rails
Il gestore di packages procederร autonomamente allโinstallazione, la procedura prevista potrebbe perรฒ richiede un tempo piรน o meno lungo a seconda delle risorse disponibili sul sistema e, naturalmente, della larghezza della banda Internet.
Conclusa anche la fase di installazione di RoR, potremo creare il nostra primo progetto Rails in un percorso a scelta; se avete aperto il Prompt sulla directory dโinstallazione di Ruby, potreste per esempio creare una cartella denominata "project" allโinterno della quale salvare lโapplicazione generata, come nellโesempio seguente:
C:Ruby193>md project
C:Ruby193>rails new project/my_app
Il primo comando creerร la directory in cui memorizzare le cartelle di progetto, mentre lโistruzione "rail new", seguita dal nome del progetto completo di percorso, genererร tutti i file e le cartelle per il suo funzionamento e la sua implementazione.
Installazione di RoR su Linux
Fondamentalmente, le procedure per la creazione di un ambiente di sviluppo basato su RoR in Linux variano sulla base della distribuzione utilizzata; Ubuntu รจ una delle distro piรน diffuse tra quelle basate sul Kernel del Pinguino, per cui sarร il sistema operativo di riferimento per il capitolo corrente, le procedure descritte saranno perรฒ valide anche per Debian, varianti di Debian e per tutte le distribuzioni che si basano su questโultima. Prima di passare alla fase vera e propria dellโinstallazione, sarร bene lanciare da Terminale il comando di aggiornamento dei packages disponibili sul sistema in modo da essere sicuri di lavorare su quelli piรน aggiornati, lโistruzione richiesta sarร quindi la seguente:
$ sudo apt-get update
Ci verrร richiesta la password utente e, una volta digitata questโultima e clickato su [Invio], la restante procedura di aggiornamento verrร eseguita automaticamente. Terminato lโupdate, potremo digitare, sempre da Terminale, lโistruzione per lโinstallazione di Ruby:
$ sudo apt-get install ruby
Il sistema si occuperร di ricercare la versione piรน recente (e stabile) del linguaggio e a rilevare le dipendenze necessarie per il buon esito dellโinstallazione; fatto questo, ci verrร richiesto di confermare il proseguimento della procedura. Verranno quindi scaricati dai repository tutti i package necessari e non sarร richiesto un ulteriore intervento da parte dellโutente sino alla conclusione delle fasi richieste; la presenza di una precedente installazione di Ruby non dovrebbe dare luogo ad alcun conflitto.
Se tutto dovesse andare per il meglio, si potrร procedere con la creazione dellโambiente Rails tramite RubyGems, ma prima di fare questo sarร necessario verificare che questโultimo sia stato installato attraverso il seguente comando:
$ sudo apt-get remove --purge rubygems
Se RubyGems non รจ presente, il sistema risponderร con la seguente notifica:
Il pacchetto rubygems non รจ installato e quindi non รจ stato rimosso.
Si dovrร quindi procedere con lโinstallazione delle RubyGems digitando lโistruzione:
$ sudo apt-get install rubygems
Anche in questo caso il sistema procederร automaticamente fino al termine della procedura scaricando ed installando tutti i package necessari.
Una volta rese disponibili anche le RubyGems, si potrร passare allโinstallazione di RoR tramite il comando:
$ sudo gem install rails
Questa sarร con tutta probabilitร la fase che richiederร piรน tempo in assoluto per essere completata, i pacchetti da scaricare sono infatti molti e se, come capita non di rado, avete utilizzato Ubuntu per dare nuova vita ad un Pc datato o disponete di una connessione lenta, essa potrebbe richiedere qualche minuto prima della conclusione. Terminata anche questa procedura, รจ consigliabile installare unโulteriore Gem, il Bundler, esso infatti mette a disposizione unsistema di gestione delle Gems nei progetti basati su Rails:
$ sudo gem install bundler
A questo punto, lโambiente di sviluppo per la realizzazione applicazioni Rails sarร pronto per lโutilizzo, si potrร quindi dar vita ad un primo progetto creando una cartella nel quale salvarlo e lanciando in comando "rails new":
$ mkdir project
$ rails new project/my_app
Fatto questo, avremo un progetto RoR pronto per la creazione della nostra applicazione.
Installazione di RoR su Mac Os X
Come sottolineato nel capitolo riguardante lโinstallazione sulle distribuzioni Linux, anche la procedura richiesta per creare un ambiente Rails in Mac Os X puรฒ variare a seconda della versione corrente del sistema operativo; le operazioni descritte di seguito sono state eseguite su Os X Lion ma dovrebbero essere valide anche per Snow Leopard.
La prima operazione da compiere sarร qualla di scaricare ed installare Xcode, la nota IDE (Integrated development environment, "ambiente di sviluppo integrato") creata dai laboratori della Casa di Cupertino per lo sviluppo di applicazioni destinate a funzionare su Mac OS X e iOS. Tale componente non รจ richiesto per le features messe a disposizione, ma per le librerie in dotazione con Xcode, come per esempio il compilatore multi-target GCC (GNU Compiler Collection) necessario per Ruby. Xcode viene fornito in bundle con il sistema operativo Apple a partire dalla versione Mac OS X 10.3 (Panther), ma se non lo avete installato potrete scaricarlo gratuitamente dalla sezione dedicata ai developers del sito Internet di Cupertino. Per installarlo, una volta eseguito il download, sarร sufficiente un doppio click sullโicona del package.
Una volta installato Xcode, dovrete dotarvi di un package manager se non ne avete uno giร presente nel vostro sistema; dato che Apt-get non รจ piรน disponibile per Os X Lion, potrete ricorrere ad unโalternativa altrettanto valida denominata Homebrew, un gestore appositamente concepito per le applicazioni rilasciate sotto licenza Open Source. Per poter funzionare Homebrew richiede, oltre ad Xcode, anche il Java Developer Update; una volta configurato lโambiente tramite i requisiti richiesti, lโinstallazione del manager potrร essere effettuata tramite il seguente comando:
/usr/bin/ruby -e "$(/usr/bin/curl -fksSL https://raw.github.com/mxcl/homebrew/master/Library/Contributions/install_homebrew.rb)"
Il prossimo passaggio richiederร lโinstallazione di Git (se assente sul sistema), cioรจ lโormai diffusissimo sistema distribuito per il controllo di versione dei codici sorgenti realizzato da Linus Torvalds, il papร di Linux; tale procedura prevederร la digitazione della seguente istruzione:
brew install git
Da notare che per installare una qualsiasi applicazione tramite Homebrew รจ richiesto il comando "brew install" seguito dal nome del package desiderato. Ora che anche Git รจ disponibile, per poter procedere sarร prima necessario agire sul proprio profilo bash editando il file ".bash_profile" (o creandolo se insesistente) e aggiungendo in esso la seguente stringa:
export PATH=$PATH:/usr/local/git/bin/
Quindi, una volta salvate le modifiche effettuate, bisognerร procedere con unโulteriore installazione, quella di RVM (Ruby Version Manager o Ruby enVironment (Version) Manager), uno strumento Open Source, utilizzabile da linea di comando, che permette di semplificare le operazioni di installazione, gestione e aggiornamento di ambienti Ruby (anche multipli). Per dotare la nostra piattaforma di RVM dovremo lanciare il seguente comando:
bash -s stable < <(curl -s https://raw.github.com/wayneeseguin/rvm/master/binscripts/rvm-installer)
Fatto questo bisognerร poi seguire le istruzioni dettate dal sistema per il completamento dellโinstallazione; non rimarrร quindi che installare Ruby e RoR con unโunica sequenza di comandi:
rvm install 1.9.3
rvm --default 1.9.3
gem update
gem install rails
Da notare lโopzione "โdefault" che permette di impostare come predefinita una specifica installazione di Ruby nel caso essa non sia lโunica presente sul sistema. Come in Windows e in Linux, potremo ora generare un primo progetto Rails attraverso il comando "rails new" salvandolo in una cartella a nostra scelta:
mkdir project
ails new project/my_app
La procedura dโinstallazione di RoR su Mac Os X รจ probabilmente la piรน articolata tra quelle presentate fino ad ora, una volta terminata con successo essa permetterร perรฒ di sviluppare progetti Rails con la medesima efficacia.
Inizializzazione dellโambiente operativo
Ora che abbiamo installato Ruby, RoR e creato il nostro primo progetto basato su Rails, potremo passare alla fase relativa allโinizializzazione del nostro ambiente di produzione; i comandi che verranno utilizzati nel corso di questa trattazione sono indipendenti dal sistema operativo adottato, per cui, anche se gli esempi proposti faranno riferimento a Windows, essi potranno essere riprodotti fedelmente sia su Linux che su Mac Os X. Prima di procedere con qualsiasi altra operazione consiglio perรฒ di effettuare un controllo di versione verificando che la release di RoR corrente sia quella piรน aggiornata, per far questo sarร sufficiente lanciare lโistruzione:
update rails
Se non dovessero essere disponibili ulteriori upgrade, il sistema risponderร inviando al Prompt la notifica:
Nothing to update
diversamente verrร segnalata la disponibilitร di una nuova versione e sarร possibile procedere con lโaggiornamento di RoR.
A questo punto si potrร lanciare il server di RoR sul progetto predecedentemente generato, per cui ci sposteremo da terminale sulla relativa cartella e poi digiteremo il comando rails server per lโinzializzazione:
C:>cd Ruby193projectmy_app
C:Ruby193projectmy_app>rails server
=> Booting WEBrick
=> Rails 3.2.3 application starting in development on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
[2012-04-19 12:20:10] INFO WEBrick 1.3.1
[2012-04-19 12:20:10] INFO ruby 1.9.3 (2012-02-16) [i386-mingw32]
[2012-04-19 12:20:10] INFO WEBrick::HTTPServer#start: pid=2836 port=3000
Il sistema reagirร allโistruzione attivando innanzitutto WEBrick, che รจ in pratica la libreria di Ruby che mette a disposizione un basilare servizio HTTP Web server per le applicazioni; in secondo luogo verrร resa operativa la "porta dโascolto" per le chiamate provenienti dal client, questa sarร di default la "3000" e funzionerร esattamente come quella utilizzata da altri Web server (si pensi per esempio ad Apache) generalmente in "attesa" delle richieste sulla porta "8080".
Da notare che, tra le notifiche relative allโavvio del server RoR, ve nโรจ anche una che segnala il comando necessario per lโarresto (shutdown) del servizio, esso consiste nella combinazione di tasti [Ctrl]+[C]. Ho notato perรฒ che alcune versioni di Rails non producono il comportamento atteso da questa istruzione, facendo si che il server continui a rimanere attivo; fortunatamente esiste comunque una combinazione di tasti alternativa, [Ctrl]+[Pause/Break], che permetterร di arrestare il Rails server nellโeventualitร che [Ctrl]+[C] non dovesse funzionare.
Adesso che il servizio รจ attivo, sarร possibile avviare il nostro browser Internet preferito e digitare nella barra degli indirizzi la seguente URL:
http://localhost:3000
Se tutto dovesse andare per il meglio il Rail server metterร a disposizione la seguente pagina Web:
Eโ utile sottolineare che il Rails server terrร traccia della chiamate da parte del client, infatti, se avete lasciato aperto il terminale, noterete la comparsa di alcuni messaggi simili ai seguente:
Started GET "/assets/rails.png" for 127.0.0.1 at 2012-04-19 12:42:45 +0200
Served asset /rails.png - 304 Not Modified (2ms)
Started GET "/assets/rails.png" for 127.0.0.1 at 2012-04-19 12:43:19 +0200
Served asset /rails.png - 200 OK (0ms)
Attraverso di essi viene in pratica notificato il caricamento del file"rails.png", esso non รจ altro che lโimmagine del logo di RoR presente nella pagina Internet precedentemente caricata. Una volta attivato lโambiente operativo lanciando il server Rails, sarร possibile passare alla fase relativa alla creazione di unโapplicazione RoR.
RoR e database: installare MySQL
Ruby On Rails รจ stato sviluppato implementando con particolare attenzione le funzionalitร destinate allโinterazione del framework con le basi di dati e le informazioni in esse archiviate; tra i numerosi DBMS supportati da RoR, per citare soltanto alcuni di quelli rilsciati sotto licenza Open Source, รจ possibile fare riferimento a MySQL, SQLite e PostgreSQL. Dato che il Database Manager di riferimento per questa trattazione sarร MySQL, la prima operazione da compiere sarร quella relativa allโinstallazione della Gem necessaria per il dialogo con questa applicazione; una volta aperto il terminale dovremo quindi digitare la seguente istruzione che potrร essere lanciata da qualsiasi percorso:
gem install mysql
In alternativa (consigliata) รจ possibile installare la Gem "mysql2", una libreria veloce e molto stabile per lโinterazione con MySQL:
gem install mysql2
Fatto questo, dovremo recarci sulla directory dโinstallazione di RoR e raggiungere la cartella "project/my_app/config/" precedentemente creata tramite il comando "rails new", in essa รจ presente il file "database.yml"; aprendolo con un editor di testo (รจ sufficiente il Blocco Note) noteremo come di default il progetto presenti una configurazione relativa al DBMS SQLite:
# SQLite version 3.x
# gem install sqlite3
#
# Ensure the SQLite 3 gem is defined in your Gemfile
# gem 'sqlite3'
development:
adapter: sqlite3
database: db/development.sqlite3
pool: 5
timeout: 5000
# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
adapter: sqlite3
database: db/test.sqlite3
pool: 5
timeout: 5000
production:
adapter: sqlite3
database: db/production.sqlite3
pool: 5
timeout: 5000
Questโultima dovrร quindi essere modificata in modo da utilizzare MySQL come DBMS predefinito, prima di fare questo sarร perรฒ necessario fare riferimento ai tre ambienti previsti da RoR per lโinterazione con le basi di dati:
- "development": รจ lโambiente destinato allo sviluppo locale dellโapplicazione prima delle fasi di test e di produzione (messa on-line);
- "test": รจ lโambiente utilizzabile in seguito alla fase di sviluppo per lโesecuzione dei test automatizzati sullโapplicazione prima della fase di produzione;
- "production": รจ lโambiente a cui farร riferimento lโapplicazione una volta resa pubblicamente disponibile come servizio.
Per modificare il file "database.yml" in modo che questo interagisca con MySQL, potremo quindi utilizzare delle nuove impostazioni di configurazione sul modello delle seguenti:
development:
adapter: mysql2
encoding: utf8
database: my_app_development
pool: 5
username: nome_utente
password: password_utente
host: localhost
port: 3306
socket: /tmp/mysql.sock
test:
adapter: mysql2
encoding: utf8
database: my_app_test
pool: 5
username: nome_utente
password: password_utente
host: localhost
port: 3306
socket: /tmp/mysql.sock
production:
adapter: mysql2
encoding: utf8
database: my_app_production
pool: 5
username: nome_utente
password: password_utente
host: localhost
port: 3306
socket: /tmp/mysql.sock
Per tutti e tre gli ambienti di riferimento sarร quindi necessario indicare:
- Lโadapter, nel nostro caso "mysql2" che permetterร lโinterazione con il DBMS;
- lโencoding (codifica di caratteri) Unicode relativo ai dati manipolati, nel nostro caso verrร utilizzato UTF-8;
- il nome del database che dovrร essere diverso per ciascun ambiente;
- il numero massimo (pool) delle connessioni indirizzabili al Database Manage per ciascun thread (processo)r;
- la username relativa allโutente attraverso il quale lโapplicazione interagirร con il DBMS e la password necessaria per lโautenticazione dellโutente stesso;
- lโhost di MySQL, cioรจ il nome della macchina che ospita il servizio che รจ generalmente "localhost" nelle istallazioni locali;
- la "porta dโascolto" utilizzata dal Database Manager per attendere le richieste dai client, "3306" รจ la porta predefinita assegnata in installazione;
- il percorso al socket per la connessione con MySQL.
Una volta effettuate e salvate le modifiche richieste (ricordiamoci di modificare il parametri associati a "username" e "password" con quelli relativi alla nostra installazione di MySQL), RoR ci permetterร di creare i database di sviluppo e test precedentemente definiti da terminale. Per fare ciรฒ utilizzeremo unโistruzione basata sulla Gem "rake", per prima cosa, quindi, dovremo installare questโultima da un qualsiasi percorso sul file system:
gem install rake
Rake รจ in pratica un "command runner", cioรจ un tool che ci permetterร di accedere velocemente a comandi Rails utili per lo sviluppo della nostra applicazione; ora finalmente potremo lanciare il comando per la generazione delle basi di dati dal percorso della nostra applicazione:
C:Ruby193projectmy_app>rake db:create
Ottenuti i database desiderati (per il momento ancora vuoti), sarร possibile procedere con la creazione di una prima applicazione basata su Rails.
Creiamo la nostra prima Web Application con RoR
Per il funzionamento di una semplice applicazione basata su Ruby On Rails sono necessari almeno due elementi, cioรจ un Controller e una View; la generazione di tali componenti puรฒ essere effettuata attraverso una singola istruzione da linea di comando basata sul comando "rails generate":
C:Ruby193projectmy_app>rails generate controller home index
la quale produrrร il seguente output:
create app/controllers/home_controller.rb
route get "home/index"
invoke erb
create app/views/home
create app/views/home/index.html.erb
invoke test_unit
create test/functional/home_controller_test.rb
invoke helper
create app/helpers/home_helper.rb
invoke test_unit
create test/unit/helpers/home_helper_test.rb
invoke assets
invoke coffee
create app/assets/javascripts/home.js.coffee
invoke scss
create app/assets/stylesheets/home.css.scss
Lโistruzione porterร alla generazione del Controller e della Home Page relativi allโapplicazione; da notare (come risulta evidente dallโoutput) che in questa fase verranno invocate una serie di classi destinate a strutturare lโapplicazione generata:
- erb: mette a disposizione un template system il cui compito รจ nel caso specifico la generazione della home come elemento front end della view;
- test_unit: fornisce gli strumenti per la progettazione, il debug e la valutazione del codice sorgente e del suo funzonamento;
- helper: gli helpers sono sostanzialemente delle porzioni di codice, o snippets, che potranno essere richiamati per evitare ripetizioni allโinterno del sorgente;
- assets: consentono di comprimere e "minificare" le componenti JavaScript e CSS perchรฉ incidano meno pesantemente sui tempi di caricamento della Web application; nello specifico vengono utilizzati il linguaggio CoffeeScript per snellire i sorgenti degli script JS e lโestensione SCSS (Sass CSS) per la semplificazione della sintassi nelle regole di stile.
A ben vedere, il risultato del comando precedentemente inviato non รจ altro che una applicazione Web completa, ha infatti una Home Page, un foglio di stile per la formattazione, un file JavaScript per le funzionalitร client side e una base per la realizzazione di un template; mancano quindi soltanto i contenuti da gestire.
Riguardo allโultimo punto indicato, sarร possibile intervenire immediatamente editando con un qualsiasi editor testuale il file "index.html.erb" presente sul percorso "app/views/home/" della nostra applicazione, esso รจ in pratica il template che permetterร di visualizzare gli output derivanti dalle chiamate al metodo "index" nel controller "home"; al suo interno potremo inserire direttamente del markup HTML come nellโesempio seguente:
<h1>La mia prima applicazione con Rails</h1>
Fatto questo, dovremo recarci (da terminale o manulamente) sulla cartella "public" della nostra applicazione ed eliminare (o rinominare) il file "index.html" che รจ la Home Page di default della nostra installazione di RoR, quella intitolata "Welcome Aboard" per intenderci.
Ora che non sarร piรน disponibile lโindex predefinito per la Web application, dovremo definirne uno noi e, per far questo, sarร necessario editare il file "routes.rb" presente nella cartella "config" della nostra applicazione; esso รจ in pratica un "routing file", perchรฉ indica al Rails server verso quale pagina devono essere instradate le richieste dirette alla Home e ad altre pagine.
Tra le varie istruzioni presenti in "routes.rb" รจ contenuta anche la seguente stringa:
# root :to => 'welcome#index'
Essa di default indica che lโHome Page dellโapplicazione dovrร esse la pagina di benvenuto prodotta da Rails nel momento in cui รจ stata generata lโapplicazione; per stabilire il nuovo file su cui redirigere le richieste dovremo quindi modificare quanto scritto sostituendo "welcome" con "home" e decommentare la riga eliminando il simbolo del cancelletto ("#") posto dinnanzi ad essa:
root :to => 'home#index'
Ora potremo aprire il nostro browser Web preferito e digitare nella barra degli indirizzi la seguente URL:
http://localhost:3000/
Se tutto dovesse andare per il meglio, al posto della "Welcome page" o della notifica di errore per la sua eliminazione dovremmo visualizzare una pagina bianca contenente la stringa "La mia prima applicazione con Rails". La nostra Web application Rails comincia quindi a prendere forma, ma nel corso dei prossimi capitoli diventerร via via piรน aticolata.
Generare codice con lo Scaffolding
In Ruby On Rails tutto รจ stato concepito per essere piรน immediato, piรน comodo e meno macchinoso per lo sviluppatore; perchรฉ scrivere migliaia di righe di codice sorgente quando questo compito puรฒ essere svolto da delle librerie? Unโulteriore conferma di tale caratteristica del framework รจ data dal supporto di questโultimo ad un meccanismo, denominato Scaffolding, per la generazione automatica del codice.
"Scaffolding" non รจ un termine facilmente traducibile con una corrispondente parola in Italiano ("impalcatura"?), questo perchรฉ esso fa riferimento a numerosi ambiti (รจ presente persino in psicologia!); per quando riguarda lo sviluppo di applicazioni per Internet, รจ possibile definire lo Scaffolding come una tecnica propria dei framework Model-View-Controller, attraverso la quale lโutilizzatore ha la possibilitร di definire delle specifiche sullโimpiego di una base di dati da parte di un programma; un compilatore si occuperร poi di generare il sorgente necessario allโapplicativo per lโesecuzione di operazioni CRUD (create, read, update & delete) a carico dei record manipolati.
Per proporre un esempio pratico riguardante lโutilizzo dello Scaffolding, รจ possibile procedere con la specifica di una struttura di dati che preveda di associare alla risorsa "New" alcuni fields:
- lโautore del contenuto, un dato di tipo stringa;
- il titolo del contenuto, anchโesso di tipo stringa;
- il testo, quindi il contenuto stesso, dato di tipo text.
Il comando necessario per la creazione della risorsa "New" dovrร basarsi sullโistruzione "rails generate scaffold" seguita dal nome della stessa e dai campi relativi ad essa; una volta aperto il terminale Prompt dovremo quindi digitare quanto segue:
C:Ruby193projectmy_app>rails generate scaffold New autore:string titolo:string testo:text
Lโesecuzione dellโistruzione porterร alla visulizzazione del seguente output:
invoke active_record
create db/migrate/20120425095451_create_news.rb
create app/models/new.rb
invoke test_unit
createtest/unit/new_test.rb
createtest/fixtures/news.yml
route resources :news
invoke scaffold_controller
create app/controllers/news_controller.rb
invoke erb
createapp/views/news
createapp/views/news/index.html.erb
createapp/views/news/edit.html.erb
createapp/views/news/show.html.erb
createapp/views/news/new.html.erb
createapp/views/news/_form.html.erb
invoke test_unit
createtest/functional/news_controller_test.rb
invoke helper
createapp/helpers/news_helper.rb
invoketest_unit
create test/unit/helpers/news_helper_test.rb
invoke assets
invoke coffee
createapp/assets/javascripts/news.js.coffee
invoke scss
createapp/assets/stylesheets/news.css.scss
invoke scss
identical app/assets/stylesheets/scaffolds.css.scss
Sulla base di quanto proposto, si tenga presente che quella che si sta effettuando non รจ la procedura per il popolamento di un database (cosa che avverrร in seguito), anche se la natura delle operazioni svolte potrebbe farlo pensare, ma di una struttura di file.
Infatti, il comando per la generazione dello Scaffold produrrร quanto segue in esecuzione:
- "db/migrate/20120424140329_create_news.rb": si tratta di un file denominato migration, esso verrร utilizzato per la creazione della tablella che dovrร essere inserita nel database; ogni "migration" viene marcata inserendo nel nome del file corrispondente un timestamp che la renda identificabile univocamente;
- "app/models/new.rb": รจ il Model della risorsa "New";
- "test/unit/new_test.rb": รจ la routine per lโUnit Test associata al Model;
- "test/fixtures/news.yml": associa dei dati generici, utilizzabili a scopo di test, ai campi precedentementemente definiti;
- "app/controllers/news_controller.rb": รจ il controller della risorsa "New";
- "app/views/news": รจ la cartella destinata al salvataggio delle Views;
- "app/views/news/index.html.erb": รจ una View con funzione di index per i contenuti;
- "app/views/news/edit.html.erb": la View per lโediting dei contenuti gestiti;
- "app/views/news/show.html.erb": la View per la visualizzazione dei singoli contenuti;
- "app/views/news/new.html.erb": la View per la visualizzazione di un nuovo contenuto;
- "app/views/news/_form.html.erb": รจ uno schema basilare di modulo utilizzabile come interfaccia per la gestione dei contenuti;
- "test/functional/news_controller_test.rb": รจ una routine per lโesecuzione di test sulle funzionalitร associate al Controller;
- "app/helpers/news_helper.rb": contiene le funzionalitร helper per le Views dei contenuti;
- "test/unit/helpers/news_helper_test.rb": รจ il file di Unit Test per gli helper;
- "app/assets/javascripts/news.js.coffee": il file JavaScript del Controller;
- "app/assets/stylesheets/news.css.scss": il foglio di stile associato al Controller;
- "app/assets/stylesheets/scaffolds.css.scss": รจ il foglio contenente il CSS associato alle Views prodotte dallo Scaffolding.
Uno Scaffold puรฒ essere cancellato del tutto attraverso una semplice istruzione basata sul comando "rail destroy scaffold" seguita dal nome della risorsa che si vuole eliminare; nel nostro caso lโistruzione richiesta sarร per esempio:
C:Ruby193projectmy_app>rails destroy scaffold New
Se invece si desidera proseguire nello sviluppo di unโapplicazione Rails sulla base dello Scaffold generato, il prossimo passaggio dovrร essere quello relativo alla popolazione del database sulla base delle specifiche definite attraverso di esso.
Creare tabelle con le migration
Grazie allโutilizzo dello Scaffolding, nel capitolo precedene abbiamo generato un file denominato "20120424140329_create_news.rb", il suo nome รจ composto da un timestamp che ne certifica il momento della generazione e da un riferimento alla risorsa Scaffold, nel caso del nostro esempio alla risorsa "New" corrisponde il riferimento "news"; aprendo "20120424140329_create_news.rb" si potrร osservare allโinterno di esso una struttura come la seguente:
class Createnews < ActiveRecord::Migration
def change
create_table :news do |t|
t.string :autore
t.string :titolo
t.text :testo
t.timestamps
end
end
end
Nel contesto di unโapplicazione RoR tale file costituisce una "migration", esso contiene infatti una classe che consente di creare la tabella definita nelle specifiche dello Scaffold e i relatvi campi; ricordate il command runner "Rake"? Bene, ora lo riutilizzaremo per eseguire lโoperazione necessaria alla "migrazione", cioรจ alla trasformazione delle specifiche precedentemente definite in istruzioni per la generazione di una tabella di database:
C:Ruby193projectmy_app>rake db:migrate
== CreateNews: migrating ================================================
-- create_table(:news)
-> 0.0811s
== CreateNews: migrated (0.0820s) =======================================
Come impostazione predefinita, Rails lavora prendendo quale riferimento lโambiente di sviluppo, per cui lโistruzione appena lanciata si occuperร di creare la tabella nel database indicato allโinterno della sezione "development" definita nel file "database.yml"; opzionalmente sarร possibile decidere di generare la tabella allโinterno di un altro database, il comando seguente permetterร per esempio di eseguire tale operazione a carico del database di test:
rake db:migrate RAILS_ENV=test
Tornando allโoperazione di migrazione precedentemente conclusa, interrogando MySQL tramite Prompt si potrร effettuare una veloce verifica riguardante il buon esito delle istruzioni lanciate; cominciamo quindi con il visualizzare le tabelle presenti allโinterno del database per lo sviluppo ("my_app_development"):
mysql> use my_app_development
Database changed
mysql> show tables;
+------------------------------+
| Tables_in_my_app_development |
+------------------------------+
| news |
| schema_migrations |
+------------------------------+
2 rows in set (0.00 sec)
La tabella "news", relativa alla risorsa "New" specificata tramite lo Scaffolding, รจ dunque presente nellโarchivio; ora effettuaremo lo stesso controllo a carico dei campi:
mysql> show columns from news;
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| autore | varchar(255) | YES | | NULL | |
| titolo | varchar(255) | YES | | NULL | |
| testo | text | YES | | NULL | |
| created_at | datetime | NO | | NULL | |
| updated_at | datetime | NO | | NULL | |
+------------+--------------+------+-----+---------+----------------+
6 rows in set (0.01 sec)
Grazie a questa verifica รจ possibile rendersi conto della potenza di Rails nelle operazioni di interazione con le basi di dati e, nel caso specifico, con il DBMS MySQL; si puรฒ notare infatti come:
- il framework abbia generato autonomamente un campo per lโidentificatore univoco ("id"), intero, chiave primaria e auto_incrementale da associare a ciascun record; ciรฒ รจ avvenuto anche se tale campo non era stato precedentemente definito nelle specifiche dello Scaffolding;
- Rails ha attribuito, sempre in modo automatico, il tipo di dato VARCHAR ai campi "autore" e "titolo" che erano stati specificati come "string" nello Scaffold;
- sono stati generati, senza la necessitร di alcun intervento da parte dello sviluppatore, i campi "crated_at" ("creato il..") e "updated_at" ("aggiornato il..") associati al tipo di dato "DATETIME"; grazie ad essi sarร possibile disporre di un riferimento temporale per ciascun record che verrร registrato o manipolato tramite lโapplicazione.
Dalla struttura della tabella appena generata comincia ad emergere la natura CRUD del nostro applicativo che continua a prendere forma; tutte le funzionalitร per la manipolazione e la visualizzazione dei dati saranno utilizzabili via browser Web, quello che otterremo alla fine del nostro lavoro sarร un semplice esempio di News Manager basato su Rails.
Attivazione dellโapplicazione e routing
Fino ad ora abbiamo configurato un ambiente integrato per lo sviluppo di applicazioni Rails, creata una prima applicazione, generati i database, realizzata la struttura dei dati e dei file con lo Scaffolding e popolate le basi di dati con tabelle e campi. Tutte queste operazioni sono state eseguite da Prompt o terminale con un unico intervento manuale a livello di codice sorgente, la digitazione di una stringa delimitata dai tags "<h1></h1>" allโinterno del file "index.html.erb" situato sul percorso "app/views/home"; ora riapriremo quel file e, dopo la stringa iniziale inseriremo il codice necessario per lโinserimento di un link, concluse le modifiche il codice di pagina dovrebbe presentarsi in questo modo:
<h1>La mia prima applicazione con Rails</h1>
<%= link_to 'News manager', news_index_path %>
"link_to" รจ un metodo messo a disposizione nativamente da RoR sotto forma di helper, esso ha la funzione di generare un collegamento ipertestuale che abbia come descrizione il primo parametro passato gli come argomento (nel nostro caso "News manager"), mentre avrร come destinazione la risorsa specificata nel secondo parametro che, nellโesempio, รจ il percorso relativo allโindice delle news gestite tramite lโapplicazione. Una volta salvate le modifice apportate, non sarร necessario riavviare il server Rails per confermarle, infatti in fase di sviluppo questโultimo sarร in grado di ricaricare le pagine Web prodotte dallโapplicazione ad ogni nuova richiesta da parte del client; apriamo quindi il nostro browser e digitiamo lโindirizzo:
http://localhost:3000/
LโHome Page dellโapplicazione dovrebbe essere simile a quella reppresentata nellโimmagine sottostante:
Ora clickiamo sul link "News manager" prededentemente inserito, in questo caso dovrebbe aprirsi una pagina come la seguente:
In tale pagina verranno elencati, quando presenti, tutti i valori inseriti allโinterno della tabella del database incolonnati sotto i relativi campi, il link inserito sulla pagina porterร invece al form per lโinserimento dei nuovi record; di sicuro la descrizione "New New" per il collegamento (che significa in pratica "Inserisci una nuova notizia") non รจ il masssimo, ma in seguito vedremo come sia facile tradurre, formattare e adattare ogni elemento delle pagine Web con una semplice azione di editing a carico delle Views.
Avrete notato che il framework รจ in grado di generare e riconoscere automaticamente i percorsi che collegano le varie pagine; nella modifica apportata alla Home Page abbiamo dovuto inserire soltanto un link verso la risorsa che consente la visualizzazione dei contenuti, per il resto tutti gli altri collegamenti verrano messi a disposizione nativamente dallโapplicazione senza un nostro intervento. Ciรฒ รจ possibile per via del fatto che le regole di routing per lโinstradamento delle richieste vengono generate al momento dello Scaffolding, per avere una loro panoramica completa รจ possibile digitare da Prompt il seguente comando:
rake routes
Questo il risultato prodotto:
news_index GET /news(.:format) news#index
POST /news(.:format) news#create
new_news GET /news/new(.:format) news#new
edit_news GET /news/:id/edit(.:format) news#edit
news GET /news/:id(.:format) news#show
PUT /news/:id(.:format) news#update
DELETE /news/:id(.:format) news#destroy
home_index GET /home/index(.:format) home#index
root / home#index
Analizzando quanto stampato in output troviamo un mapping completo della struttura applicativa, per cui ad ogni percorso corrisponderร un metodo HTTP, unโazione e una determinata funzionalitร :
- metodi GET e POST per il percorso "/news" che ha come azioni "index" o "create" e come funzione quella di visualizzare lโelenco delle news o quello di crearne una nuova;
- metodo GET per il percorso "/news/new" che ha come azione "new" e il compito di generare un form HTML per lโinserimento di ulteriori news;
- metodo GET per il percorso "/news/:id/edit" che ha come azione "update" e il compito di generare un form HTML per lโaggiornamento di un record;
- metodi GET, PUT e DELETE per il percorso "/news/:id" che ha come azioni quelle di mostare, aggiornare e cancellare un determinato contenuto;
- metodo GET per il percorso "home/index" che ha il compito di indicizzare i contenuti in home;
- percorso "/", Root del Reails server.
Durante lo Scaffolding Rails crea automaticamente degli helper in grado di restituire il percorso relativo ad una determinata risorsa, per cui, ad esempio, "news_index_path" sarร una costante che avrร come valore "/news", mentre "new_news_path" corrisponderร a "/news/new".
Ora che sappiamo come "muoverci" tra i percorsi della nostra applicazione, potremo passare alla parte che riguarda il suo utilizzo, cominciando con il descrivere una prima modalitร per lโinserimento dei contenuti.
Validazione dei parametri e Rails console
A questo punto abbiamo unโapplicazione Rails completa che ci permetterร di visualizzare, inserire, modificare e cancellare i dati presenti nella tabella di sviluppo; essa non prevede perรฒ alcun controllo sugli input, per cui potremo colmare questa mancanza agendo sul Model. Nel caso specifico il file da modificare รจ "new.rb", raggiungibile dal percorso "app/models/" interno alla cartella della nostra applicazione; in esso troveremo poche righe di codice:
class New < ActiveRecord::Base
attr_accessible :autore, :testo, :titolo
end
La classe contenuta non fa altro che presentare i nomi dei campi definiti nel corso dello Scaffolding, quindi, potremmo per sempio validare i parametri "autore", "titolo" e "testo" assegnando ad ognuno di essi una lunghezza minima o massima, per far questo dovremo editare il contenuto di "new.rb" nel modo seguente:
class New < ActiveRecord::Base
attr_accessible :autore, :testo, :titolo
validates :autore, :length => { :minimum => 5 }
validates :titolo, :length => { :maximum => 10 }
validates :testo, :length => { :maximum => 500 }
end
Una volta effettuate e salvate le modifiche desiderate potremo metterle alla prova; un sistema rapido per i test di inserimento รจ rappresentato dalla "console", una funzionalitร che permette di lanciare istruzioni da linea di comando per lโinterazione con unโapplicazione; avviare la console รจ semplice, basterร richiamarla da terminale:
C:Ruby193projectmy_app>rails console
Ora, proveremo ad inserire un nuovo record, limitandoci ad associare un valore al parametro "autore"; per far questo digiteremo:
irb(main):001:0> p = New.new(:autore => "Ruby")
Dando un [Invio] il sistema risponderร completando la nostra istruzione in questo modo:
=> #<New id: nil, autore: "Ruby", titolo: nil, testo: nil, created_at: nil,
updated_at: nil>
Ora cercheremo di memorizzare in tabella il nuovo record e, a questo scopo, digiteremo il nome dellโoggetto ("p") creato dalla precedente istanza associandolo al metodo "save":
irb(main):013:0> p.save
Questa volta il sistema risponderร come segue:
โ[1mโ[35m (0.0ms)โ[0m BEGIN
โ[1mโ[36m (0.0ms)โ[0m โ[1mROLLBACKโ[0m
=> false
La notifica "false" indica in pratica che non รจ stato possibile eseguire lโistruzione per il verificarsi di un errore; per conoscere la natura del malfunzionamento potremo quindi utilizzare la clausola "errors.full_messages" concatenandola in istruzione al nome dellโoggetto:
irb(main):014:0> p.errors.full_messages
=> ["Autore is too short (minimum is 5 characters)"]
La risposta del sistema appare abbastanza chiara: il termine "Ruby" รจ composto da sole quattro lettere, ma non in "new.rb" abbiamo definito un controllo che associa a questo parametro una lunghezza non inferiore alle 5 lettere, per cui lโistruzione non potrร essere eseguita. A questo punto sarร quindi possibile formulare una secondo istruzione rispettando i vincoli di validazione per gli input, come nellโesempio seguente:
irb(main):018:0> p = New.new(:autore => "Rails", :titolo => "Test RoR", :testo=> "Prova di contenuto")
=> #<New id: nil, autore: "Rails", titolo: "Test RorR", testo: "Prova di contenuto",
created_at: nil, updated_at: nil>
irb(main):019:0> p.save
โ[1mโ[35m (0.0ms)โ[0m BEGIN
โ[1mโ[36mSQL (0.0ms)โ[0m โ[1m
INSERT INTO `news` (`autore`, `created_at`, `testo`,
`titolo`, `updated_at`) VALUES ('Rails', '2012-04-28 07:40:48',
'Prova di contenuto', 'Test RorR', '2012-04-28 07:40:48')โ[0m
โ[1mโ[35m (31.2ms)โ[0m COMMIT
=> true
Lโistruzione non farร altro che convertire il nostro comando Rails in un query verso il database, "true" indica che lโesecuzione รจ andata a buon fine, quindi, potremo accedere alla tabella di sviluppo per verificare lโinserimento dei dati:
mysql> select * from news;
+----+--------+-----------+--------------------+---------------------+---------------------+
| id | autore | titolo | testo | created_at | updated_at |
+----+--------+-----------+--------------------+---------------------+---------------------+
| 7 | Rails | Test RoR | Prova di contenuto | 2012-04-28 07:40:48 | 2012-04-28 07:40:48 |
+----+--------+-----------+--------------------+---------------------+---------------------+
1 row in set (0.00 sec)
Eโ da segnalare il fatto che la Rails console puรฒ essere utilizzata in tutte le sue funzionalitร anche senza che vengano apportate effettivamente delle modifiche a carico dei dati in tabella, per questo scopo รจ infatti disponibile un meccanismo di sandbox grazie al quale simulare lโinvio e il funzionamento dei comandi evitando che questi abbiano effetto sui record; la sandbox potrร essere attivata attraverso lโopzione "โsandbox" o lโalias "-s":
C:Ruby193projectmy_app>rails console --sandbox
Loading development environment in sandbox (Rails 3.2.3)
Any modifications you make will be rolled back on exit
irb(main):001:0>
Per uscire dalla console basterร digitare il comando "exit" o, in alternativa, utilizzare la combinazione [Ctrl]+[d], ricordiamoci perรฒ sempre che, a differenza del Rails server, la console non opera un refresh delle pagine quando queste subiscono delle modifiche; quindi, ogni volta che si edita il contenuto di un Model sarร necessario lanciare il comando "reload!" perchรฉ il framework agisca sulla base delle nuove impostazioni.
irb(main):001:0> reload!
Reloading...
=> true
Nel corso dei prossimi capitoli continueremo naturalmente a parlare della parte funzionale (CRUD) della nostra applicazione, prima perรฒ ci occuperemo di un altro aspetto importante, quello relativo alla presentazione dei dati.
Views e personalizzazione del layout
Ora che abbiamo inserito un primo record nella nostra tabella, potremo utilizzare il nostro browser Web per ritornare sullโapplicazione creata e visualizzare tale contenuto nellโelenco delle news:
Come รจ possibile notare, il framework ha generato automaticamente il link necessari alla visualizzazione ("Show"), alla modifica ("Edit") e alla cancellazione ("Destroy") del record, ogni record successivamente inserito sarร associato alle medesime funzionalitร per la manipolazione dei dati. Come anticipato, la struttura delle pagine di funzione รจ data dalle Views che presiedono alla loro presentazione; nel caso della nostra applicazione, sul percorso "app/views/news/" saranno presenti cinque file che corrispondono ad altrettante Views:
- "index.html.erb": rappresenta la View relativa alla visualizzazione di tutti i record;
- "new.html.erb": presenta il form per lโinserimento di un nuovo record;
- "show.html.erb": mostra un singolo record e fornisce i collegamenti per la sua manipolazione;
- "_form.html.erb": contiene il form che verrร utilizzato per le operazioni di editing;
- "edit.html.erb": mette a disposizione il form per la modifica di un singolo record.
Per descrivere quale sia la procedura necessaria permodificare una View, รจ possibile aprire uno qualsiasi di questi file e visualizzarne il contenuto; il codice seguente sarร per esempio disponibile aprendo il file "index.html.erb" della nostra applicazione:
<h1>Listing news</h1>
<table>
<tr>
<th>Autore</th>
<th>Titolo</th>
<th>Testo</th>
<th></th>
<th></th>
<th></th>
</tr>
<% @news.each do |news| %>
<tr>
<td><%= news.autore %></td>
<td><%= news.titolo %></td>
<td><%= news.testo %></td>
<td><%= link_to 'Show', news %></td>
<td><%= link_to 'Edit', edit_news_path(news) %></td>
<td><%= link_to 'Destroy', news, confirm: 'Are you sure?', method: :delete %></td>
</tr>
<% end %>
</table>
<br />
<%= link_to 'News New', new_news_path %>
Immaginiamo ora di voler apportare una semplice modifica al contenuto della View di Home Page, essa consisterร nella traduzione in Italiano delle voci attualmente in lingua inglese; attraverso il nostro editor di testo potremmo per esempio alterare la View in questo modo:
<h1>Visualizza tutte le news</h1>
<table>
<tr>
<th>Autore</th>
<th>Titolo</th>
<th>Testo</th>
<th></th>
<th></th>
<th></th>
</tr>
<% @news.each do |news| %>
<tr>
<td><%= news.autore %></td>
<td><%= news.titolo %></td>
<td><%= news.testo %></td>
<td><%= link_to 'Visualizza', news %></td>
<td><%= link_to 'Modifica', edit_news_path(news) %></td>
<td><%= link_to 'Cancella', news, confirm: 'Confermi la cancellazione?', method: :delete %></td>
</tr>
<% end %>
</table>
<br />
<%= link_to 'Inserisci un nuovo record', new_news_path %>
Una volta salvate le modifiche effettuate, potremo riaprira la pagina che elenca le news e visualizzare il risultato del nostro lavoro:
Il funzionamento di questa pagina dipende da un apposito Controller contenuto nella pagina "news_controller.rb" presente sul percorso "app/controllers/" dellโapplicazion, esso รจ molto ricco di configurazioni, ma a noi in questo momento interessa la parte relativa ad unโazione specifica, lโindex:
def index
@news = New.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: @news }
end
end
In pratica lโelemento New.all restituisce tutti i record presenti nella tabella del database al momento corrente, per far questo viene generato un array che viene memorizzato in una specifica varibile che nel nostro caso รจ chiamata @news, tale array verrร ciclato per consentire la visualizzazione dei record; i formati supportati per i dati messi a disposizione in modo predefinito sono HTML, come appena visto a proposito delle Views e JSON per lโinterscambio.
Le Views generate in seguito allo Scaffolding assolvono perรฒ soltanto parzialmente i compiti relativi alla presentazione del dato e alla generazione del markup di pagina; a tale meccanismo presiedono infatti anche altre componenti dellโapplicazione, per questo motivo nel prossimo capitolo continueremo a parlare di layout.
Il layout specifico per lโapplicazione
Otre alle Views, per quanto riguarda lโaspetto presentazionale di unโapplicazione Rails, RoR supporta una struttura basata sulla nozione di layouts; questi ultimi sono stati concepiti come una sorta di contenitori per le Views; in pratica, quando il framework restituisce tramite il browser lโoutput relativo ad una View, esso da vita ad un meccanismo in grado di integrare il markup della stessa allโinterno dellโHTML relativo ad un layout specifico per lโapplicazione corrente. Tale layout รจ valido per lโintero contesto dellโapplicazione e quindi per tutti i Controllers che fanno riferimento a questโultima, esso viene definito allโinterno del file "application.html.erb" presente sul percorso "app/views/layouts/" della cartella di progetto; aprendo questo file dovremmo visualizzare un listato simile al seguente:
<!DOCTYPE html>
<html>
<head>
<title>MyApp</title>
<%= stylesheet_link_tag "application", :media => "all" %>
<%= javascript_include_tag "application" %>
<%= csrf_meta_tags %>
</head>
<body>
<%= yield %>
</body>
</html>
Ora, proveremo ad applicare alcune regole di stile utilizzando CSS, per esempio potremmo cominciare con il modificare il colore di sfondo delle pagine:
<!DOCTYPE html>
<html>
<head>
<title>MyApp</title>
<%= stylesheet_link_tag "application", :media => "all" %>
<%= javascript_include_tag "application" %>
<%= csrf_meta_tags %>
<style type="text/css">
body
{
background-color:#b0c4de;
}
</style>
</head>
<body>
<%= yield %>
</body>
</html>
Ora, potremo visualizzare da browser lโHome Page della nostra applicazione e valutare le modifiche effettuate:
Il colore di sfondo della pagina รจ cambiato, lo stesso effetto sarร visibile su tutte le altre pagine che compongono lโapplicazione; unโulteriore modifica potrebbe essere per esempio quella di ridurre le dimensioni e il colore dei testi delimitati dai tag "<h1></h1>":
h1
{
color:yellow;
font-size:16px;
}
Anche in questo caso si potrร visualizzare attraverso il browser Web lโesito della regola di stile appena definita.
Infine, potremmo effettuare anche delle modifiche a carico della formattazione delle tabelle:
table, th, td
{
border: 1px solid black;
}
ottenendo un risultato simile al seguente nella pagina contenente lโelenco dei record:
Proseguendo, si potrebbero associare ulteriori regole di stile a molti altri elementi della nostra applicazione, come per esempio ai link (e ai loro diversi stati), ai campi del form, ai pulsati di input o ai testi; lโunico limite sarร la nostra fantasia; lโimportante รจ tenere sempre conto della regola di base che prevede di applicare modifiche alle singole Views quando si desidera agire su pagine specifiche e al file "application.html.erb" quando lโintenzione รจ quella di apportare combiamenti allโintero layout dellโapplicazione.
Come avrete potuto notare, Rails offre numerose possibilitร di intervenire sul rendering delle pagine Web e degli elementi che le compongono; a breve vedremo come il framework sia in grado di organizzare la struttura di unโapplicazione riutilizzando le medesime porzioni di codice per il completamento di funzionalitร differenti.
Riutilizzare codice con i partials
A questo punto abbiamo acquisito le conoscienze necessarie per la creazione di una prima, seppur semplice, applicazione Rails completa e per la modifica dei layouts ad essa associati, vale perรฒ la pena proseguire nellโanalisi della struttura di tale applicazione anche per un semplice motivo pratico, infatti, a differenza degli utenti alle prime armi, gli sviluppatori piรน esperti non utilizzano sempre lo Scaffolding per la generazione dei loro progetti, in particolare quando necessitano una maggiore flessibilitร delle varie componenti funzionali e presentazionali. Per questo motivo, maggiore sarร la conoscenza delle dinamiche create dal framework, maggiori saranno le possibilitร di personalizzazione.
Un aspetto importante nellโeconomia della nostra applicazione รจ quella relativo allโinterazione con il database per lโinserimento dei contenuti; come abbiamo visto in precedenza, esso coinvolge due azioni: "new" e "create"; nel caso specifico del nostro esempio, la prima ha il compito di effettuare lโistanza di un oggetto "New" vuoto, la seconda invece istanzia un oggetto "New" popolato tramite il form per lโinserimento di nuovi record. Per chiarire meglio il concetto esposto, รจ possibile aprire il file View "new.html.erb" contenuto sul percorso "/app/views/news/" della nostra cartella progetto; esso presenta un codice sul modello del seguente:
<h1>New news</h1>
<%= render 'form' %>
<%= link_to 'Back', news_path %>
Dal listato emerge un importante elemento funzionale, cioรจ il riferimento "<%= render โformโ %>"; esso presenta un esempio di partial, cioรจ una porzione di codice che puรฒ essere composta sia da markup HTML che da snippets Ruby e che potrร essere utilizzata piรน volte nel contesto di una medesima applicazione.
Allโinterno di una struttura CRUD abbiamo la necessitร di utilizzare due form: uno per lโinserimento dei dati e uno per il loro aggiornamento, a questo scopo Rails permette di utilizzare uno stesso partial per generare i moduli necessari in entrambi i casi, evitando in questo modo inutili ripetizioni. A conferma di ciรฒ รจ possibile aprire il file "edit.html.erb" contenuto sullo stesso percorso di "new.html.erb":
<h1>Editing news</h1>
<%= render 'form' %>
<%= link_to 'Show', @news %> |
<%= link_to 'Back', news_path %>
Come รจ possibile notare, anche allโinterno di questa View รจ presente il riferimento al partial "<%= render โformโ %>" e, come nel caso della Vista per lโinserimento di un nuovo record, tale elemento determina lโinclusione di unโaltra View contenuta nel file denominato "_form.html.erb" che presenta una struttura come la seguente:
<%= form_for(@news) do |f| %>
<% if @news.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@news.errors.count, "error") %> prohibited this news from being saved:</h2>
<ul>
<% @news.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :autore %><br />
<%= f.text_field :autore %>
</div>
<div class="field">
<%= f.label :titolo %><br />
<%= f.text_field :titolo %>
</div>
<div class="field">
<%= f.label :testo %><br />
<%= f.text_area :testo %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Analizzando il codice visualizzato, si noterร come tutto sia organizzato per effettuare le operazioni previste con meno codice possibile ed eliminando la necessitร di ripetizioni; per fare un esempio, si noti come la prima parte del listato sia dedicata al controllo degli eventuali errori, in essa รจ contenuto un metodo, "pluralize", che permetterร di proporre la parola "error" al singolare o al plurale a seconda del numero di errori generati. Tale metodo risulta particolarmente comodo anche per restituire al plurale parole scritte in Italiano, a questo scopo sarร possibile aprire il file "environment.rb" contenuto nella cartella "/config" della nostra applicazione e inserire delle regole (inflectors) per la pluralizzazione dei termini, ad esempio:
inflect.irregular 'errore', 'errori'
Inoltre, sarร possibile utilizzare anche le espressioni regolari, come per la regola seguente che mostra un esempio di trasformazione da singoli a plurali (e viceversa) di parole che finiscono con la desinenza "ente":
inflect.plural /([w]*)nte$/i, '1nti'
inflect.singular /([w]*)nti$/i, '1nte'
In linea generale รจ possibile affermare che il blocco "form_for" permette di accedere ai metodi necessari per la generazione di controlli a carico dei moduli; "f.text_area :testo" impone per esempio al framework di realizzare un campo di input sotto forma di textarea e associa ad esso il nome del parametro che dovrร essere passato allโapplicazione per lโinseriemnto in tabella. Eโ importante precisare che sarร possibile utilizzare i metodi esclusivamente con gli attributi relativi al Model su cui รจ basato il form corrente, nel nostro esempio gli attributi consentiti saranno per esempio "autore", "titolo" e "testo".
Ma quali funzionalitร entrano in gioco nella gestione dei singoli record in unโapplicazione basata su Rails? Affronteremo questo argomento nel prossimo capitolo.
Visualizzazione, aggiornamento e cancellazione dei record
Per proseguire, lanceremo una query utilizzando il nostro terminale allo scopo di visualizzare il contenuto del database di sviluppo e controlleremo i record presenti nella tabella della news:
mysql> select * from news;
Nella tabella creata per lโapplicazione relativa a questa trattazione, รจ presente un solo record ed รจ associato ad esso lโid "7", utilizzeremo quindi questโultimo per il nostro esempio, il lettore invece potrร scegliere uno qualsaisi degli id archiviati nella propria tabella; ora, se il nostro Rails server non รจ stato ancora attivato, lo lanceremo e subito dopo apriremo il nostro browser per digitare nella barra degli indirizzi unโURL simile alla seguente:
http://localhost:3000/news/7
Dovremmo visualizzare una pagina contenente dei dati simili a quelli presenti nellโimmagine successiva:
La generazione di questa pagina dipende dal fatto che il framework interpreta lโURL digitata come una chiamata allโazione "show" per il contenuto relativo allโidentificatore specificato come parametro, tale "id" diventa quindi un argomento (":id") che viene utilizzato dal metodo "New.find" per effettuare una ricerca tra i record disponibili; una volta terminata la scansione dei dati, questi vengono mostrati attraverso la View contenuta nel file "show.html.erb" presente sul percorso "/app/views/news" della nostra applicazione. Aprendo tale View sarร possibile visualizzare il codice seguente:
<p id="notice"><%= notice %></p>
<p>
<b>Autore:</b>
<%= @news.autore %>
</p>
<p>
<b>Titolo:</b>
<%= @news.titolo %>
</p>
<p>
<b>Testo:</b>
<%= @news.testo %>
</p>
<%= link_to 'Edit', edit_news_path(@news) %> |
<%= link_to 'Back', news_path %>
Come รจ possibile notare, alla fine del codice della View sono presenti due link, in questo momento a noi interessa il primo, dato che il secondo si limita a riportare lโutente alla pagina precedente rispetto a quella corrente; il collegamento ipertestuale che ha come descrizione "Edit" conduce alla pagina contenente il form per effettuare le modifiche sui singoli record. Abbiamo giร visualizzato nel capitolo precedente il codice relativo alla View per la presentazione del modulo di aggiornamento delle news, รจ quindi venuto il momento di proporre il form cosรฌ come viene generato da Rails con in piรน le modifiche da noi apportate al layout:
Il processo per lโaggiornamento di un record funziona in modo molto simile a quello necessario per la sua creazione; in questo caso il primo passaggio sarร quello relativo allโinvio di una richiesta effettuata tramite il costrutto "edit_news_path(@new)" che, in pratica, passa al percorso verso il file per lโediting ("edit_news_path") una variabile ("@new") contenente lโinformazione relativa allโidentificatore del record da manipolare. Lโazione "edit" viene messa a disposizione da un Controller strutturato nel modo seguente:
def edit
@new = New.find(params[:id])
end
La chiamata allโazione determina la presentazione della risorsa richiesta attraverso la View contenuta nel file "edit.html.erb" che fornisce il form completo di campi giร popolati con i dati da modificare. Anche in questo caso verra utilizzato il partial "<%= render โformโ %>" per lโinclusione del modulo, perรฒ, a differenza di quanto accade con la View per lโinserimento dei record, il form farร riferimento al controller NewController tramite metodo PUT e il pulsante per lโinvio dei dati presenterร una descrizione differente: "Update New".
La modifica dei dati in tabella, effettuata utilzzando in parametri inviati tramite form, sarร possibile grazie allโazione "update"; RoR utilizzerร inizialmente il parametro ":id" per rilevare lโesatto record da modificare, quindi, applicherร le modifiche e, se lโoperazione di aggiornamento dovesse concludersi con esito positivo, verrร invocata lโazione "show" per visualizzare nuovamente il contenuto a conferma dellโavvenuto aggiornamento; diversamente, in presenza di qualche impedimento (ad esempio un formato non valido associato ad un paramentro), verrร effettuto un redirect alla stessa pagina del form per lโediting in modo da permettere la ricompilazione del modulo.
Lโultima funzionalitร che non puรฒ mancare in unโapplicazione ispirata al modello CRUD รจ quella relativa alla cancellazione dei record, a questo scopo Rails introduce unโapposita azione, denominata "destroy", che fa riferimento ad uno specifico controller:
def destroy
@new = New.find(params[:id])
@new.destroy
respond_to do |format|
format.html { redirect_to news_url }
format.json { head :no_content }
end
end
Dato un determinato id, "destroy" lo utilizza per rilevare il record corrispondente attraverso una ricerca in tabella e quindi concellarlo; fatto questo, il framework redireziona la navigazione tramite verso lโazione "index" del Controller.
Ora che abbiamo analizzato tutti gli aspetti funzionali della nostra prima applicazione RoR (per ora estremamente basilare), non ci resta che sfruttare le potenzialitร del framework per lโintegrazione di nuove features che la rendano piรน completa.
Introdurre un Model addizionale
A questo punto del nostro approfondimento riguardante la realizzazione di unโapplicazione basata sul RoR, potremo passare ad una fase piรน avanzata, cioรจ รฌquella relativa allโinclusione di funzionalitร aggiuntive; come vedremo a breve, i passaggi richiesti non differiscono di molto da quelli descritti per la creazione delle features di base del nostro progetto, in questo caso perรฒ partiremo dalla generazione del Model; questโultimo verrร ottenuto grazie allโapposito comando "rails generete model" che dovrร essere lanciato dal percorso della cartella della nostra applicazione:
C:Ruby193projectmy_app>rails generate model Commenti utente:string
messaggio:text new:references
invoke active_record
create db/migrate/20120504102314_create_commentis.rb
create app/models/commenti.rb
invoke test_unit
create test/unit/commenti_test.rb
create test/fixtures/commentis.yml
Nel caso specifico, creeremo un sistema di commenti che gli utenti potranno postare a corredo degli articoli; nellโesempio proposto, tale funzionalitร permetterร di manipolare tre soli campi ("utente", il nickname del commentatore, "messaggio", il commento vero e proprio e"new", la chiave esterna che consentirร di mettere in relazione news e commenti), ma il lettore se lo desidera potrร rendere la struttura di questa "estensione" ancora piรน articolata. Con lโesecuzione del comando per la generazione del nuovo Model, verranno quindi creati i file indicati di seguito con tanto di percorso allโinterno della directory progetto:
- "db/migrate/20100207235629_create_commentis.rb": รจ la migration con la quale generare la tabella dei commenti e i relativi campi, il nome del file riporta un timestamp che permetterร di identificare univocamente la migration;
- "app/models/commenti.rb": il Model associato alla funzionalitร per i commenti;
- "test/unit/commenti_test.rb": รจ il file di Unit testing del Model per i commenti;
- "test/fixtures/commentis.yml": una risorsa generata automaticamente al momento della creazione del Model per lโesecuzione di test;
Il file "commenti.rb" presente sul percorso "/app/models/" della nostra applicazione presenterร al suo interno una classe strutturata nel modo seguente:
class Commenti < ActiveRecord::Base
belongs_to :new
attr_accessible :messaggio, :utente
end
La parte piรน interessante del codice appena proposto riguarda sicuramente la riga:
belongs_to :new
Infatti, come potrete notare, Rails รจ in grado di capire sulla base di un dato di relazione (nel nostro caso la key "new") e dal contesto applicativo che sussiste una rapporto tra la nuova tabella e quella giร prevista per lโapplicazione di base, in pratica il framework riesce a strutturare le sorgenti di dati sulla base di indicazioni minime fornite dallo sviluppatore.
Ora si potrร passare alla fase in cui la migration, contenuta sul percorso "/db/migrate/" della cartella progetto, ci permetterร di creare unโulteriore tabella, tale file dovrebbe presentare giร tutte le indicazioni riguardanti i campi da generare:
class CreateCommentis < ActiveRecord::Migration
def change
create_table :commentis do |t|
t.string :utente
t.text :messaggio
t.references :new
t.timestamps
end
add_index :commentis, :new_id
end
end
La creazione della tabella per i commenti allโinterno del database sarร possibile grazie ad un comando a noi giร noto:
C:Ruby193projectmy_app>rake db:migrate
== CreateCommentis: migrating ================================================
-- create_table(:commentis)
-> 0.2344s
-- add_index(:commentis, :new_id)
-> 0.2705s
== CreateCommentis: migrated (0.5049s) =======================================
Al termine dellโesecuzione potremo verificare lโesito della nostra ultima migrazione:
mysql> show columns from commentis;
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| utente | varchar(255) | YES | | NULL | |
| messaggio | text | YES | | NULL | |
| new_id | int(11) | YES | MUL | NULL | |
| created_at | datetime | NO | | NULL | |
| updated_at | datetime | NO | | NULL | |
+------------+--------------+------+-----+---------+----------------+
6 rows in set (0.06 sec)
Ricordate il file"new.rb" contenuto nel percorso "/app/models/" della nostra applicazione? Ora lo dovremo aprire nuovamente allo scopo di effettuare una semplice modifica che consiste nellโaggiunta dellโespressione "has_many :commentis" prima dellโ"end" che delimita il blocco di codice in chiusura:
class New < ActiveRecord::Base
attr_accessible :autore, :testo, :titolo
validates :autore, :length => { :minimum => 5 }
validates :titolo, :length => { :maximum => 10 }
validates :testo, :length => { :maximum => 500 }
has_many :comments
end
In questo modo concluderemo le operazioni necessarie per associare i due Models disponibili, cioรจ quello del New Manager a quello dei commenti, ad ogni "new" potranno essere associati piรน commenti.
Il nostro lavoro per lโintegrazione della nuova funzionalitร non รจ perรฒ ancora conlcuso, infatti, adesso dovremo occuparci della sua parte funzionale (il Controller) e di quella presentazionale (la View).
Routing, Controller e View del model addizionale
Ora che sono state completate le operazioni necessarie per il completamento del Model, come รจ giร accaduto per il News Manager, anche per quanto riguarda i commenti sarร necessario impostare delle regole di routing attraverso le quali Rails sia in grado di instradare correttamente le diverse componenti applicative; quindi, quello che dovremo fare ora sarร editare il file "routes.rb" presente nella cartella "config/" della nostra directory progetto e aggiungere una nuova regola, essa dovrร essere posizionata dopo "resources :news" in modo che le prime quattro righe risultino scritte nel modo seguente:
MyApp::Application.routes.draw do
resources :news do
resources :commentis
end
Completato in nostro blocco per il routing salveremo il file editato e sarร possibile generare il Controller associato al Model dei commenti; a questo scopo รจ disponibile lโapposito comando denominato "rails generate controller" seguito dal nome del Controller desiderato che dovrร essere omonimo rispetto alla tabella creata tramite migration:
C:Ruby193projectmy_app>rails generate controller Commentis
create app/controllers/commentis_controller.rb
invoke erb
create app/views/commentis
invoke test_unit
create test/functional/commentis_controller_test.rb
invoke helper
create app/helpers/commentis_helper.rb
invoke test_unit
create test/unit/helpers/commentis_helper_test.rb
invoke assets
invoke coffee
create app/assets/javascripts/commentis.js.coffee
invoke scss
create app/assets/stylesheets/commentis.css.scss
Alcuni dei file creati svolgono una funzione molto simile a quelli giร visti in occasione dello Scaffolding utilizzato per il News manager, descriviamoli nel dettaglio:
- "app/controllers/commentis_controller.rb": รจ il Controller della nostra nuova funzionalitร ;
- "app/views/commentis": la cartella destinata a contenere tutte le View associate al Controller generato;
- "test/functional/commentis_controller_test.rb": contiene i riferimenti per i test funzionali a carico del Controller;
- "app/helpers/commentis_helper.rb": รจ lโhelper file delle View per il codice riutilizzabile;
- "test/unit/helpers/commentis_helper_test.rb": il file helper per i test unitari sulle funzioni;
- "app/assets/javascripts/commentis.js.coffee": contine il JavaScript associato alla nuova feature;
- "app/assets/stylesheets/commentis.css.scss": rappresenta il foglio di stile esterno contenente le regole CSS da associare ai commenti;
Il prossimo passaggio consisterร in unโulteriore azione di editing, dovremo infatti mettere mano ad una nostra vecchia conoscenza, cioรจ il file di View "show.html.erb" contenuto sul percorso "/app/views/news/" della nostra applicazione; allโinterno di esso dovremo infatti inserire il codice necessario per la visualizzazione dei commenti (quando presenti), quindi, prima del link per la modifica delle news potremo inserire delle istruzioni come le seguenti:
<h2>Visualizza i commenti</h2>
<% @new.commentis.each do |commenti| %>
<p>
<b>Utente:</b>
<%= commenti.utente %>
</p>
<p>
<b>Messaggio:</b>
<%= commenti.messaggio %>
</p>
<% end %>
Ma perchรฉ i commenti siano visualizzabili, prima sarร naturalmente necessario che questi possano essere inseriti, motivo per il quale potremo aggiungere subito dopo il blocco dei commenti un form per la loro digitazione
<h2>Inserisci un tuo commento:</h2>
<%= form_for([@new, @new.commentis.build]) do |f| %>
<div class="field">
<%= f.label :utente %>
<br />
<%= f.text_field :utente %>
</div>
<div class="field">
<%= f.label :messaggio %>
<br />
<%= f.text_area :messaggio %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Ora che abbiamo implementato le parti funzionali e prensentazionali dellโestensione per i commenti, dovremo permettere alle varie componenti di lavorare insieme; avrete infatto notato che la cartella destinata a contenere le Views per la nostra funzionalitร addizionale รจ ancora vuota; nel prossimo capitolo descriveremo quindi le procedure necessarie per il suo popolamento.
Generazione manuale delle Views
Se avete provato a testare la funzionalitร aggiuntiva per lโinclusione dei commenti e questa ancora non dovesse funzionare, non vi preoccupate! Il motivo di questo risultato รจ infatti semplice, tale estensione dellโapplicazione non รจ al momento associata ad alcuna View, quindi il malfunzionamento รจ pienamente giustificato. Per rimediare a tale mancanza, dovremo recarci sulla cartella denominata "commentis" (come abbiamo visto Rails aggiunge autonomamente una "s" finale al nome del Controller per questioni legate alla pluralizzazione in lingua Inglese) che si trova allโinterno del percorso "/app/views/" della directory di progetto; essa รจ vuota di default, quindi procederemo con il riempirla creando innanzitutto allโinterno di essa un file denominato "_commenti.html.erb" che apriremo ed editeremo digitando il seguente codice:
<p>
<b>Utente:</b>
<%= commenti.utente %>
</p>
<p>
<b>Messaggio:</b>
<%= commenti.messaggio %>
</p>
Una volta salvato il nostro lavoro, avremo generato una prima View, quella destinata alla visualizzazione dei commenti per le singole news; ora passeremo alla creazione di una seconda View, quella relativa al form per lโinserimento dei commenti che potrร contenere un listato come quello mostrato di seguito:
<%= form_for([@new, @new.commentis.build]) do |f| %>
<div class="field">
<%= f.label :utente %><br />
<%= f.text_field :utente %>
</div>
<div class="field">
<%= f.label :messaggio %><br />
<%= f.text_area :messaggio %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Salviamo quanto digitato sempre sullo stesso percorso della View precedente e in un file denominato "_form.html.erb"; ora i piรน attenti si chiederanno il perchรฉ del fatto che proprio RoR, concepito per evitare quanto piรน possibile la presenza di ripetizioni, consenta la creazione di unโaltra View per il form nel contesto di unโapplicazione che ne contiene giร una; la risposta รจ immediata anche in questo caso: una nuova View ci permetterร infatti di risparmiare un buon numero di righe di codice. Vediamo come.
Aprimo il file "show.html.erb" contenuto sul percorso "/app/views/news/" e modifichiamo il codice contenuto nel modo seguente:
<p id="notice"><%= notice %></p>
<p>
<b>Autore:</b>
<%= @news.autore %>
</p>
<p>
<b>Titolo:</b>
<%= @news.titolo %>
</p>
<p>
<b>Testo:</b>
<%= @news.testo %>
</p>
<h2>Visualizza i commenti</h2>
<%= render @new.commentis %>
<h2>Inserisci un tuo commento:</h2>
<%= render "commentis/form" %>
<%= link_to 'Edit', edit_news_path(@news) %> |
<%= link_to 'Back', news_path %>
Come รจ semplice notare, ora, invece di specificare direttamente il codice necessario per la visualizzazione dei commenti e per lโintegrazione del form dโinserimento degli stessi, รจ stato sufficiente ricorrere a due partials che eseguiranno lo stesso compito: "<%= render @new.commentis %>" e "<%= render "commentis/form" %>".
Adesso che abbiamo la possibilitร di inserire i commenti e di renderli disponbili sulla pagina, non resta che introdurre la funzionalitร necessaria per la loro cancellazione; riapriamo quindi il file "_commenti.html.erb" e modifichiamo il codice presente nel modo seguente:
<p>
<b>Utente:</b>
<%= commenti.utente %>
</p>
<p>
<b>Messaggio:</b>
<%= commenti.messaggio %>
</p>
<p>
<%= link_to 'Elimina commenti', [commenti.new, commenti],
:confirm => 'Vuoi eliminare il commento?',
:method => :delete %>
</p>
La modifica effettuata permetterร di utilizzare il collegamento "Elimina commenti" per richiamare il metodo "delete" dal Controller dei commenti, questโultimo effettuerร una ricerca in tabella sulla base dellโidentificatore selezionato che, se rilevato, permetterร la cancellazione del record. A questo scopo, bisognerร aggiungere un nuovo blocco di istruzioni alla classe "CommentsController" dellโ"ApplicationController":
def destroy
@new = New.find(params[:new_id])
@commenti = @new.commentis.find(params[:id])
@commenti.destroy
redirect_to new_path(@new)
end
In pratica, lโazione "destroy" si occuperร di scovare la "new" desiderata e di identificare il relativo commento allโinterno del set "@post.commentis"; una volta cancellato il commento verrร effettuato un redirect alla pagina della "new".
Ora che abbiamo creato una applicazione CRUD completa, questa ci consentirร di manipolare tutti i dati presenti nel nostro database, ma se desiderassimo utilizzarla in Rete non potremmo di certo renderla accessibile a chiunque, dovremo quindi proteggerla con un sistema per lโautenticazione.
Autenticazione in Rails
Il framework Ruby On Rails mette a disposizione alcune modalitร per mettere in sicurezza le applicazioni CRUD, queste ultime sono infatti generalmente destinate a fornire funzionalitร di back-end che non dovrebbero essere accessibili pubblicamente. Nel nostro caso faremo ricorso alla soluzione piรน semplice, cioรจ alla creazione di un sistema di autenticazione basato su HTTP che viene supportato nativamente da Rails attraverso un metodo appositamente dedicato denominato "http_basic_authenticate_with". Una configurazione ideale potrebbe essere quella che prevede la possibilitร di visualizzare i contenuti senza la necessitร di alcuna procedura di login, questโultima dovrebbe essere invece richiesta per effettuare operazioni di inserimento, modifica e cancellazione; a questo scopo potremo agire sul file "news_controller.rb", contenuto sul percorso "/app/controllers/" della nostra applicazione, ed editarlo aggiungendo una semplice riga di istruzioni da digitare subito dopo la dichiarazione del NewsController:
class NewsController < ApplicationController
http_basic_authenticate_with :name => "user", :password => "pass", :except => [:index, :show]
Lo schema dellโistruzione รจ abbastanza lineare:
- viene effettuata la chiamata al metodo;
- si definiscono la username e la password necessarie per lโautenticazione;
- si stabiliscono delle eccezioni introdotte dalla clausola "except" che accetta come argomenti le actions che non dovranno essere vincolate alla procedura di login per poter essere richiamate.
Una volta salvate le modifiche effettuate, potremo avviare il Web server di Rails e visualizzare lโeffetto della nuova istruzione; lโimmagine seguente mostra per esempio la reazione dellโapplicazione in seguito ad un click sul link che dovrebbe consentire lโaccesso al form per la modifica di uno specifco record:
Una volta digitate le credenziali richieste, gli esiti dellโoperazione potrebbero essere due: se username e password sono corrette, si potrร accedere alla pagina per la modifica del record selezionato, diversamente, lโapplicazione segnalerร lโesito negativo del login riproponendo il modulo per lโautenticazione o tramite la seguente notifica di stato:
HTTP Basic: Access denied.
Vale la pena di sottolineare che il Rails server intercetta una procedura di login eseguita con successo notificandola nel modo seguente:
Started GET "/news" for 127.0.0.1 at 2012-05-08 10:24:05 +0200
Processing by NewsController#index as HTML
?[1m?[36mNew Load (0.0ms)?[0m ?[1mSELECT `news`.* FROM `news` ?[0m
Rendered news/index.html.erb within layouts/application (1.0ms)
Completed 200 OK in 15ms (Views: 11.7ms | ActiveRecord: 0.0ms)
Mentre un tentativo di login conclusosi negativamente verrร segnalato in questo modo:
Started GET "/news/7/edit" for 127.0.0.1 at 2012-05-08 10:31:24 +0200
Processing by NewsController#edit as HTML
Parameters: {"id"=>"7"}
Filter chain halted as #<Proc:0x378d6b0@C:/Ruby193/lib/ruby/gems/1.9.1/gems/acti
onpack-3.2.3/lib/action_controller/metal/http_authentication.rb:112> rendered or
redirected
Completed 401 Unauthorized in 0ms (ActiveRecord: 0.0ms)
Lo status code HTTP 200, cioรจ quello relativo alla prima risposta del Rails server, evidenzia il successo della richiesta ("The request was fulfilled"); il codice di stato 401 ("Unauthorized"), quello generato nel secondo caso, indica invece la necessitร di inviare nuovamente la richiesta di autenticazione.