In questa nuova serie, “Ruby in azione”, ci dedichiamo ad alcuni aspetti utili al programmatore Ruby on Rails, quali la componente view e i templates, la gestione dei file XML, l‘ambiente di deployment, e così via. Iniziamo dall‘analizzare il meccanismo dei template e le potenzialità della ActionView.
Introduzione
In questa serie di articoli, “Ruby in azione”, ci dedichiamo ad alcuni aspetti utili al programmatore Ruby on Rails. In particolare focalizzeremo la nostra attenzione su alcuni argomenti “di routine” quali la componente view e i templates, la gestione dei file XML, l‘ambiente di deployment, etc. Con l‘espressione “di routine” non si vuole intendere “noiosi” quanto piuttosto “molto frequenti”, e quindi utili nella maggior parte dei contesti operativi. Diciamo che, piuttosto, è nostra intenzione cercare di fornire qualche suggerimento proprio al fine di evitare operazioni “noiose”.
Iniziamo dall‘analizzare il meccanismo dei template e le potenzialità della ActionView.
L‘idea è quella di cercare di fornire una “manciata di informazioni” relative a quali feature il framework RoR mette a disposizione dello sviluppatore, cercando di soffermarci (comunque non troppo) su alcune di esse.
Non specifichiamo di nuovo il concetto di MVC (Model – View – Controller), supponendo che i lettori non necessitino di approfondimenti a riguardo, e ci immergiamo immediatamente sulle modalità di implementazione della View in Rails. L‘obiettivo che abbiamo in testa è quello di imparare a realizzare, rapidamente e senza molti artifici, una view che non vada troppo in deroga allo spirito di fondo dell‘MVC e che quindi mantenga una netta separazione tra le componenti architetturali.
ActionView
ActionPack, di cui Action View è parte insieme ad Action Controller, gestisce il meccanismo che consente di processare le richieste, di generare le risposte a tali richieste e di far scambiare dati tra il controller e la view.
Il modulo ActionView, come si può ben immaginare, contiene le funzionalità necessarie alla gestione della componente “vista”, in particolare il “rendering” dei template, al fine di generare HTML o XML.
In Rails le tipologie di template disponibili sono le seguenti:
- rhtml template
- rxml template
- rjs template (Ruby JavaScript Template, di cui non ci occuperemo in questo articolo)
Iniziamo dalla prima tipologia e facciamoci aiutare da un esempio per capirne il funzionamento.
Premettiamo che un programmatore Java/JSP o PHP non dovrebbe avere alcuna perplessità nella comprensione dei template rhtml. L‘immissione di codice Ruby nelle pagine html con una sintassi tipo
<%=Ruby code%>
non dovrebbe lasciare sconcertato il lettore. Passiamo immediatamente a qualche prova sul campo.
Un esempio
Creiamo una applicazione Rails chiamata RubyInAzioneI. Utilizzando un database MySQL, creiamo l‘istanza MOKA e aggiungiamo la tabella “employees”. Questo lo script associato.
CREATE TABLE employees (
id int(11) auto_increment,
firstname varchar(60) collate utf8_bin NOT NULL,
lastname varchar(60) collate utf8_bin NOT NULL,
dob date collate utf8_bin NOT NULL,
PRIMARY KEY (id)
)
Generiamo una scaffold application basandoci su di un controller di nome AdminEmployees e un model Employee (i file generati saranno rispettivamente admin_employees.rb e employee.rb).
Figura 1 – Questa la struttura della componente view dopo aver generato la scaffold application
Per i dettagli su come effettuare questa operazione e sulle caratteristiche di una scaffold application in Rails fare riferimento a [3].
Prima di andare ad analizzare il risultato della nostra operazione, facciamo qualche premessa partendo dall‘assunto che ogni qualvolta generiamo una view, stiamo creando un template. Ora ci interessa andare ad approfondire la struttura di un template, la sua localizzazione sul file system, cosa contiene e le varie features che ci consentono di mantenere una abbastanza netta separazione tra view e controller, cioè gli helpers.
La configurazione di default di Rails prevede che i template siano contenuti nella cartella “app/views”. A meno di esigenze particolari di condivisione dei template tra più applicazioni, questa è la localizzazione che il metodo render(), invocato nel controller, si aspetta per le componenti della view. All‘interno di questa directory sono presenti
- una cartella associata ad ogni controller con all‘interno i file di riferimento di ogni action;
- una cartella layouts all‘interno della quale sono presenti i template veri e propri, uno per ogni controller di default.
A questo punto, qualsiasi personalizzazione nella struttura della pagina passa per un adeguamento di quanto contenuto in “app/views/layouts”, mentre un adeguamento del contenuto della singola view associata a una action particolare necessita un intervento in “app/view/[controllername]/[viewname]”.
Nel caso in cui volessimo avere un unico template per l‘applicazione invece di uno per ogni controller è possibile aggiungere “application.rhtml in app/view/layouts” ed eliminare il template associato al controller (o solo di alcuni che non hanno bisogno di specificità grafica).
Quanto descritto lo possiamo visualizzare nella figura 1 (e soprattutto nel codice allegato, scaricabile dal menu in alto a sinistra).
- Abbiamo admin_employees.rhtml in app/views/layouts come struttura della pagina
- I file della view che consentono di visualizzare il risultato delle operazioni CRUD complete nella cartella app/views/admin_employees
- Per fare una prova del funzionamento del template cross-controller inseriamo application.rhtml rinominando admin_employees.rhtml (magari dopo aver introdotto una piccola modifica grafica in uno dei due template).
Torna tutto?
L‘invocazione della componente view avviene, ovviamente, dal controller. La modalità con la quale avviene l‘invocazione è la seguente:
render :action => ‘viewname‘
Consultiamo file admin_controller_emplyees.rb per visualizzare una serie di esempi.
Potremmo anche effettuare richieste del tipo
render :template => ‘controllername‘
render :file => ‘templatedirectory‘
In particolare con l‘ultima invocazione abbiamo la possibilità di posizionare i template ovunque sul file system (e quindi condividerli con qualsiasi altra applicazione)
All‘interno dei file .rhtml di cui al punto 2. abbiamo codice html unito a codice Ruby. A livello di eleganza e separazione delle componenti architetturali non risulta essere sicuramente la migliore situazione.
Per cercare di ovviare, almeno in parte, a questo inconveniente ci vengono in soccorso gli helpers. Localizzati di default in app/helpers, essi consentono di separare tutte quelle operazioni riutilizabili e che risulterebbero indubbiamente poco eleganti all‘interno del codice dei template.
Helpers
Apriamo il file admin_employees_helper.rb. All‘interno troviamo qualcosa del tipo
module AdminEmployeesHelper
end
Per i dettagli sull‘utilizzo dei module in Ruby facciamo riferimento a [2]. In questo caso vengono utilizzati per tener “pulito” il sorgente della view da troppo codice Ruby delegandolo all‘helper di riferimento. Questo facilita, e non poco, l‘interazione con il designer di turno, soprattutto in fase di recepimento delle esigenze del cliente a posteriori.
Facciamo un esempio e inseriamo un banale metodo nell‘helper
module AdminEmployeesHelper
def title
@title || "My Title"
end
end
Invochiamolo in una nostra componente view con la seguente sintassi
<%=title%>
Quello che abbiamo disponibile è uno strumento molto potente. Abbiamo disponibili tutti i metodi dell‘helper all‘interno della view evitando così inutili ripetizioni e mantenendola di conseguenza piuttosto pulita.
Rails però fa qualcosina in più, e cioè sfrutta questo meccanismo per mettere a disposizione dei built-in helpers, (senza doverli implementare quindi) relativi a formattazione di date, numeri e testo, strumenti ai quali il programmatore ricorre piuttosto frequentemente. Si veda la documentazione RDoc in merito alle numerose possibilità in questo ambito (ci riferiamo alle varie number_to_percentage(:number), number_to_currency(:number), e così via).
Altre feature e… compiti a casa da svolgere
A questo punto elenchiamo brutalmente una serie di caratteristiche utili al programmatore che potrà facilmente approfondire quelle di maggiore interesse. L‘idea è di mettere in luce una serie di aspetti senza doverli necessariamente trattare in maniera completa (anche perchè abbastanza banali nell‘utilizzo).
La trattazione risulta tutt‘altro che esaustiva se la finalità è comprendere il dettaglio implementativo, ma fornisce comunque un‘idea delle potenzialità del framework e di quanto sia stato pensato sul campo. In sostanza: “vediamo cosa abbiamo a disposizione, e poi il modo di usarlo correttamente… lo impareremo”.
Questi che seguono sono alcuni degli aspetti che segnaliamo per suscitare l‘interesse del lettore cuirioso che potrà decidere di approfondirli adeguatamente.
Form and Field Helpers
Rails dispone di una serie piuttosto ricca di helpers di supporto alla creazione delle form e degli oggetti in questi contenuti (select, text, button, ecc). La sintassi risulta essere qualcosa del genere:
<% form_tag :action => ‘actionname‘, :id => @list do %>
...
<% end %>
Come esempio, si veda edit.rhtml in app/views/admin_employees/
Condivivisione di Helper
Supponiamo che gli helper che stiamo aggiungendo non siano specifici di ogni controller ma possano essere riutilizabili in maniera più diffusa. A questo punto li inseriamo in application_helper.rb, rendendoli così fruibili a qualunque template.
Partial page template
Senza difficoltà riusciamo a metter su un‘applicazione che aggiorna, ricaricandola, solo un frammento della pagina HTML. Particolarmente utile nelle interazioni Ajax (si veda [4] per i dettagli in merito)
Paginazione delle liste di output
Disponiamo della possibilità di paginare le liste di output (si veda il metodo list di admin_employees_controller.rb del nostro esempio allegato per capire come si fà )
Link a risorse esterne ai template.
I vari
<%= link_to ‘Link Description‘, :action => ‘actionname‘ %>
<%= stylesheet_link_tag ‘stylesheetfile‘ %>
non hanno necessità di commenti riguardo al significato.
E poi cè altro che non abbiamo menzionato per problemi di spazio e tempo… Passiamo ora ai template .rxml.
Builder Templates
Nel caso in cui volessimo dare una maggiore sistematizzazione dell‘output, o necessitiamo di XML in ritorno, possiamo ricorrere ai Builder Templates. Il supporto non è disponibile nel pacchetto base e quindi è necessario aggiungerlo. Se si dispone di ‘gem‘ sulla propria macchina, l‘installazione risulta immediata
gem install builder
Attendiamo che le procedure arrivino al termine e il gioco è fatto. Builder supporta namespaces, entities, ecc. Diamo uno sguardo alla documentazione per verificare il tutto.
Passiamo a scrivere qualche riga di codice di esempio di un template .rxml
require ‘builder‘
mycars = {
‘utilitaria‘ => ‘Panda‘, ‘supercar‘ => ‘Ferrari‘, ‘stationwagon‘ => ‘Volvo 240‘
}
xml = Builder::XmlMarkup.new( :target => $stdout, :indent => 2 )
xml.instruct! :xml, :version => "1.1", :encoding => "US-ASCII"
xml.mycars do
mycars.each do | name, choice |
xml.mycars( choice, :item => name )
end
end
Lanciamo questo codice in irb, o facciamone un .rb standalone e verifichiamone il funzionamento. A questo punto possiamo lanciarci nell‘implementazione della nostra applicazione. Il meccanismo dei template .rxml è del tutto equivalente a quello dei .rhtml.
Conclusioni
Ora che abbiamo cognizione degli strumenti a nostra disposizione, facciamo delle sperimentazioni… e cerchiamo di tirar fuori tutto quello che non torna. In attesa del prossimo capitolo della saga (previsto per febbraio 2008), sono disponibile a qualsiasi interazione… scrivetemi.
Riferimenti
[1] D. Thomas, “Agile Software Development with Rails. The Pragmatic Programmer‘s Guide”, Pragmatic Bookshelf, 2005
http://www.pragmaticprogrammer.com
[2] A. Nucci, “Ruby – II parte: Approfondiamo alcuni aspetti del linguaggio Ruby”, Mokabyte 115, Febbraio 2007
[3] A. Nucci, “Ruby – III parte: Iniziamo a percorrere i binari di Rails”, Mokabyte 118, Maggio 2007
[4] A. Nucci, “Ruby – V parte: RoR e il Web 2.0”, Mokabyte 121, Settembre 2007
[5] M. Fitzgerald, “Creating XML with Ruby and Builder”
http://www.xml.com/pub/a/2006/01/04/creating-xml-with-ruby-and-builder.html