65
Imparando Ruby... Introduzione alla programmazione con Ruby a cura di Stefano Sasso stefano(at)gnustile.net Versione 1.0 - 10 Febbraio 2009

Imparando Ruby

Embed Size (px)

DESCRIPTION

Guida alla programmazione in ruby partendo da zero: spiegazioni, esempi, esercizi.

Citation preview

Page 1: Imparando Ruby

Imparando Ruby...

Introduzione alla programmazione con Ruby

a cura di

Stefano Sasso

stefano(at)gnustile.net

Versione 1.0 - 10 Febbraio 2009

Page 2: Imparando Ruby

Indice

I Le Basi 1

1 Introduzione a Ruby 2

1.1 Interprete interattivo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2

1.2 Interprete non interattivo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2

1.3 La prima prova . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

2 Il linguaggio Ruby 4

2.1 Metodi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4

2.2 Assegnazione di variabili . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

2.3 Tipi di dato numerici . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

2.4 I commenti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

2.5 Le costanti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

2.6 Tutti i metodi di un oggetto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

2.7 Standard input + Stringhe di caratteri . . . . . . . . . . . . . . . . . . . . . . . . . 8

2.8 Conversione tra diversi tipi di dato . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

2.9 Dati booleani . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

2.10 Enunciati decisionali - IF/UNLESS . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

2.11 Altro tipo di dato: gli array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

2.12 Cicli di istruzioni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

2.12.1 while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

2.12.2 for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

2.12.3 blocco each - blocco times . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

2.13 Ancora array... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

2.14 Ancora stringhe... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

2.15 Definizione di metodi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

2.16 Semplici operazioni con file di testo . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

2.16.1 lettura di un file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

i

Page 3: Imparando Ruby

2.16.2 scrittura di un file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

2.16.3 lavorare con il filesystem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

2.17 Altri oggetti utili . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

2.17.1 La classe ’Time’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

2.17.2 La classe ’Hash’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

3 Programmazione orientata agli oggetti 23

3.1 Definizione di classe e oggetto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

3.2 Creazione di classi personalizzate . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

3.2.1 costruzione di una classe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

3.2.2 variabili di istanza . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24

3.2.3 ereditarieta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27

3.3 Modifica di classi esistenti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

3.4 Gestione degli errori - Eccezioni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

4 Uso di librerie particolari 32

4.1 Gemme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

4.2 Lavorare con indirizzi IP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

4.3 YAML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

4.4 Inviare mail via SMTP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

4.5 Accesso a database MySQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34

4.6 Accesso a database SQLite . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35

4.7 Accesso a database KirbyBase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36

4.8 ActiveRecord . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37

4.9 File XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38

4.9.1 xmlsimple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38

4.9.2 rexml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

4.10 Networking a basso livello . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

II Programmazione web di base 46

5 CGI 47

5.1 Introduzione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47

5.2 CGI in ruby . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47

5.3 CGI + ERB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48

5.4 mod ruby . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

ii

Page 4: Imparando Ruby

III Ruby on Rails 51

A Esercizi e soluzioni 53

A.1 Semplici operazioni con stringhe . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53

A.1.1 Esercizio 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53

A.2 Metodi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53

A.2.1 Esercizio 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53

A.2.2 Esercizio 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54

A.3 Operazioni con Array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55

A.3.1 Esercizio 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55

A.3.2 Esercizio 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56

A.3.3 Esercizio 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56

A.4 Operazioni con Hash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57

A.4.1 Esercizio 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57

A.5 Operazioni con File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58

A.5.1 Esercizio 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58

A.6 Operazioni con Oggetti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59

A.6.1 Esercizio 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59

A.6.2 Esercizio 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59

A.6.3 Esercizio 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60

A.6.4 Esercizio 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60

A.6.5 Esercizio 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61

iii

Page 5: Imparando Ruby

Parte I

Le Basi

1

Page 6: Imparando Ruby

Capitolo 1

Introduzione a Ruby

Ruby e un linguaggio di programmazione interpretato, questo significa che passando un file ditesto contentente codice ruby all’interprete, questo viene eseguito.E possibile scrivere spezzoni di codice ruby anche in un interprete interattivo, il che significa cheappena uno spezzone di codice viene inserito, lo stesso viene eseguito.

1.1 Interprete interattivo

E possibile lanciare l’interprete interattivo con

~# irb

ci comparia quindi un prompt simile al seguente:

irb(main):001:0> _

1.2 Interprete non interattivo

Per lanciare l’interprete non interattivo e necessario scrivere all’interno di un file di testo il codiceruby che vogliamo venga eseguito.Possiamo poi lanciare l’interprete con

~# ruby nomedelfile.rb

oppure, piu semplicemente, inserire in testa al file contenente il codice ruby la riga

#!/usr/bin/env ruby

come qualsiasi altro script in ambiente UNIX.

2

Page 7: Imparando Ruby

1.3 La prima prova

Facciamo subito la prima prova: creiamo il file prova1.rb con un editor di testo, e al suo internoscriviamo

puts "Ciao mondo!"

poi eseguiamolo con

~# ruby prova1.rb

allo stesso modo apriamo irb, e al prompt digitiamo

puts "Ciao mondo!"

il nostro interprete sara quindi in questa situazione:

irb(main):001:0> puts "Ciao mondo!"

Ciao mondo!

=> nil

irb(main):002:0> _

Analizziamo ora questo semplicissimo programma:puts e una funzione del linguaggio che ci consente di mandare qualche oggetto allo standardoutput.Ruby e un linguaggio orientato agli oggetti ; per il momento consideriamo gli oggetti come delle“cose” che vengono manipolate da un programma.

In questo caso l’oggetto in questione, da stampare a schermo, e una stringa di caratteri, delimitatada doppi apici (′′). Equivalente sarebbe stato delimitarla da apici singoli (’).

3

Page 8: Imparando Ruby

Capitolo 2

Il linguaggio Ruby

2.1 Metodi

Abbiamo visto prima che in Ruby esistono delle funzioni, comunemente detti metodi che compionouna qualche azione. Ad esempio puts serve per stampare a schermo un oggetto.Esistono piu modi per richiamare un metodo da un programma: con o senza parentesi tonde ( ).

metodo parametro1, parametro2

metodo(parametri1, parametro2)

parametro1 e parametro1 sono degli oggetti su cui il metodo specifico andra ad operare. Il numerodi oggetti “passabili” a un metodo e deciso dal progettista del metodo stesso.

Nel contesto di prima, ad esempio,

puts "Ciao mondo!"

e

puts("Ciao mondo!")

sono del tutto equivalenti. Il metodo puts puo lavorare, come gia visto, su degli oggetti stringadi caratteri, ma anche, ad esempio, su oggetti del tipo numero intero (ma anche altri):

puts 12

puts(12)

Ricordiamoci che in Ruby, tutto e un oggetto, anche i numeri.

Su ogni oggetto e possibile invocare dei metodi, che lavoreranno direttamente su quell’oggetto. Epossibile anche invocare dei metodi su dei metodi ; questo perche un metodo ritorna al programmasempre un oggetto, quindi in realta e come invocare un metodo su un oggetto. vediamo un esempio(lavorando da irb):

3.next

4

Page 9: Imparando Ruby

in questo caso invochiamo il metodo next sull’oggetto 3. Il metodo ritornera come valore di uscita4. Ora, invocando il metodo next sull’oggetto 4, otterremo 5.Lo stesso risultato si sarebbe potuto ottenere con

3.next.next

E da ricordare che ogni tipo di oggetto (chiamato classe) ha i suoi metodi, anche se piu oggettipossono avere dei metodi che si chiamano nello stesso modo.

Ad esempio

"a".next

non e lo stesso metodo visto prima (infatti, la classe di partenza e diversa), ma si chiama nellostesso modo.Ad esempio,

"ciao".length

ha senso (lunghezza della stringa), mentre

2.length

no.

Ah, nel frattempo abbiamo visto che l’operatore per invocare dei metodi su degli oggetti e il .(punto).

Nel caso di numeri, le operazioni matematiche sono anch’esse metodi.Ad esempio

3+4

viene automaticamente convertito da ruby in

3.+(4)

ovvero viene invocato il metodo che si chiama + sull’oggetto 3.Idem per -, *, /, **

I metodi che agiscono su un oggetto di norma ritornano un altro oggetto; per ritornare non siintende ritornare qualcosa all’utente, bensı ritornare qualcosa (un oggetto) al programma.

(Capiremo meglio il significato di ritornare andando avanti con le nozioni imparate.)

Ad esempio, i metodi

3.next

3.+(2)

ritornano, rispettivamente, oggetti numero 4 e 5.

A volte i metodi possono ritornare un oggetto particolare, l’oggetto nil, che sta per nulla. Adesempio, nel caso di puts, non ha senso che venga ritornato un qualcosa, quindi ritornera nil.

5

Page 10: Imparando Ruby

2.2 Assegnazione di variabili

Un operatore particolare e l’operatore di assegnazione, =, che consente di salvare il valore (oggetto)ritornato da un metodo all’interno di un’area della memoria, chiamata variabile.

Ad esempio

risultato=2+5

L’area della memoria da noi chiamata risultato conterra ora l’oggetto 7, ritornato dal metodo+ con parametro 5 invocato sull’oggetto 2.Su un’area di memoria (variabile) e possibile invocare tutti i metodi propri dell’oggetto che vi ememorizzato:

risultato2=risultato.next

la variabile risultato2 conterra ora l’oggetto numerico 8.

2.3 Tipi di dato numerici

Visto che ne stavamo parlando, vediamo ora come sono esprimibili i tipi di dato numerici.Fondamentalmente e possibile esprimere un numero in due notazioni:

• Numero intero (Classe: Fixnum)

• Numero a virgola mobile (Classe: Float)

vediamo qualche esempio per comprendere meglio la “divisione”:

3 (classe: Fixnum)

-5 (classe: Fixnum)

4.33564 (classe: Float)

-4.5 (classe: Float)

1.34E98 (classe: Float)

-1.43E12 (classe: Float)

1.2E-5 (classe: Float)

-3.6E-3 (classe: Float)

ad esempio, gli oggetti della classe Float non hanno il metodo next, se hai studiato matematicacapirai il perche :)

2.4 I commenti

I commenti sono parti di testo all’interno del codice, non vengono eseguiti dall’interprete mapossono essere utili per annotare cose che servono al programmatore.

6

Page 11: Imparando Ruby

# commento su una riga

=begin

commento

su

piu‘

righe

=end

puo essere utile ad esempio, in questo caso

# la variabile a contiene l’anno corrente

a=MioOggetto.mio_metodo

2.5 Le costanti

Le costanti sono come delle variabili e si usano per contenere dei valori che devono venir ripetutipiu volte all’interno del programma.Si consiglia di usare nomi simbolici e descrittivi per le costanti, per facilitare la leggibilita delprogramma.Una costante si rappresenta con il nome formato da sole lettere maiuscole.esempio, conversione Euro:

FATTORE_DI_CONVERSIONE=1936.27

euro=lire/FATTORE_DI_CONVERSIONE

2.6 Tutti i metodi di un oggetto

Come detto prima, ogni oggetto ha i suoi metodi. Per sapere quali sono i metodi disponibili in unoggetto basta cercare su google

ruby+Classe

ad esempio

ruby+String

il primo risultato sara qualcosa del tipo

http://www.ruby-doc.org/core/classes/String.html

Proviamo a darci una rapida occhiata.

Se abbiamo un oggetto, ma non ne conosciamo il tipo, possiamo invocare il metodo class

sull’oggetto stesso per conoscerne la classe di “partenza”.

5.class

"ciao".class

3E2.class

7

Page 12: Imparando Ruby

2.7 Standard input + Stringhe di caratteri

Consideramo questo breve programmino:

puts "Come ti chiami?"

nome = gets

nome.chomp!

puts "Ciao, " + nome

puts l’abbiamo gia visto, gets invece e una funzione che recupera una riga dallo standard inpute la ritorna in una variabile di tipo stringa.Perche poi invochiamo il metodo chomp! su quell’oggetto?

Perche gets ritorna l’input inserito COMPRENSIVO di ritorno a capo (carattere speciale \n ),quindi chomp! serve a rimuovere proprio questo carattere dalla fine della stringa.

Volendo, si sarebbe anche potuto scrivere

nome = gets.chomp

la differenza tra chomp e chomp! ? vedere tra i metodi nella documentazione ufficiale :)comunque, per convenzione, i metodi! agiscono direttamente sull’oggetto, mentre i metodi neritornano una copia (modificata). vediamo un esempio:

a="ciao\n"

b=a.chomp

# a vale "ciao\n"

# b vale "ciao"

a.chomp!

# a vale "ciao"

Nel programmino di prima vediamo anche come sia possibile concatenare 2 stringhe, ovvero il“Ciao, “ e nome. (sempre l’operatore-metodo +)Sarebbe anche stato possibile includere la stringa nome direttamente all’interno della stringadefinita con “ “:

puts "Ciao, #{nome}"

La serie di caratteri # permette di includere in una stringa il contenuto di una variabile.esempio:

var1="bao"

var2="ciao #{var1}"

var2 conterra quindi “ciao bao“.

8

Page 13: Imparando Ruby

2.8 Conversione tra diversi tipi di dato

Su alcuni oggetti -tipi di dato e possibile invocare dei metodi per la conversione in altri oggetti -tipidi dato.Questi metodi si chiamano to , ad esempio

• .to i - converte in numero intero

• .to f - converte in numero con virgola

• .to s - converte in stringa

esempio:

k="ccc".to_i #=> k vale 0

k="ccc".to_f #=> k vale 0.0

k="ccc".to_s #=> k vale "ccc"

k="c123d4".to_i #=> k vale 0 (la stringa non e‘ convertibile)

k="123".to_i #=> k vale 123

k=123.to_s #=> k vale "123"

k=123.to_f #=> k vale 123.0

k=1.23.to_i #=> k vale 1

2.9 Dati booleani

Oltre a quelli gia visti, ovvero numeri e stringhe di caratteri, esiste un altro tipo di dato “fonda-mentale”: il tipo booleano. I dati booleani rispecchiano l’algebra di Boole, per cui il valore puoessere solamente true o false (vero o falso, 1 o 0); e vengono usati soprattutto negli enunciatidecisionali (che vedremo in seguito).

I principali metodi che ritornano un oggetto booelano, e che si trovano in gran parte delle classiesistenti, sono i metodi di comparazione:

• == uguale a

• < minore di

• > maggiore di

• <= minore o uguale a

• >= maggiore o uguale a

• != diverso da

ad esempio:

confronto1= 4==5

confronto2= 4<5

confronto3= "a">="c"

9

Page 14: Imparando Ruby

ovviamente, confronto1 sara false, confronto2 true e confronto3 false.

Nell’algebra booleana sono presenti gli operatori e, o e “negazione“; dunque anche i tipi di datobooleano dispongono di questi operatori:

• and - e

• or - o

• ! - “negazione“

vediamo qualche esempio:

e1= 3<4 and 4<5 #=> true

e2= !(3<4) #=> false

e3= 3<4 or 5>7 #=> true

e4= "a"<"d" and !(3>6) #=> true

e5= !("h"<"c" and "h"<"z") #=> true

e6= ((2>=5) or !("a"!="r")) and e4 #=> false

2.10 Enunciati decisionali - IF/UNLESS

L’enunciato if, o il suo negato unless viene adoperato nel caso si debbano eseguire delle istruzionisolo in presenza di una determinata condizione (oggetto booleano)Ad esempio:

if (2 < 3)

puts "minore!!!"

end

ne abbiamo anche aprofittato per vedere che ogni blocco di codice - in questo caso quello da eseguirenel caso la condizione sia vera - deve terminare con la parola chiave end.Se pero, come in questo caso, dobbiamo eseguire una sola istruzione, possiamo compattare tuttoin un’unica riga, evitando percio il blocco di codice:

puts "minore!!!" if (2 < 3)

Ora, nel caso volessimo eseguire il blocco di codice solo nel caso la condizione di controllo sia falsa,abbiamo due modi di procedere: 1) usando la negazione propria dell’algebra booleana; 2) usandola parola chiave unless. Vediamolo insieme:

if !(2 < 3)

puts "non minore"

end

e del tutto equivalente a

10

Page 15: Imparando Ruby

unless (2 < 3)

puts "non minore"

end

Come visto prima per l’if, se abbiamo una sola istruzione da eseguire, anche l’unless puo esserecompattato su una sola riga:

puts "non minore" unless (2 < 3)

Se alla condizione si vuole realizzare un’alternativa, si puo utilizzare la clausula else, che sta peraltrimenti :

if condizione_booleana

puts "condizione vera"

else

puts "condizione falsa"

end

in questo caso si chiude con l’end il blocco totale, comprensivo di else.La stessa cosa vale per unless:

unless condizione_booleana

puts "condizione falsa"

else

puts "condizione vera"

end

Nel caso di if e anche possibile aggiungere degli “step” intermedi; ipotizziamo di voler confrontaredue numeri:

if n1 < n2

puts "n1 minore di n2"

elsif n1 == n2

puts "n1 uguale a n2"

else

puts "n1 maggiore di n2"

end

2.11 Altro tipo di dato: gli array

Prima di vedere i cicli iterativi ci conviene dare uno sguardo agli array:un array e un insieme di oggetti, a livello di codice delimitati da [ e ]

ad esempio, per “creare” un oggetto di tipo array vuoto:

array=[]

11

Page 16: Imparando Ruby

mentre, per creare un array contentente gli oggetti-numeri 1, 2, 3 e 4

array=[1, 2, 3, 4]

Se ad un array esistente volessimo aggiungere in coda un altro elemento e sufficiente usare <<:

array << 6

array << 5

ora array sara [1, 2, 3, 4, 6, 5].

In realta << e un “sinonimo” del metodo push invocato su un array,quindi

array << 8

avra lo stesso effetto di

array.push 8

Se volessimo ordinare gli elementi al suo interno

array.sort!

array sara ora [1, 2, 3, 4, 5, 6, 8]

E possibile accedere ad uno specifico elemento di un array sempre con [], ricordandoci che ogni og-getto ha un “indice” che lo identifica, e che gli indici partono da 0 e arrivano a numero di elementi -1in questo caso:

primo_elemento=array[0]

primo elemento conterra quindi 1in maniera simile, nel caso volessimo sovrascriverne il valore

array[0]=-1

Un’altro oggetto particolare, simile agli array, e l’oggetto Range, (intervallo) che si definisce inquesto modo:

intervallo=(1..10)

dove intervallo rappresenta ora tutti i numeri da 1 a 10.Se ora volessimo avere un array contenente tutti questi numeri basterebbe invocare il metodo.to a sul range:

array=(1..10).to_a

e possibile una cosa simile anche con caratteri:

array=("a".."d").to_a

array sara quindi [’a’, ’b’, ’c’, ’d’]

Un array puo contenere al suo interno qualsiasi tipo di oggetto (quindi anche un array)

12

Page 17: Imparando Ruby

2.12 Cicli di istruzioni

2.12.1 while

Il ciclo while serve a ripetere delle istruzioni (blocco di codice) finche una condizione e vera

count = 0

while count < 10

puts "count = #{count.to_s}"

count = count+1

end

2.12.2 for

Il ciclo for, a differenza del while, esegue delle istruzioni per un determinato numero di oggetti(array o range):

array=[1, 2, 3, 4, 5]

for n in array

puts "Numero: #{n.to_s}"

end

2.12.3 blocco each - blocco times

Tuttavia esiste un modo piu efficace per ottenere il risultato di un ciclo for:tutti gli oggetti di tipo array o range possiedono il metodo each.Il metodo each e un metodo di tipo particolare, esso infatti non richiede parametri in ingresso,ma richiede che dopo la sua invocazione sia presente un blocco di codice da eseguire:eccone un esempio:

array=[1, 2, 3, 4, 5]

array.each do |n|

puts "Numero: #{n.to_s}"

end

simile al metodo each di un array/range, e il metodo times dei numeri interi:

5.times do |n|

puts "Numero: #{n.to_s}"

end

A volte nella documentazione si trova qualcosa di simile a

array.each { |n|

puts n

}

13

Page 18: Imparando Ruby

o meglio ancora

array.each {|n| puts n }

ma il significato e sempre lo stesso.

Personalmente trovo sia piu leggibile delimitare il blocco con do e end.

2.13 Ancora array...

Sempre per restare in tema di array vediamo altri metodi utili, invocabili su array:

• to s - converte un array in stringa

[1, 2, 3].to_s #=> "123"

• join - simile a to s, ma permette di definire una stringa da inserire tra i vari oggetti in fasedi concatenazione

[’a’, ’b’, ’c’].join(", ") #=> "a, b, c"

[’a’, ’b’, ’c’].join("--") #=> "a--b--c"

• push - vista prima, inserisce un oggetto in coda ad un array

array=[1,2,3]

array.push 4 #=> [1,2,3,4]

• pop - estrae dall’array l’ultimo elemento

array=[1,2,3]

ultimo=array.pop # (array vale ora [1,2] e ultimo vale 3)

• first - ritorna il primo elemento dell’array, senza estrarlo

[1,2,3].first #=> 1

• last - ritorna l’ultimo elemento dell’array, senza estrarlo

[1,2,3].last #=> 3

• length o size - ritorna la dimensione di un array

[1,2,3].size #=> 3

• sort e sort! - ricordi il ! nelle funzioni?la prima ritorna un nuovo array, contenente gli elementi dell’array di partenza ordinati; laseconda ordina in-place

ar1=[3, 5, 1]

ar2=ar1.sort # (ar2 vale [1, 3, 5], mentre ar1 resta invariato)

ar1.sort! # (ar1 vale [1, 3, 5])

• include? - ritorna un valore booleano, vero se l’elemento cercato e incluso, altrimenti falso

14

Page 19: Imparando Ruby

ar1=[1, 2, 3]

ar1.include? 2 #=> true

ar1.include? 8 #=> false

altri metodi consultabili su

http://www.ruby-doc.org/core/classes/Array.html

2.14 Ancora stringhe...

Vediamo ora alcuni metodi utili invocabili su una stringa:

• split - si puo considerare come l’inverso di join applicato ad un array, infatti divide lastringa in elementi di un array

"a--b--c".split("--") #=> ["a", "b", "c"]

• [] - e una funzione particolare che permette di accedere a determinati caratteri della stringa.Se invocata con un numero ritorna il byte relativo, mentre se invocata con un “range” ritornala serie di caratteri relativa:

a = "hello there"

a[1] #=> 101 (byte in posizione 1)

a[1].chr #=> "e" (byte in posizione 1 convertito in carattere)

a[1,3] #=> "ell" (3 caratteri partendo dalla posizione 1 compresa)

a[2,3] #=> "llo" (3 caratteri partendo dalla posizione 2 compresa)

a[1..3] #=> "ell" (caratteri dalla posizione 1 alla posizione 3)

a[-3,2] #=> "er" (2 caratteri dalla posizione 3 partendo da destra)

a[-4..-2] #=> "her" (dalla posizione -4 alla -2)

• []= - cambia un determinato carattere della stringa:

a = "hello there"

a[0]="H" #=> a vale "Hello there"

• <=> - serve a comparare una stringa con un’altra; ritorna -1 se la stringa viene prima(alfabeticamente parlando), 0 se equivalenti, 1 se viene dopo

"abc"<=>"cba" #=> -1

"ciao"<=>"ciao" #=> 0

"ciao"<=>"bao" #=> 1

• + - l’abbiamo gia visto, e l’operatore di concatenazione

"ci"+"ao" #=> "ciao"

• * - ripete la stringa per n volte

saluto="ciao " * 3 #=> "ciao ciao ciao "

15

Page 20: Imparando Ruby

• capitalize e capitalize! - fanno diventare maiuscola la prima lettera, la prima crea unnuovo oggetto, la seconda modifica l’oggetto esistente

a = "ciao"

b=a.capitalize # (b vale "Ciao", a vale ancora "ciao")

a.capitalize! # (a vale "Ciao")

• chomp e chomp! - rimuove carattere di fine linea, la prima crea un nuovo oggetto, la secondomodifica l’oggetto esistente

a = "ciao\n"

b=a.chomp # (b vale "ciao", a vale ancora "ciao\n")

a.chomp! # (a vale "ciao")

• size - determina la lunghezza della stringa

"ciao".size #=> 4

• downcase e downcase! - trasformano tutti i caratteri maiuscoli in minuscoli, la prima creaun nuovo oggetto, la secondo modifica l’oggetto esistente

a = "CIAO"

b=a.downcase # (b vale "ciao", a vale ancora "CIAO")

a.downcase! # (a vale "ciao")

• empty? - ritorna true se la stringa e vuota

"".empty? #=> true

"c".empty? #=> false

• include? - ritorna true se la stringa include una sequenza di caratteri

"ciao".include? "ao" #=> true

"ciao".include? "bo" #=> false

• index - ritorna la posizione della prima occorrenza di una stringa in quella di partenza

"ciao".index "a" #=> 2

"ciao".index "ia" #=> 1

• reverse e reverse! - serve a invertire i caratteri di una stringa, la prima crea un nuovooggetto, la secondo modifica l’oggetto esistente

"stefano".reverse #=> "onafets"

• upcase e upcase! - trasformano tutti i caratteri minuscoli in maiuscoli, la prima crea unnuovo oggetto, la secondo modifica l’oggetto esistente

a = "ciao"

b=a.upcase # (b vale "CIAO", a vale ancora "ciao")

a.upcase! # (a vale "CIAO")

altri metodi consultabili su

http://www.ruby-doc.org/core/classes/String.html

16

Page 21: Imparando Ruby

2.15 Definizione di metodi

Abbiamo visto che le strutture iterative sono molto utili per evitare di dover scrivere codiceripetitivo, un altro modo per evitare di scrivere codice ripetitivo e definire dei propri metodi (ofunzioni).partiamo per esempi:ipotizziamo il seguente programma:

nome="Mirko"

puts "Ciao, #{nome}!"

nome="Stefano"

puts "Ciao, #{nome}!"

vediamo chiaramente che e presente una parte ripetitiva.Possiamo semplificarci la vita creando il metodo saluta, che accetta come parametro in entrataun oggetto da stampare con puts:

def saluta(nome)

puts "Ciao, #{nome}!"

end

saluta(’Stefano’)

saluta ’Mirko’

Partiamo con l’analisi:

un metodo e un blocco di codice, che comincia con

def <nome_del_metodo>(<variabili_in_ingresso>)

ed essendo un blocco deve terminare con end.

All’interno possono starci delle istruzioni standard o chiamate ad altri metodi.Come vediamo, dall’istruzione puts interna al metodo e usata la variabile nome, che e appuntoquella definita in ingresso.

Ora, come possiamo ben vedere, quello che abbiamo scritto e un metodo che non restituisce alcunvalore in uscita.Come fare quindi, se vogliamo far uscire qualcosa da quel metodo?Vediamo questo metodo un po’ piu complesso che, dato un array di numeri interi torna in uscitala somma di tutti i valori presenti:

def somma_valori(array)

somma=0

array.each do |elemento|

somma=somma+elemento

end

return somma

17

Page 22: Imparando Ruby

end

somma=somma_valori([1, 2, 3, 4])

puts somma

notiamo subito prima dell’end l’istruzione return somma, questa e l’istruzione che serve perritornare un valore al programma chiamante.

A volte l’istruzione return puo anche essere omessa, in quanto, se tale, ruby ritornera per defaultil valore dell’ultima assegnazione o chiamata a funzione.Ad esempio, molto banalmente

def banale

a=1

end

puts banale

stampera a video 1

Nel nostro caso quindi avemo potuto scrivere

def somma_valori(array)

somma=0

array.each do |elemento|

somma=somma+elemento

end

somma

end

attenzione: il somma prima di end corrisponde ad un’assegnazione a vuoto, proprio per direa ruby che il valore da tenere nel buffer da ritornare e il valore di somma, altrimenti avrebberitornato l’array di partenza, perche l’ultima istruzione sarebbe stata array.each (che lavoraquindi sull’array di partenza) [in questo caso il blocco viene visto come istruzione unica!!!]

Ma allora, se e possibile farlo implicitamente, perche usare l’istruzione return?Perche il return forza l’uscita dal metodo. Ad esempio:

def prova123(numero)

if numero < 10

return "si"

end

"no"

end

puts prova123(12) => ’no’

puts prova123(9) => ’si’

18

Page 23: Imparando Ruby

omettendo il return invece avrebbe scritto ’no’ anche per il 9, perche l’esecuzione del metodosarebbe continuata, ottenendo cosı come ultimo valore ’no’ in ogni caso.

A un metodo e possibile passare piu di un parametro in ingresso, ad esempio:

def confronto(a, b)

if a<b

puts "a < b"

elsif a==b

puts "a = b"

else

puts "a > b"

end

end

ma nonostante un metodo possa prevedere piu di un parametro in ingresso puo ritornare un solooggetto. Tuttavia, nel caso sentissimo la necessita di tornare piu di un oggetto in uscita possiamoapplicare un piccolo trucco: ritornare un array di oggetti, e usare l’assegnazione multipla:

def ritorno_multiplo

[1, ’ciao’, [1, 2]]

end

numero, stringa, array = ritorno_multiplo

numero ora vale 1, stringa vale ’ciao’ e array vale [1, 2].

2.16 Semplici operazioni con file di testo

2.16.1 lettura di un file

In fase di lettura tutto il file viene caricato come stringa unica, sta poi all’utente recuperare levarie righe.Il separatore di riga e sempre ritorno a capo, carattere speciale “\n“, quindi basta uno split, o,piu semplicemente, each line.

contenuto = File.read ’prova.txt’

# ora leggo le righe con uno split: (righe sara‘ un array)

righe = contenuto.split("\n")

# oppure con each_line:

contenuto.each_line do |riga|

...

end

19

Page 24: Imparando Ruby

2.16.2 scrittura di un file

La scrittura di un file di testo e leggermente piu complicata, in quanto richiede un blocco di codice:

File.open ’prova123.txt’, ’w’ do |f|

f.write "Ciao, sono la riga 1\n" # ricordiamoci il ritorno a capo!

f.write "Ciao, sono la riga 2"

end

2.16.3 lavorare con il filesystem

Vediamo ora alcune funzioni utili per poter lavorare con il filesystem:

• directory listing (con array)

files_txt_directory_corrente=Dir[’*.txt’]

files_immagine_ricorsivi=Dir[’**/*.{JPG,jpg}’]

• spostamento in una directory

Dir.chdir ’/home/stefano/prove_ruby’

• rinominare un file

File.rename ’vecchio_file.txt’, ’nuovo_file.txt’

• cambiare i permessi di un file (solo Unix)

File.chmod(0644, "testfile.txt")

• creazione di una directory

Dir.mkdir ’dir_1’

Dir.mkdir ’dir_2’, 0644

• directory corrente

current_directory=Dir.pwd

• copia di un file

File.copy ’file_sorgente.txt’, ’file_destinazione.txt’

per altri metodi vedere

http://ruby-doc.org/core/classes/File.htmlhttp://ruby-doc.org/core/classes/Dir.html

20

Page 25: Imparando Ruby

2.17 Altri oggetti utili

Vediamo ora altri oggetti che possono essere utili:come introduzione e necessario sapere che per avere una nuova istanza di un oggetto bisognachiamare la classe stessa con il metodo new.

Finora non e stato visto perche ruby introduce delle semplificazioni nei tipi di dato nativi,ad esempio

stringa=""

array=[]

e equivalente a

stringa=String.new

array=Array.new

2.17.1 La classe ’Time’

Rappresenta una data comprensiva di giorno, mese, anno e orario:

adesso=Time.new

adesso_piu_un_minuto=adesso+60

puts adesso #=> Tue Sep 09 11:54:45 CEST 2008

puts adesso_piu_un_minuto #=> Tue Sep 09 11:55:45 CEST 2008

praticamente con l’operatore + vengono sommati i secondi.

Se si vuole creare un oggetto Time per uno specifico momento si puo usare mktime:

puts Time.mktime(2000, 1, 1) #=> Sat Jan 01 00:00:00 CET 2000

puts Time.mktime(1986, 11, 21, 18, 30) #=> Fri Nov 21 18:30:00 CET 1986

ovviamente un oggetto Time ha tutta una serie di metodi di utilita; vediamone brevemente alcunicon un esempio:

adesso=Time.new #=> Tue Sep 09 11:54:45 CEST 2008

giorno=adesso.day #=> 9

mese=adesso.month #=> 9

anno=adesso.year #=> 2008

Come ogni classe, l’elenco completo dei metodi e disponibile a

http://ruby-doc.org/core/classes/Time.html

21

Page 26: Imparando Ruby

2.17.2 La classe ’Hash’

E disponibile anche qui, come array, string, ... un costrutture semplificato,

hash=Hash.new

e infatti equivalente a

hash={}

Un hash e molto simile ad un’array, con la differenza che gli indici possono essere degli oggetti.Vediamola molto simile ad un dizionario, in cui ad una parola corrisponde una definizione.ad esempio:

hash={}

hash[’mirko’]=’porseo’

hash[’stefano’]=’nerd’

Anche come metodi e molto simile ad un array:

http://ruby-doc.org/core/classes/Hash.html

22

Page 27: Imparando Ruby

Capitolo 3

Programmazione orientata agli

oggetti

3.1 Definizione di classe e oggetto

Un oggetto e una “scatola”, con le proprie caratteristiche e i propri comportamenti;ad esempio, un router ha marca e modello, e fa’ determinate cose (connettersi, disconnettersi, ...).Questo puo essere visto come un “oggetto”Informaticamente parlando, per poter creare un tipo di oggetto occorre definire una classe che nedefinisce i metodi e gli attributi.Diminuendo il grado di astrazione e tornando alla programmazione vera e propria possiamo dire cheun oggetto puo essere visto come un contenitore di dati (che a questo punto possiamo identificarecon delle semplici variabili) dotato di una serie di funzioni (i metodi) che definiscono un’interfacciaall’oggetto stesso (azioni da compiersi sull’oggetto).

3.2 Creazione di classi personalizzate

3.2.1 costruzione di una classe

Vediamo subito come e implementata in Ruby la programmazione orientata agli oggetti. Scriviamoun semplice esempio supponendo di dover programmare un videogame di guerra dove tra le altrecose sono presenti dei carri armati e dei camion per il trasporto dei soldati. Rappresentiamo questioggetti in Ruby:

class CarroArmato

def initialize

puts "Sono un nuovo carro armato"

end

end

23

Page 28: Imparando Ruby

class Camion

def initialize

puts "Sono un nuovo camion"

end

end

ca = CarroArmato.new #=> Sono un nuovo carro armato

c = Camion.new #=> Sono un nuovo camion

abbiamo quindi capito che la definizione di una classe inizia con la parola chiave class e terminacon end; initialize e il metodo che viene invocato in fase di creazione di un oggetto (vienechiamato costruttore).

In questo caso, molto banalmente, il costruttore stampa a video l’avvenuta creazione dell’oggetto.

Vediamo ora invece come sia possibile passare dei dati al costruttore; supponiamo quindi di vo-ler definire alcuni dati caratteristici dell’oggetto (carroarmato I carburante e colpi; camion I

carburante e posti passeggeri):

class CarroArmato

def initialize (colpi, carburante)

@carburante = carburante

@colpi = colpi

end

end

class Camion

def initialize (posti, carburante)

@carburante = carburante

@posti = posti

end

end

3.2.2 variabili di istanza

Ma cosa sono quella specie di variabili il cui nome comincia per @?Vengono chiamate variabili di instanza, e una volta inizializzate esse sono visibili a tutti i metodidella classe, e verranno conservate per tutta l’esistenza del singolo oggetto.capiremo poi il motivo della loro importanza.

Ora quindi possiamo creare un’istanza di carroarmato e una di camion:

ca = CarroArmato.new(10, 100)

c = Camion.new(20, 100)

le variabili di istanza @carburante, @posti, @colpi, ... vengono anche detti attributi della classe,e definiscono i dati fondamentali su cui si basa la stessa.

24

Page 29: Imparando Ruby

Per accedere dall’esterno della classe a questi attributi dobbiamo definire degli appositi metodi;ad esempio, per fare in modo di poter capire quanti posti sono presenti nel camion

class Camion

def initialize (posti, carburante)

@carburante = carburante

@posti = posti

end

def posti

@posti

end

end

da notare che il metodo posti si sarebbe anche potuto scrivere (come abbiamo gia visto)

def posti

return @posti

end

(ovviamente sempre all’interno del blocco di codice che definisce la classe)quindi, potremo

c = Camion.new(20, 100)

puts c.posti #=> 20

Abbiamo visto ora come accedere agli attributi di una classe in lettura; anche nel caso volessimoaccederci in scrittura e necessario definire un metodo;rivediamo sempre la classe Camion, con la possibilita di modificare il numero di posti a disposizione:

class Camion

def initialize (posti, carburante)

@carburante = carburante

@posti = posti

end

def posti

@posti

end

def posti=(nuovi_posti)

@posti=nuovi_posti

end

end

il metodo per accedere in scrittura non e nient’altro che un semplicissimo metodo con un parametroin ingresso. Riproviamo quindi ad eseguire:

25

Page 30: Imparando Ruby

c = Camion.new(20, 100)

puts c.posti #=> 20

c.posti=25

# le parentesi tonde () sono facoltative,

# scrivere c.posti=(25) e‘ uguale a c.posti=25

puts c.posti #=> 25

E qui ruby ci viene incontro ancora una volta...

Ipotizziamo voler avere 50 variabili che devono essere accessibili dall’esterno della classe sia in scrit-tura che in lettura... dovremmo quindi scrivere per 50 volte i 2 metodi, variabile, e variabile=...ruby ci viene in aiuto con una determinata parola chiave che ci permette di definire automatica-mente questi due metodi per ogni variabile che vogliamo venga trattata:vediamolo sempre con la classe Camion:

class Camion

attr_accessor :posti

attr_accessor :carburante

def initialize (posti, carburante)

@carburante = carburante

@posti = posti

end

end

e proprio la “parola” attr accessor che permette di definire automaticamente questi due metodiper ogni variabile specificata.

Da notare che per ridurre ancora di piu il codice scritto, invece di

attr_accessor :posti

attr_accessor :carburante

avremmo potuto scrivere

attr_accessor :posti, :carburante

ma... se volessimo che una variabile, chiamata sola lettura fosse accessibile in sola lettura, eun’altra viariabile chiamata sola scrittura fosse accessibile in sola scrittura? Anche in questocaso ruby ci viene in aiuto con due costrutti:

attr_reader :sola_lettura

attr_writer :sola_scrittura

da notare che, praticamente, scrivere

attr_accessor :variabile

26

Page 31: Imparando Ruby

corrisponde esattamente a scrivere

attr_reader :variabile

attr_writer :variabile

(ovviamente il primo caso e molto piu pratico)

Vediamone ora un uso pratico delle variabili di istanza, che non sia il semplice reperimento/impostazione;sempre riguardo la classe Camion:

class Camion

attr_reader :posti

attr_reader :carburante

def initialize (posti, carburante)

@carburante = carburante

@posti = posti

end

def rifornimento (quantita)

@carburante = @carburante + quantita

end

end

Vediamo che il metodo rifornimento va a modificare la variabile di instanza @carburante

c = Camion.new(20, 100)

puts c.posti #=> 20

puts c.carburante #=> 100

c.rifornimento 50

puts c.carburante #=> 150

Ricapitolando un attimo sulle variabili, abbiamo ora capito che ci sono 2 ambiti di visibilita dellestesse:

le variabili che iniziano per @ sono visibili a tutta la classe in cui sono definite,mentre le variabili senza @ sono visibili solo all’interno dei metodi in cui sono definite.

3.2.3 ereditarieta

Ora pero, pensandoci bene, entrambi i due mezzi (camion e carroarmato) sono dei veicoli, e cometale hanno delle cose in comune (ad esempio il carburante);e percio possibile definire una “macro-classe” che li contiene entrambi, quindi possiamo scrivere

27

Page 32: Imparando Ruby

class Veicolo

attr_reader :carburante

def initialize (carburante)

@carburante = carburante

end

def rifornimento (quantita)

@carburante += quantita

end

end

e poi far “discendere” camion e carroarmato direttamente da veicolo, in modo che ne ereditino leproprieta:

class CarroArmato < Veicolo

attr_reader :colpi

def initialize (carburante, colpi)

super(carburante)

@colpi = colpi

end

end

class Camion < Veicolo

attr_reader :posti

def initialize (carburante, posti)

super(carburante)

@posti = posti

end

end

Il simbolo < sta per discende da, o eredita da,mentre il metodo super serve per chiamare il metodo con lo stesso nome ma della classe genitore.In questo caso, super richiama initialize del genitore.

Ovviamente questo ci fa risparimare un bel po’ di codice scritto, e ci puo semplificare di moltola vita... Ipotizziamo di avere 100 classi, e tutte queste classi hanno un metodo uguale. Dovendomodificare la logica di quel metodo, avremo bisogno di andare a modificare ognuna delle 100 classiprecedentemente definite. Utilizzando l’ereditarieta invece, la modifica necessaria si riduce ad unasola classe.

3.3 Modifica di classi esistenti

A volte, una cosa utile puo essere modificare una classe esistente.

28

Page 33: Imparando Ruby

Supponiamo di avere un array di numeri, di cui calcolare la somma, oppure la media.Certo con un each si puo fare tutto, ma se il numero di array aumenta bisognerebbe scrivere uneach per ogni array...ci farebbe molto comodo se per ogni oggetto array esistessero i metodi sum e avg... in ruby perodi default non esistono.Possiamo pero crearli noi andando a modificare al volo la classe Array! Basta semplicemente:

class Array

def sum

s=0

self.each do |x|

s=s+x

end

s

end

def avg

s=sum.to_f

l=self.size.to_f

s/l

end

end

somma=[1, 2, 3, 4, 5].sum #=> 15

media=[1, 2, 3, 4, 5].avg #=> 3

3.4 Gestione degli errori - Eccezioni

Un programma in esecuzione puo passare attraverso vari problemi inaspettati. Un file che vorrebbeleggere potrebbe non esistere; il disco potrebbe essere pieno quando desidera salvare dei dati;l’utente potrebbe fornirgli dei dati inaccettabili.

Un programma robusto gestira tali situazioni sensibilmente e in maniera elegante.

In ruby, come in molti linguaggi moderni, possiamo gestire le eccezioni per blocchi di codice inmodo compartimentato, ottenendo un risultato sorprendentemente efficiente ma senza caricare inmodo eccessivo il programmatore o chiunque tenti di leggere il codice in seguito. Il blocco dicodice segnalato con begin viene eseguito finche non c’e un’eccezione, che causa il trasferimentodel controllo ad un blocco di gestione dell’eccezione, che viene segnalato da rescue. Se non c’e uneccezione, il codice rescue non viene usato. Il metodo seguente ad esempio restituisce la primalinea di un file di testo, o nil se c’e un eccezione:

def prima_linea(filename)

begin

file = open("un_file")

info = file.gets

file.close

29

Page 34: Imparando Ruby

info

rescue

nil

end

end

E necessario sapere che anche un’eccezione di per se e un oggetto come un altro (discendente diException), ed essendo tale e percio possibile distinguere il tipo di errore sollevato, e agire diconseguenza.Potremo quindi voler scrivere

begin

...codice...

rescue TipoDiEccezione1

...azione1...

rescue TipoDiEccezione2

...azione2...

end

ed e anche possibile recuperare e stampare a video in maniera piu gradevole il tipo di errore:

begin

...codice...

rescue Exception => e

puts e.message

end

Volendo potremmo sollevare un’eccezione da un punto qualsiasi del nostro programma, ad esempio:

def inverse(x)

raise ArgumentError, ’Argument is not numeric’ unless x.is_a? Numeric

1.0 / x

end

La gerarchia delle eccezioni e questa:

• Exception

– NoMemoryError

– ScriptError

∗ LoadError

∗ NotImplementedError

∗ SyntaxError

– SignalException

∗ Interrupt

30

Page 35: Imparando Ruby

– StandardError

∗ ArgumentError

∗ IOError

· EOFError

∗ IndexError

∗ LocalJumpError

∗ NameError

· NoMethodError

∗ RangeError

· FloatDomainError

∗ RegexpError

∗ RuntimeError

∗ SecurityError

∗ SystemCallError

∗ SystemStackError

∗ ThreadError

∗ TypeError

∗ ZeroDivisionError

– SystemExit

31

Page 36: Imparando Ruby

Capitolo 4

Uso di librerie particolari

4.1 Gemme

Le librerie di ruby come potrebbero chiamarsi se non, per l’appunto, gemme?

Alcune gemme (le piu usate) sono gia incluse nell’installazione standard di ruby, mentre per altree necessaria l’installazione manuale. Per questo ci viene in aiuto il comando gem.

Ad esempio

~# gem install mysql

installa la gemma per accedere a database mysql.

4.2 Lavorare con indirizzi IP

Esiste una classe molto comoda che ci permette di trattare indirizzi IP:

# Include la libreria ’ipaddr’

require ’ipaddr’

# 1- indirizzo in notazione decimale puntata

IPAddr.new(’192.168.1.1’)

# 2- indirizzo + netmask in notazione decimale puntata

IPAddr.new(’192.168.1.1/255.255.255.255’)

# 3- indirizzo in notazione decimale puntata + CIDR

IPAddr.new(’192.168.1.1/32’)

# 4- verifica se un indirizzo appartiene a una determinata sottorete

network=IPAddr.new(’192.168.0.0/23’) # <IPAddr: IPv4:192.168.0.0/255.255.254.0>

32

Page 37: Imparando Ruby

network.include?(IPAddr.new(’192.168.0.23’)) #=> true

network.include?(IPAddr.new(’192.168.1.1’)) #=> true

network.include?(IPAddr.new(’192.168.10.1’)) #=> false

network.include?(IPAddr.new(’172.16.100.2’)) #=> false

4.3 YAML

YAML1 e un formato che permette di salvare come stringhe/file di testo degli oggetti ruby, insom-ma, una specie di XML.Vediamo un esempio rapido:

# inclusione della libreria YAML

require ’yaml’

test_array = [’Elemento 1’ ,

’Elemento 2’ ,

’Elemento 3’ ]

test_string = test_array.to_yaml

filename = ’output_yaml.txt’

File.open filename, ’w’ do |f|

f.write test_string

end

read_string = File.read filename

read_array = YAML::load read_string

puts(read_string == test_string) #=> true

puts(read_array == test_array ) #=> true

4.4 Inviare mail via SMTP

Utilizzando la libreria net/smtp e molto facile inviare una e-mail:

require ’net/smtp’

user_from = "[email protected]"

user_to = "[email protected]"

email_body = "From: [email protected]\n"

email_body = email_body + "Subject: Ciao\n\nEmail da Ruby.\n\n"

1http://en.wikipedia.org/wiki/YAML

33

Page 38: Imparando Ruby

# gestione delle eccezioni

begin

Net::SMTP.start(’my.smtp.server’, 25,) do |smtpclient|

smtpclient.send_message(email_body, user_from, user_to)

end

rescue Exception => e # rescue e‘ utilizzato per gestire le eccezioni

print "Eccezione verificata: " + e

end

4.5 Accesso a database MySQL

E necessaria la gemma mysql

Vediamone l’uso per esempi:

# libreria mysql

require ’mysql’

# stabilisce connessione con il server

con=Mysql.new(’localhost’, ’username’, ’password’, ’database_name’)

# query senza risultati di ritorno:

con.query("CREATE TABLE prova (

name VARCHAR(40),

email VARCHAR(40)

)")

con.query("DROP TABLE IF EXISTS prova1234")

con.query("INSERT INTO prova (name, email) VALUES

(’tizio’, ’[email protected]’),

(’caio’, ’[email protected]’),

(’sempronio’, ’[email protected]’)

)" )

numero_righe_inserite=con.affected_rows # => 3

# certo che pero‘ effettuare inserimenti cosi‘

# o interpolando stringhe in una query

# puo‘ risultare pericoloso...

# meglio inserire i caratteri di escape...

email=con.escape_string("[email protected]")

con.query("UPDATE prova SET email = ’#{email}’ WHERE name = ’sempronio’")

numero_righe_inserite=con.affected_rows # => 1

34

Page 39: Imparando Ruby

# query con record di ritorno (SELECT)

rs=con.query(’select * from prova’)

numero_di_righe=rs.num_rows

# ora abbiamo due modi per scorrere un insieme di record (recordset):

# 1) ciclo while

while row = rs.fetch_hash do

puts "Nome: " + row[’name’]

puts "E-Mail: " + row[’email’]

puts "------"

end

# 2) each

rs.each_hash do |row|

puts "Nome: " + row[’name’]

puts "E-Mail: " + row[’email’]

puts "------"

end

# 3) each, includento nel come del campo anche il nome della tabella

rs.each_hash(with_table=true) do |row|

puts "Nome: " + row[’prova.name’]

puts "E-Mail: " + row[’prova.email’]

puts "------"

end

# liberiamo la ram utilizzata dal recordset

rs.free

# e ora chiudiamo la connessione

con.close

4.6 Accesso a database SQLite

E necessaria la gemma sqlite3

Un database SQLite e molto piu facile da manipolare che non MySQL:

require ’sqlite3’

db = SQLite3::Database.new("book.db")

db.execute("SELECT * FROM libro") do |riga|

puts "#{riga.titolo} - #{riga.autore}"

end

35

Page 40: Imparando Ruby

# se invece siamo interessati solo alla prima riga:

prima_riga = db.get_first_row( "SELECT * FROM libro" )

db.close

4.7 Accesso a database KirbyBase

KirbyBase e un DBMS molto leggero e interamente scritto in Ruby. I dati vengono salvati sufile di testo e possono essere eventualmente modificati a mano, e possibile usarlo sia in modalitaembedded sia in modalita client/server. Puo essere visto come una via di mezzo tra i file di testosemplice e i database di tipo SQL.

Partiamo con la creazione di un database e di una tabella:

# richiesta la libreria base delle gemme

require ’rubygems’

# libreria kirbybase

require ’kirbybase’

db = KirbyBase.new

book = db.create_table(:libro,

:titolo, :String,

:autore, :String,

:anno, :Integer

)

KirbyBase.new crea un database nella directory corrente, se pero volessimo utilizzare un’altradirectory per immagazzinare i dati sarebbe sufficiente dare

db = KirbyBase.new(:local, nil, nil, ’./data’)

Il metodo create table prende come argomenti il nome della tabella e il nome e il tipo di ognicolonna. I tipi disponibili sono: String, Integer, Float, Boolean, Time, Date, DateTime, Memo,Blob, e YAML. Per ogni campo e possibile specificare anche alcune proprieta come il valore didefault, l’obbligatorieta del campo, l’indicizzazione e riferimenti ad altri record in altre tabella.

E’ anche possibile cifrare la tabella, utilizzando

book = db.create_table(:libro, ...) do |t|

t.encrypt = true

end

Per popolare la nostra tabella ora si usa il metodo insert:

36

Page 41: Imparando Ruby

book.insert("Reduce","Giovanni Lindo Ferretti",2006)

#=> 1

book.insert("Il bosco degli urogalli","Mario Rigoni Stern",1962)

#=> 2

(il numero ritornato e il valore della chiave primaria)in alternativa e possibile usare dei blocchi di codice:

book.insert do |l|

l.titolo = "Memorie dal sottosuolo"

l.autore = "Fedor Dostoevskij"

l.anno = 1864

end

#=> 3

se ora volessimo ricavare tutto il contenuto della tabella basta utilizzare il metodo select senzanessun parametro:

ris = book.select

#=> [#<struct #<Class:0xb7a98f14> recno=1, titolo="Reduce", autore="Giovanni Lindo Ferretti", anno=2006>, #<struct #<Class:0xb7a98f14> recno=2, titolo="Il bosco degli urogalli", autore="Mario Rigoni Stern", anno=1962>, #<struct #<Class:0xb7a98f14> recno=3, titolo="Memorie dal sottosuolo", autore="Fedor Dostoevskij", anno=1864>]

ris e un oggetto di tipo KBResultSet.Se invece si vogliono visualizzare solo alcune colonne basta specificarle come parametro:

ris_titolo = book.select(:titolo)

#=> [#<struct #<Class:0xb79ac8d0> titolo="Reduce">, #<struct #<Class:0xb79ac8d0> titolo="Il bosco degli urogalli">, #<struct #<Class:0xb79ac8d0> titolo="Memorie dal sottosuolo">]

Per meglio definire la ricerca e possibile usare dei blocchi di codice:

ris_duemila = book.select(:titolo) do |l|

l.anno > 2000

end

#=> [#<struct #<Class:0xb79a6638> titolo="Reduce">]

I primi passi che abbiamo visto sono necessari a creare la tabella; se invece la nostra tabella esistegia, e vogliamo soltanto un riferimento che punta ad essa:

tabella = db.get_table(:libro)

Per i vari dettagli e altre operazioni rimando a

http://www.netpromi.com/files/kirbybase ruby manual.html

4.8 ActiveRecord

**** DA FARE ****

37

Page 42: Imparando Ruby

4.9 File XML

Vedremo ora come utilizzare due diverse librerie per trattare file XML, capiremo come leggerli ecome costruirli.Le librerie si chiamano xmlsimple e rexml. La prima permette di convertire un file xml in unhash e viceversa. La seconda invece va a lavorare direttamente con la struttura DOM2 del file.

4.9.1 xmlsimple

E richiesta la gemma xml-simple.

lettura di un file

require ’rubygems’

require ’xmlsimple’

xmlfile = File.new("cd.xml")

doc = XmlSimple.xml_in xmlfile

Come detto prima, doc sara un semplice oggetto di tipo Hash.

creazione di un file

require ’rubygems’

require ’xmlsimple’

miohash={}

miohash[’root’]={}

miohash[’root’][’dir1’]="Prova 123"

miohash[’root’][’dir2’]="Prova 124"

miohash[’root’][’k’]=[]

miohash[’root’][’k’][0]={"ciao"=>"bao1"}

miohash[’root’][’k’][1]={"ciao"=>"bao2"}

doc = XmlSimple.xml_out miohash

doc sara una stringa contenente il nostro codice xml, che in questo caso sara:

<opt>

<root dir1="Prova 123" dir2="Prova 124">

<k ciao="bao1" />

<k ciao="bao2" />

2DOM: Document Object Model

38

Page 43: Imparando Ruby

</root>

</opt>

4.9.2 rexml

Questa libreria offre un trattamento piu avanzato rispetto a xmlsimple.

lettura di un file

Ipotizziamo un file di esempio, cd.xml cosı formato:

<negozio nome="CD House">

<genere nome="Stoner">

<cd asin="B00000JBDE">

<titolo>Frequencies From Planet Ten</titolo>

<autore>Orange Goblin</autore>

<anno>1997</anno>

</cd>

<cd asin="B000WM72FC">

<titolo>Witchcult Today</titolo>

<autore>Electric Wizard</autore>

<anno>2007</anno>

</cd>

</genere>

<genere nome="Colonne sonore">

<cd asin="B00005O6PA">

<titolo>Amelie</titolo>

<autore>Yann Tiersen</autore>

<anno>2001</anno>

</cd>

</genere>

</negozio>

per leggerlo avremo bisogno di un programma simile al seguente:

require ’rexml/document’

xmlfile = File.new("cd.xml")

doc = REXML::Document.new(xmlfile)

doc.root.each_element do |genere|

puts genere.attributes["nome"]

genere.each_element do |cd|

cd.each_element do |val|

puts " #{val.name}: #{val.text}"

39

Page 44: Imparando Ruby

end

end

end

creazione di un file

require ’rexml/document’

# creiamo ora un documento xml, con tag radice <doc/>

doc=REXML::Document.new("<doc/>")

# creiamo ora un elemento example:

el = REXML::Element.new "example"

# con il testo "esempio 1"

el.text="esempio 1"

# e un attributo data

el.attributes["data"]="20081001"

# e lo aggiungiamo alla radice:

rt = doc.root

rt << el

# vediamo il risultato

puts doc.to_s #=> <doc><example data=’20081001’>esempio 1</example></doc>

*** DA SISTEMARE ***

4.10 Networking a basso livello

In Ruby il networking a basso livello e gestito dalla libreria standard socket che e strutturatanel seguente modo: alla base c’e BasicSocket classe astratta, sottoclasse di IO, che contienealcuni metodi fondamentali, ereditati da tutte le sue sottoclassi, come ad esempio close read,close write, getpeername, getsockname, recv e send.

Da BasicSocket discendono direttamente IPSocket, Socket e UNIXSocket. IPSocket e la classeche implementa le socket che usano il protocollo di trasporto IP e ha due sottoclassi: TCPSockete UDPSocket che rispettivamente trattano connessioni da, e verso, socket TCP e UDP. La classeSocket fornisce invece direttamente accesso all’implementazione delle socket del sistema operativo.UnixSocket infine gestisce le comunicazioni IPC utilizzando lo UNIX domain protocol.

Tutte le classi di socket discendono indirettamente dalla classe IO, questo vuol dire che e possibileusare i metodi di IO sui socket cosı come accade ad esempio per i file.

40

Page 45: Imparando Ruby

socket UDP

Iniziamo con un semplice esempio client-server, in cui il server inviera al client che si connetteraora e data corrente.

Server:

require "socket"

server = UDPSocket.open

server.bind(nil, 12345)

loop do

data, sender = server.recvfrom(1)

chost = sender[3]

cport = sender[1]

retstring = "Data corrente: #{Time.new.to_s}\n"

puts "Request from #{chost}:#{cport}"

server.send(retstring, 0, chost, cport)

end

Innanzitutto creiamo una nuova istanza di UDPSocket, e al mettiamo in ascolto sulla porta 12345(ovviamente UDP).Il loop principale non fa altro che rimanere in attesa di una connessione, e quando avviene inviail messaggio con la data

Client:

require "socket"

client = UDPSocket.open

client.connect(’localhost’, 12345)

client.send("", 0)

while msg=client.gets

puts msg

end

In questo caso creiamo una connessione verso localhost, e inviamo una stringa vuota al server(avremo potuto inviare qualsiasi cosa, tanto al nostro server non importa nulla). Infine ci mettiamoin ascolto per la risposta da parte del server e la stampiamo a video.

socket TCP

Analogamente, un server e un client basati su TCP saranno:

41

Page 46: Imparando Ruby

Server:

require "socket"

server = TCPServer.open(’localhost’, 12345)

while session = server.accept

msg = "Data corrente: #{Time.new.to_s}\n"

session.puts msg

session.close

end

Client:

require "socket"

client = TCPSocket.open(’localhost’, 12345)

while msg=client.gets

puts msg

end

client.close

Vediamo che, grazie alle proprieta del protocollo, server e client TCP sono piu semplici di quelliUDP.

Un piccolo server Web

I server web si basano sul protocollo TCP; vediamo ora come scrivere in pochissime riche di codiceun web-server minimale che ad ogni richiesta risponde con data e ora corrente; ovviamente larisposta dovra essere conforme alle specifiche del protocollo HTTP:

require ’socket’

server = TCPServer.new(’localhost’, 9090)

while (session = server.accept)

puts "Request: #{session.gets}"

session.print "HTTP/1.1 200/OK\r\nContent-type: text/html\r\n\r\n"

session.print "<html><body><p>#{Time.new.to_s}</p></body></html>\r\n"

session.close

end

Proviamo ora a puntare il nostro browser a

http://localhost:9090/

42

Page 47: Imparando Ruby

Un server Web un po’ piu complesso

richiede la gemma mime-types

Questo server web ridotto all’osso server solo contenuti statici. Niente contenuti dinamici.

require ’rubygems’

require ’socket’

require ’mime/types’

class HttpServer

def initialize(session, request, basePath)

@session = session

@request = request

@basePath = basePath

end

def getFullPath()

fileName = nil

if @request =~ /GET .* HTTP.*/

fileName = @request.gsub(/GET /, ’’).gsub(/ HTTP.*/, ’’)

end

fileName = fileName.strip

unless fileName == nil

fileName = @basePath + fileName

fileName = File.expand_path(fileName, @defaultPath)

end

fileName << "/index.html" if File.directory?(fileName)

return fileName

end

def serve()

@fullPath = getFullPath()

src = nil

begin

if File.exist?(@fullPath) and File.file?(@fullPath)

if @fullPath.index(@basePath) == 0 #path should start with base path

contentType = getContentType(@fullPath)

@session.print "HTTP/1.1 200/OK\r\n"

@session.print "Server: MySecondRubyWebServer 1.0\r\n"

@session.print "Content-type: #{contentType}\r\n\r\n"

src = File.open(@fullPath, "rb")

while (not src.eof?)

buffer = src.read(256)

@session.write(buffer)

end

43

Page 48: Imparando Ruby

src.close

src = nil

else

# should have sent a 403 Forbidden access but

# then the attacker knows that such a file exists

@session.print "HTTP/1.1 404/Object Not Found\r\n"

@session.print "Server: MySecondRubyWebServer 1.0\r\n\r\n"

end

else

@session.print "HTTP/1.1 404/Object Not Found\r\n"

@session.print "Server: MySecondRubyWebServer 1.0\r\n\r\n"

end

ensure

src.close unless src == nil

@session.close

end

end

def getContentType(path)

types= MIME::Types.type_for(path)

if types.size == 0

return "text/html"

else

return types[0].content_type

end

end

end

basePath = "/home/stefano/Manoscritti/Imparando_Ruby/temp/"

server = TCPServer.new(’localhost’, 9090)

loop do

session = server.accept

request = session.gets

Thread.start(session, request) do |session, request|

HttpServer.new(session, request, basePath).serve()

end

end

Un server Web ancora piu complesso

Perche complicarsi la vita a scrivere ancora piu codice? I web server di prima erano a scopopuramente didattico, ma quando vogliamo qualcosa di piu perche non appoggiarsi a prodotti

44

Page 49: Imparando Ruby

stabili e testati?

Vediamo ora come usare webrick, un web server piuttosto complesso, nei nostri programmi ruby.In ogni caso questo listato e solo a titolo di esempio, affronteremo la programmazione web neldettaglio nei capitoli successivi.

richiede la gemma webrick

require ’webrick’

s = WEBrick::HTTPServer.new(

:Port => 2000,

:DocumentRoot => Dir.pwd + "/htdocs"

)

## mount sotto-directory

s.mount("/d1", WEBrick::HTTPServlet::FileHandler, "/srv/dati/dir1/public_html")

s.mount("/~stefano",

WEBrick::HTTPServlet::FileHandler, "/home/stefano/public_html",

true) #<= permette directory index

## mount di una "servlet"

class HelloServlet < WEBrick::HTTPServlet::AbstractServlet

def do_GET(req, res)

res.body = "<HTML>hello, world.</HTML>"

res[’Content-Type’] = "text/html"

end

end

s.mount("/hello", HelloServlet)

trap("INT"){ s.shutdown }

s.start

Rimando qui per ulteriori dettagli su come estendere webrick con delle “servlet”:

http://segment7.net/projects/ruby/WEBrick/servlets.htmlhttp://microjet.ath.cx/WebWiki/WEBrick.html

Volendo potremo anche usare Apache e mod ruby, ma lo vedremo meglio piu avanti...

45

Page 50: Imparando Ruby

Parte II

Programmazione web di base

46

Page 51: Imparando Ruby

Capitolo 5

CGI

5.1 Introduzione

**** i metodi http, e il passaggio di parametri ****

http://openskill.info/infobox.php?ID=357http://it.wikipedia.org/wiki/HTTP

5.2 CGI in ruby

L’approccio tradizionale alla programmazione Web e sicuramente costituito dai CGI. Un program-ma CGI sostanzialmente e un programma che viene eseguito dal webserver, e l’output viene inviatoal client. In ruby i CGI sono gestiti dalla libreria cgi che fornisce tutti gli strumenti necessari.

Cominciamo con un primo CGI, ciao.rb, che deve essere posizionato nella directory cgi-bin delnostro web server (e ovviamente deve essere reso eseguibile):

#!/usr/bin/env ruby

# un programma CGI deve sempre dire quale e il formato

# del contenuto che manda in output+una linea vuota

# contenuto html - tipo MIME text/html

puts "Content-type: text/html"

# linea vuota

puts ""

# stampa del codice html

puts "<html>"

puts "<head>"

puts "<title>ciao</title>"

47

Page 52: Imparando Ruby

puts "</head>"

puts "<body>"

puts "<h2>ciao mondo</h2>"

puts "</body>"

puts "</html>"

Andiamo quindi a

http://localhost/cgi-bin/ciao.rb

per vedere il risultato.

L’input di programmi cgi non e pero come il classico standard-input...i dati in ingresso vengono dati formattati dal browser in modo particolare; per questo per leggerlici viene in aiuto la libreria ’cgi’: (ciao2.rb)

#!/usr/bin/env ruby

require ’cgi’

cgi = CGI.new

#un aiuto al puts ce lo da’ sempre il modulo cgi

#invece di puts "Content-type: text/html"

puts cgi.header("type"=>"text/html")

# stampa del codice html

puts "<html>"

puts "<head>"

puts "<title>ciao</title>"

puts "</head>"

puts "<body>"

puts "<h2>ciao #{cgi[’nome’]}</h2>"

puts "</body>"

puts "</html>"

Andiamo quindi a

http://localhost/cgi-bin/ciao2.rb?nome=Stefano

per vedere il risultato. In questo caso nome e un parametro in ingresso con il metodo HTTP GET,ma anche se fosse stato inviato con POST la sua lettura sarebbe stata uguale.

5.3 CGI + ERB

Certo che pero e scomodo dover copiare/scrivere codice html direttamente immerso nel codiceruby...Un grande aiuto ce lo danno i sistemi di template, come ad esempio ERB (embedded-ruby)

48

Page 53: Imparando Ruby

proviamo cosı:

ciao erb.rb:

#!/usr/bin/env ruby

require ’erb’

require ’cgi’

cgi = CGI.new

puts cgi.header("type"=>"text/html")

input = File.read(’ciao_erb.erb’)

# carichiamo l’oggetto erb

eruby = ERB.new(input)

# qualche variabile tanto per provare

list = [’aaa’, ’bbb’, ’ccc’, ’ddd’]

name = ’Bla Bla Bla’

# Print the result of the two files.

puts eruby.result(binding())

e nella stessa cartella creiamo il file ciao erb.erb:

<html>

<head>

<title>Prova ERB</title>

</head>

<body>

Name: <%= name %>

<ul>

<% for item in list %>

<li><%= item %></li>

<% end %>

<%# questo e un commento %>

</ul>

</body>

</html>

Ecco, cosı e decisamente molto piu leggibile :-)

Come possiamo vedere, in un template erb il codice ruby e delimitato da <% e %>. Un po’ comePHP e ASP, insomma.

49

Page 54: Imparando Ruby

5.4 mod ruby

Si puo dire che ormai, in applicazioni web di una certa complessita, CGI e veramente pocoperformate, perche ad ogni richiesta il web server deve eseguire tutti il programma CGI.

Per questo ci vengono in aiuto alcuni componenti direttamente integrati nei web server, nel casodel popolare Apache parleremo di mod ruby.

E sufficiente installare mod ruby e inserire nella configurazione di apache

LoadModule ruby_module lib/httpd/modules/mod_ruby.so

<IfModule mod_ruby.c>

RubyRequire apache/ruby-run

<Location /ruby>

SetHandler ruby-object

RubyHandler Apache::RubyRun.instance

</Location>

<Files *.rbx>

SetHandler ruby-object

RubyHandler Apache::RubyRun.instance

</Files>

</IfModule>

Per far funzionare i nostri script ruby (che devono avere estensione .rbx) con performance deci-samente migliori.

50

Page 55: Imparando Ruby

Parte III

Ruby on Rails

51

Page 56: Imparando Ruby

**** DA FARE ****

52

Page 57: Imparando Ruby

Appendice A

Esercizi e soluzioni

A.1 Semplici operazioni con stringhe

A.1.1 Esercizio 1

Chiedere all’utente una stringa, verificare se e o meno palindroma.

#!/usr/bin/env ruby

puts "Inserisci una stringa, verifichero se e palindroma..."

# prendo una stringa dallo standard input, elimino il caporiga e

# la faccio diventare maiuscola (o minuscola)

# Il passo di farla diventare maiuscola/minuscola e fondamentale,

# altrimenti il programma non saprebbe riconoscere che la parola

# ’Anna’ e palindroma. (’Anna’ e diverso da ’annA’)

s1=gets.chomp.upcase

# ora creo una stringa invertendo i caratteri di quella di partenza

s2=s1.reverse

# e quindi faccio il confronto

if s1==s2 then

puts "La stringa e palindroma"

else

puts "La stringa NON e palindroma"

end

A.2 Metodi

A.2.1 Esercizio 1

Chiedere, per tre volte, due numeri all’utente, e per ogni richiesta dire se il primo numero edivisibile per il secondo o meno. (definire il metodo divisibile che accetta due parametri numerici

53

Page 58: Imparando Ruby

e ritorna true nel caso il primo numero sia divisibile per il secondo)

#!/usr/bin/env ruby

def divisibile(a,b)

return true if a%b==0

false

#scrittura compatta (al posto di tutte le righe precedenti):

#a%b==0

end

3.times do

puts "Inserisci a:"

a=gets.chomp.to_i

puts "Inserisci b:"

b=gets.chomp.to_i

if divisibile(a,b)

puts "#{a} e divisibile per #{b}"

else

puts "#{a} NON e divisibile per #{b}"

end

end

A.2.2 Esercizio 2

Scrivere il metodo is prime(x) che restituisce true se x e primo, false altrimenti. Testare poi ilmetodo chiedendo a un utente un numero, e verificandolo con la funzione.

#!/usr/bin/env ruby

def is_prime(x)

# 0 o negativi: non posso verificare se primo o meno, ritorno false...

return false if x < 1

# casi base: 1 e 2

return true if x==1 or x==2

# altri casi

pr=true

i=2

while i < x and pr

pr=false if x%i==0

i=i+1

end

pr

end

54

Page 59: Imparando Ruby

puts "Inserisci un numero, verifichero se e primo."

n=gets.chomp.to_i

if is_prime n

puts "#{n} e un numero primo"

else

puts "#{n} NON e un numero primo (oppure non posso verificare se e primo o meno)"

end

A.3 Operazioni con Array

A.3.1 Esercizio 1

Scrivere un programma che chiede all’utente quanti voti ha collezionato; chiedere i vari voti e poitrovare: massimo, minimo, media.

#!/usr/bin/env ruby

# inizializza l’array di voti

voti=[]

puts "Inserisci numero di voti:"

num=gets.chomp.to_i

# riempie l’array

num.times do |k|

n=k+1

puts "Inserisci voto numero #{n}:"

voti << gets.chomp.to_f

end

# calcola

somma=0.0

voti.each do |x|

somma=somma+x

end

media=somma/num

min=voti.min

max=voti.max

puts "Voto minimo: #{min}"

puts "Voto massimo: #{max}"

puts "Media: #{media}"

55

Page 60: Imparando Ruby

A.3.2 Esercizio 2

Dato un array cercare un elemento al suo interno senza usare i metodi di ricerca interni ad Array.Usare un metodo che restituisce l’indice della prima ricorrenza se l’elemento e presente, -1 in casocontrario.

#!/usr/bin/env ruby

def cerca_in_array(array, numero)

# r e il contatore della posizione in cui sono

r=0

array.each do |e|

# ritorno la posizione in caso l’elemento venga trovato

return r if e==numero

r=r+1

end

# ritorna -1 in caso non venga trovato

-1

end

# Inizializzo l’array

ar=[1, 2, 5, 6, 8, 12, 34, 56, 87]

ar_s=ar.join(",")

puts "Ecco l’array: #{ar_s}"

puts "Inserisci il numero da cercare:"

n=gets.chomp.to_i

posizione=cerca_in_array(ar, n)

if posizione==-1

puts "Numero non trovato."

else

puts "Il numero che cerchi e in posizione #{posizione}"

end

A.3.3 Esercizio 3

Con il metodo del crivello di eratostene trovare tutti i numeri primi da 1 a 100. Consiglio: vedereil crivello come un array in cui l’indice di un elemento corrisponde al numero, il valore in posizionek vale false se il numero e stato eliminato.

#!/usr/bin/env ruby

56

Page 61: Imparando Ruby

# numeri primi da 1 a 100 (Z)

Z=100

# secondo l’algoritmo trovo la radice quadrata del numero a cui arrivare

META=Math.sqrt(Z).to_i

# inizializzo l’array con tutti i valori a true

criv=[]

for i in (1..Z) do

criv[i]=true

end

# crivello vero e proprio

for i in (2..META) do

b=i+1

for k in (b..Z) do

criv[k]=false if k%i==0

end

end

puts "Ecco i numeri primi:"

for i in (1..Z) do

puts i if criv[i]

end

A.4 Operazioni con Hash

A.4.1 Esercizio 1

Dato un hash cercare la chiave relativa ad un suo elemento senza usare i metodi di ricerca interniad Hash. Usare un metodo che restituisce la chiave della prima ricorrenza se l’elemento e presente,nil in caso contrario.

#!/usr/bin/env ruby

def find_in_hash(hash, el)

# in questo caso tra || uso due variabili. Questo perche e definito cosı

# dal metodo .each_pair di hash. Vedere la documentazione ufficiale

hash.each_pair do |key, value|

return key if el==value

end

nil

end

h={}

57

Page 62: Imparando Ruby

h[’stefano’]=’nerd’

h[’mirko’]=’porseo’

puts "Ecco i valori contenuti nell’hash:"

# Vedere la documentazione ufficiale per capire il significato dei metodi

# seguenti

puts h.values.join(",")

puts "Che elemento devo cercare?"

el=gets.chomp

k=find_in_hash(h, el)

if k.nil?

puts "Elemento non trovato nell’hash."

else

puts "L’elemento cercato ha chiave #{k}"

end

A.5 Operazioni con File

A.5.1 Esercizio 1

Aprire un file di testo, e salvarlo come nome originale.ordinato dopo averne ordinato le righe.

#!/usr/bin/env ruby

puts "Inserisci il nome del file da ordinare:"

f1=gets.chomp

# verifica che il file sia leggibile, in caso contrario stampa messaggio

# di errore

if File.readable? f1 then

contenuto = File.read f1

# ora leggo le righe con uno split: (righe sara un array)

righe = contenuto.split("\n")

righe.sort!

File.open "#{f1}.sorted", ’w’ do |f|

righe.each do |row|

f.write "#{row}\n" # ricordiamoci il ritorno a capo!

end

end

puts "Fatto."

else

puts "Il file non esiste o non e leggibile"

end

58

Page 63: Imparando Ruby

A.6 Operazioni con Oggetti

A.6.1 Esercizio 1

Definire un oggetto di tipo Libro avente come attributi accessibili in sola lettura isbn, autore,titolo, editore, anno pubblicazione e prezzo. Tutti questi attributi devono essere settati infase di inizializzazione dell’oggetto.

class Libro

attr_reader :isbn, :autore, :titolo

attr_reader :editore, :anno_pubblicazione, :prezzo

# scritte due righe solo per semplicita di lettura da parte dell’utente.

# avrei potuto scrivere una riga solo

def initialize(isbn, autore, titolo, editore, anno_pubblicazione, prezzo)

@isbn = isbn

@autore = autore

@titolo = titolo

@editore = editore

@anno_pubblicazione = anno_pubblicazione

@prezzo = prezzo

end

end

A.6.2 Esercizio 2

Progettare la classe conto bancario (BankAccount), scrivere quindi un programma che costruiscaun conto bancario, versi in esso 1000 euro, prelevi da esso 500 euro, prelevi altri 400 euro e stampia video il saldo rimanente. (La progettazione della classe e libera)

class NegativeBalanceError < ArgumentError

end

class BankAccount

attr_reader :balance

def initialize

@balance = 0

end

def deposit (amount)

@balance = @balance + amount

end

59

Page 64: Imparando Ruby

def withdraw (amount)

if @balance - amount < 0

raise NegativeBalanceError

end

@balance = @balance - amount

end

end

ba=BankAccount.new

ba.deposit 1000

ba.withdraw 500

ba.withdraw 400

puts ba.balance

A.6.3 Esercizio 3

Progettare una classe Circle (cerchio), con i metodi area, per conoscere l’area, e perimeter, perconoscerne la circonferenza. Nel costruttore, indicare il raggio del cerchio. Usare π come costante.(PI=3.1415927)

class Circle

PI=3.1415927

def initialize(radius)

@radius = radius

end

def area

PI*(@radius**2)

end

def perimeter

2*PI*@radius

end

end

A.6.4 Esercizio 4

Scrivere un programma che chieda all’utente un numero intero e che poi stampi tutti i suoi fattori.Progettare e usare una classe FactorGenerator con i metodi next factor e has more factors?

.

60

Page 65: Imparando Ruby

class FactorGenerator

def initialize (n)

raise ArgumentError, ’n must be greater than zero’ if n<=1

@numero_da_scomporre = n

@attuale_scomposizione = n

end

def has_more_factors?

return false if @attuale_scomposizione==1

true

end

def next_factor

raise Exception, ’there are no more factors’ unless has_more_factors?

for i in (2..@attuale_scomposizione) do

if @attuale_scomposizione%i==0

@attuale_scomposizione = @attuale_scomposizione/i

# break serve per forzare l’uscita dal ciclo

break

end

end

i

end

end

numero = 150

f=FactorGenerator.new(numero)

while f.has_more_factors? do

puts f.next_factor

end

A.6.5 Esercizio 5

Aggiungere alla classe String il metodo num rows che conta le righe presenti nella stringa stessa.Consiglio: usare \n come separatore di linea.

class String

def num_rows

split("\n").size

end

end

61