Inizia oggi una serie di articoli che descrivono gli aspetti principali del linguaggio Ruby e che andranno a focalizzarsi su Rails (il Web Application Framework più diffuso e potente del linguaggio) che implementa il pattern MVC, ma che contiene anche uno strumento ORM molto interessante. L‘intento è quello di toccare vari aspetti, dalla sintassi del linguaggio Ruby all‘utilizzo e codifica del framework, evidenziandone peculiarità, pregi ed eventualmente anche limiti.
Introduzione
Inizia oggi una serie dedicata a Ruby.
Si tratta di un‘ interessante tecnologia nel panorama internazionale che merita, a nostro avviso, una attenta analisi da parte di chi desidera disporre di un quadro completo delle opportunità disponibili in ambito “sviluppo applicazioni”.
Una scelta snella, produttiva, portabile e free “…free of charge, but also freedom to use, copy, modify, and distribute it…”.
Nella nostra serie di articoli abbiamo intenzione di toccare vari aspetti, dai primi passi della configurazione dell‘ambiente, allo sviluppo web con il framework Rails, passando per Unit Testing, Duck Typing e altre caratteristiche del linguaggio quali Regular Expression, Moduli, ecc.
Un po‘ di Storia
Ruby è un linguaggio con caratteristiche singolari. La più interessante forse è il fatto che sia per la maggior parte il risultato del lavoro di una sola persona, il giapponese Yukihiro Matsumoto, o più confidenzialmente “Matz” che ha iniziato a sviluppare il linguaggio fin dal 1993, quindi ben prima che Java apparisse all‘orizzonte (all‘epoca era ancora un progetto interno Sun denominato Oak).
Successivamente, mentre Java si diffondeva a macchia d‘olio, sospinto da Sun e dalla diffusione del World Wide Web, Ruby cresceva lentamente, conquistandosi a poco a poco una schiera di utenti appassionati. Nell‘ultimo anno il numero di progetti basati su Ruby è cresciuto notevolmente, grazie anche alla diffusione del framework Rails, che propone un paradigma di programmazione web drasticamente semplificato (e soprattutto più produttivo) rispetto alla programmazione Java mainstream per il web.
Rails a parte, è interessante notare che Ruby è considerato in questo momento il linguaggio con cui è più piacevole programmare. La coerenza della sintassi, la sua pulizia e la sua intrinseca semplicità sono infatti le caratteristiche che più spesso vengono lodate dai programmatori Ruby.
Il primo programma “hello world” in Ruby viene alla luce nell‘estate del 1993. La prima “alpha version” viene rilasciata a dicembre del 1994.
Da quel momento è iniziato un notevole interesse per il linguaggio tanto che attualmente è uno dei pochi esempi di tecnologia software proveniente dall‘oriente e che si è affermata in tutto il mondo.
Perchè Ruby
Supponiamo di voler scrivere applicazioni (in particolare applicazioni web) solide e facilmente manutenibili.
Abbiamo varie alternative tra cui muoverci. Cerchiamo di valutarle insieme.
Java, nelle sue varie interpretazioni in forma di framework quali Struts, Java Server Faces o Tapestry, è un opzione sicuramente valida e percorribile. Il punto debole della maggior parte dei presentation frameworks in Java è tuttavia dato dal tempo di apprendimento necessario ad essere produttivi, per una serie di motivi:
- i frameworks si evolvono, spesso più velocemente del supporto che può essere offerto dall‘IDE
- spesso da progetto a progetto il framework cambia
- i framework sono complessi e necessitano la familiarità con il pattern MVC, con XML, HTML, JSP, Tag Libraries, Servlet, etc.
Il risultato ottenuto sarebbe un‘ applicazione solida, ma, probabilmente non lo otterremmo agevolemente e in maniera rapida. Inoltre il costo di manutenzione risulterebbe essere non trascurabile.
Nell‘ eventualità in cui il contesto renda necessario il ricorso ad applicazioni J2EE di una certa complessità , la scelta risulterebbe essere quasi obbligata nell‘utilizzo di Java Enterprise. Nelle altre situazioni possiamo valutare ulteriori possibilità .
Al fine di ridurre i tempi iniziali di sviluppo potremmo optare per PHP.
La sistematizzazione del codice in un linguaggio di scripting di questo tipo (anche se OO con PHP5), pressochè privo di validi framework realmente MVC, uniti alla non completa disponibilità di strumenti a supporto delle varie fasi di sviluppo (che necessariamente ci impongono self-discipline nelle varie fasi del processo) ci fanno ponderare con attenzione tale scelta.
O comunque ci fanno relegare tale scelta ad alcune situazioni particolari come p.e. estensione della miriade di applicazioni Open Source disponibili.
Potremmo optare per alternative sicuramente molto meno diffuse, e non proprio Object-Oriented, quali Phyton o Perl. Oppure… potremmo a questo punto valutare di prendere seriamente in considerazione l‘ipotesi Ruby.
Uno dei punti di forza di Ruby è senza dubbio velocità di sviluppo e la semplicità di manutenzione
Le parole di Mike Clark a riguardo “…programming in any language other than Ruby will feel like you‘re pushing the rope”testimoniano la facilità della programmazione in Ruby.
Nel prosieguo cercheremo di fornire esaustive delucidazioni e di spiegare il perchè di tale affermazione.
Caratteristiche del linguaggio (da Java Developer)
Ruby è un linguaggio Object Oriented. Linguaggio semplice da comprendere e da programmare.
Qualsiasi cosa viene manipolata in Ruby è un oggetto e lo stesso dicasi del risultato della manipolazione.
Tanto per fare un esempio il numero 1 è un istanza della classe Fixnum.
Uno sviluppatore Java si dovrebbe trovare abbastanza a proprio agio con Ruby visto che si tratta di linguaggio OO con una serie di caratteristiche che lo fanno sentire particolarmente a proprio agio, (mi riferisco alla presenza di classi, ereditarietà , variabili d‘istanza e di classe, ecc.). Ciò che però deve essere allontanato dalla mente del programmatore Java è la tentazione di utilizzare un Java-like coding style, cosa che, soprattutto all‘inizio, risulta essere un‘ attrattiva piuttosto forte.
Prima di passare alla fase di codifica facciamo un brutale, e ovviamente non esaustivo, elenco di caratteristiche di Ruby in un‘ ottica di confronto con Java. Questo ci servirà ad inquadrarlo avendo come punto di partenza la conoscenza del linguaggio di Sun.
Premettendo che Java è un linguaggio maturo, testato ma anche molto “verboso”, ciò che si noterà abbastanza rapidamente passando a Ruby è la riduzione drastica delle righe di codice scritto.
Elenchiamo le caratteristiche principali che accomunano o differenziano i due linguaggi.
Ruby, come Java
- è un linguaggio interpretato
- è un linguaggio OO (Object Oriented)
- dispone di un Garbage Collector per la gestione della memoria
- è portabile ed è utilizzabile con i principali sistemi operativi
- ha oggetti fortemente “tipati”
- dispone di uno strumento di documentazione del sorgente (RDoc) molto simile a JavaDoc
- ha “accessori” publici, protetti e privati, anche se il loro significato è diverso dai più comuni linguaggi OO e da Java
A differenza di Java
- il codice non necessità di compilazione.
- non è presente la dichiarazione del tipo
- è presente la keyword end per terminare classi, metodi e strutture in genere
- non sono presenti primitive
- non sono presenti interfacce, ma dispone di mix-in
- non è possibile fare overloading dei metodi
- ma soprattutto è “dynamically typed”. e vedremo in seguito quale è l‘impatto di questa ultima caratteristica.
Teniamo a mente quanto sopra riportato e passiamo all‘installazione dell‘ambiente, operazione preliminare per arrivare alla codifica.
Installiamo Ruby
Ruby è molto semplice da installare sotto qualsiasi piattaforma (Windows, Linux, Mac OS X, ecc).
A volte è addirittura disponibile nei pacchetti del sistema operativo (questo è il caso di alcune distribuzioni Linux e Mac OS X).
Nelle altre situazioni è necessario effettuare il download della distribuzione binaria e procedere quindi alla sua installazione.
In ambiente Windows è disponibile l‘installer al link http://rubyinstaller.rubyforge.org.
L‘installazione risulta essere molto rapida e consente di avere a disposizione non solo l‘interprete per Ruby, ma anche Ruby Gem e FreeRIDE, un IDE basico per gestire progetti.
Per alcune distribuzioni Linux “RPM-based” si può trovare a http://rpmfind.net, mentre per distribuzioni basate su dpkg Debian si può utilizzare il gestore di pacchetti apt per effettuare l‘installazione.
È possibile anche, per chi non si volesse perdere le ultimissime novità , effettuare il download del source (da http://www.ruby-lang.org) ed effettuare la relativa compilazione ed installazione.
Irb
Merita una piccola citazione per l‘importanza che ricopre il suo utilizzo.
Irb è uno strumento che consente di immettere codice Ruby e visualizzare immediatamente il risultato dell‘esecuzione. Una sorta di scrapbook interattivo.
Viene installato automaticamente con l‘installazione base e risulta particolarmente utile per fare delle prove on-the-fly.
Running Ruby
Configurato l‘ambiente siamo pronti per eseguire programmi.
Creiamo l‘ Hello World del caso in un file helloworld.rb che conterrà
puts "Hello World!!!"
e quindi lanciamo
ruby ./helloworld.rb
L‘output che otteniamo sarà
Hello World!!!
La riduzione del codice necessari ad ottenere il risultato è decisamente considerevole!
Let‘s code
Scriviamo la nostra prima classe.
class Automobile
def initialize(marca, modello)
@marca=marca
@modello=modello
end
end
Creiamo una istanza della nostra prima classe
automobile = Automobile.new("Honda","Jazz")
Aggiungiamo un primo metodo alla classe
def componi_nome
"Auto: #@marca--#@modello"
end
Quindi invochiamo il metodo
nome_auto=(automobile.componi_nome)
e quindi mandiamolo a video
puts(nome_auto)
Il risultato sarà :
Auto: Honda-Jazz
Naming Convention
Come si evidenzia dalle prime righe di codice riportate notiamo che il nome della classe inizia con lettera maiuscola (Automobile).
Notiamo inoltre che il corpo del metodo viene definito dalla keyword
def
seguita dal nome del metodo e termina con
end
Una variabile preceduta da @ è una variabile di istanza.
@variabile_istanza
Per una variabile di classe avremmo avuto @@ seguite dal nome della variabile
@@variabile_di_ classe
Per una variabile globale avremmo avuto
$variabileglobale
o
$VARIABILE_GLOBALE
Il simbolo # precede il commento. Anche se all‘interno di una stringa #[espressione] viene sotituito da [espressione] come nell‘esempio sopra riportato al metodo componi_nome.
Estendiamo la classe
Creiamo una classe che estende Automobile.
class Utilitaria < Automobile
def componi_nome
super + " che utilitaria!!!!"
end
end
Quindi aggiungiamo:
automobile = Utilitaria.new("Honda","Jazz")
nome_auto=(automobile.componi_nome)
puts(nome_auto)
Il risultato sarà :
Auto: Honda--Jazz che utilitaria!!!!
Qualche riga di codice di esempio
Introduciamo la prima struttura di controllo. Il fine è di riuscire a muoverci agevolmente nelle situazioni che incontreremo a seguire.
Le strutture di controllo, unite a Iteratori e Blocchi sono “core concepts” in Ruby. Da notare che l‘utilizzo continuo del linguaggio ci porterà sempre più ad approccio da “Rubyst” che ci allontanerà da una modalità classica di implementazione.
Nella classe Automobile aggiungiamo il metodo definisci_valore con il parametro livello (utilizziamo la notazione priva di parantesi).
def definisci_valore livello
@livello=""
if livello == 0
@livello="Scarso"
else
@livello="Buono"
end
end
Quindi invochiamo il metodo
livello_auto=(automobile.definisci_valore(3))
3.times{puts(livello_auto)}
L‘ouput sarà ovviamente
Buono
Buono
Buono
Evidenziamo che avremmo potuto scrivere il contenuto del metodo come
if l == 0: @livello="Scarso"
else @livello="Buono"
end
oppure
@livello = if l == 0 then
"Scarso"
else
"Buono"
end
Provate e divertitevi!!!
Non abbiamo mai specificato in maniera esplicita un “return value”. Potremmo farlo nel caso ce fosse bisogno, altrimenti sarà restituita l‘ultima variabile valorizzata nel metodo. Ciò è molto comodo e da non dimenticare.
Passiamo ad un esempio un pò più articolato.
Facendo un salto dalle automobili al regno animale, creiamo una classe completa dei suoi attributi e dei suoi accessori pubblici.
class Animale
def initialize(nome, classe)
@nome=nome
@classe=classe
end
def nome
return @nome
end
def classe
return @classe
end
end
Avremmo potuto utilizzare una notazione più snella per gli accessori.
class Animale
def initialize(nome, classe)
@nome=nome
@classe=classe
end
attr_reader :nome,:classe
end
Questo consente di elencare velocemente tutti gli attributi ai quali si ha necessità di accedere in lettura (attr_reader) o in scrittura (attr_writer) semplicemente riportandoli separati da virgola come sopra riportato.
Scriviamo qualche riga di codice puramente didattico. È nostra intenzione leggere il contenuto di un file, scorrerlo, introdurre la classe Array, scrivere degli esempi di cicli e di operazioni sulle stringhe. Se poi avessimo intenzione di estendere o comprendere al meglio il nostro codice possiamo far riferimento alla documentazione RDoc dell‘installazione di base per avere degli spunti in merito.
Predisponiamo un file testo “filetoread” con il seguente contenuto (o con un contenuto che abbia la medesima formattazione)
Alligatore Rettile
Falco Uccello
Leone Mammifero
Mosca Insetto
Quindi creiamo il file esegui.rb con il seguente contenuto
require ‘animale‘
# Dichiariamo un Array
@animali=Array.new
# Facciamo un ciclo sugli elementi del file ‘filetoread‘
File.open("filetoread") do |elemento|
# Scorriamo ogni riga del file
elemento.each do |riga|
# Il primo elemento della riga è il nome,
# il secondo la classe animale.
# Utilizziamo le espressioni regolari al fine di
# separare le due stringhe
nome, classe = riga.chomp.split(/s* s*/)
# Istanziamo un Animale
animale = Animale.new(nome,classe)
# Aggiungiamo gli elementi all‘Array
@animali.push(animale)
# Lo facciamo di nuovo con una notazione diversa
@animali << animale
end
end
# Operiamo sull‘Array e facciamo un reverse
@animali=@animali.reverse
# Creiamo un file in cui scrivere
f = File.new("filetowrite", "w")
# Prendiamo ogni elemento Animale
# e scriviamo nome e classe nel file
@animali.each { |animale|
f.syswrite(animale.classe + " " + animale.nome + " ")
}
Lanciamo ruby ./esegui.rb.
Andiamo a vedere il contenuto del file creato “filetowrite”. Dovrebbe contenere
Insetto Mosca
Insetto Mosca
Mammifero Leone
Mammifero Leone
Uccello Falco
Uccello Falco
Rettile Alligatore
Rettile Alligatore
Se analizziamo il codice scritto, supportati anche dai commenti, abbiamo introdotto una serie di espressioni e concetti non menzionati in precedenza.
Abbiamo utilizzato la keyword require che consente di importare nel file riferimenti a classi esterne, la classe Array e la sintassi @array.each { |arrayelement| …do something on each element…}, le operazioni sulla classe String (anche ricorrendo a Regular Expression), l‘utilizzo della classe File utilizzata per leggere e scrivere nei file
Semplice… e divertente.
Vi invitiamo pertanto a scaricare l‘allegato “ruby-1.zip” dal menu in alto a sinistra.
Conclusioni
Abbiamo fatto una rapida panoramica di dove si colloca Ruby, in particolare visto da un Java Programmer. Siamo passati all‘installazione dell‘ambiente e abbiamo fatto “girare” le nostre prime righe di codice iniziando a comprendere la facilità di utilizzo. Siamo quindi pronti per approfondire il linguaggio. Dobbiamo solo aspettare… il prossimo articolo.
Riferimenti
[1] Dave Thomas, “Programming Ruby. The Pragmatic Programmer‘s Guide”, Pragmatic Bookshelf, 2004
http://www.pragmaticprogrammer.com
[2] Dave Thomas, “Agile Software Development with Rails. The Pragmatic Programmer‘s Guide”, Pragmatic Bookshelf, 2005
http://www.pragmaticprogrammer.com
[3] Edward Lecky-Thompson, Heow Eide-Goodman, Steven D. Nowicki, Alec Cove, “Professional PHP5”, Wrox, 2004
[4] Ruby homepage
[5] Ruby Central… the source for Ruby
[6] Ruby Installer
http://rubyinstaller.rubyforge.org
[7] Clarkware Consulting
[8] Obie Fernandez Blog
http://www.jroller.com/page/obie
[9] Jim Weirich, “10 Things Every Java Programmer Should Know About Ruby”
http://onestepback.org/articles/10thingshttp://onestepback.org/articles/10things
[10] An Interview with the Creator of Ruby
http://www.linuxdevcenter.com/pub/a/linux/2001/11/29/ruby.html
[11] Apache Struts
[12] Apache Tapestry
[13] FreeRIDE
Laureato in Scienze Statistiche e particolarmente attento alle novità che lo circondano, dal 1998 lavora nel settore dell‘IT. Prevalentemente coinvolto in progetti legati a Java, da un paio d‘anni è interessato al mondo Open Source. Dopo aver accumulato una notevole esperienza presso vari clienti e con vari ruoli (da programmatore a coordinatore gruppo di sviluppo), attualmente lavora in Ancitel S.p.A.