P. 1
Imparando Ruby

Imparando Ruby

|Views: 2,927|Likes:
Published by stesasso
Guida alla programmazione in ruby partendo da zero: spiegazioni, esempi, esercizi.
Guida alla programmazione in ruby partendo da zero: spiegazioni, esempi, esercizi.

More info:

Published by: stesasso on Aug 26, 2009
Copyright:Attribution Non-commercial

Availability:

Read on Scribd mobile: iPhone, iPad and Android.
download as PDF, TXT or read online from Scribd
See more
See less

06/23/2014

pdf

text

original

Sections

Imparando Ruby...

Introduzione alla programmazione con Ruby

a cura di

Stefano Sasso stefano(at)gnustile.net

Versione 1.0 - 10 Febbraio 2009

Indice
I Le Basi 1
2 2 2 3 4 4 6 6 6 7 7 8 9 9 10 11 13 13 13 13 14 15 17 19 19 i

1 Introduzione a Ruby 1.1 1.2 1.3 Interprete interattivo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Interprete non interattivo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . La prima prova . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

2 Il linguaggio Ruby 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 Metodi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Assegnazione di variabili . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Tipi di dato numerici . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . I commenti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Le costanti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Tutti i metodi di un oggetto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Standard input + Stringhe di caratteri . . . . . . . . . . . . . . . . . . . . . . . . . Conversione tra diversi tipi di dato . . . . . . . . . . . . . . . . . . . . . . . . . . . Dati booleani . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

2.10 Enunciati decisionali - IF/UNLESS . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.11 Altro tipo di dato: gli array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.12 Cicli di istruzioni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.12.1 while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.12.2 for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.12.3 blocco each - blocco times . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.13 Ancora array... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.14 Ancora stringhe... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.15 Definizione di metodi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.16 Semplici operazioni con file di testo . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.16.1 lettura di un file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

2.16.2 scrittura di un file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.16.3 lavorare con il filesystem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.17 Altri oggetti utili . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.17.1 La classe ’Time’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.17.2 La classe ’Hash’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

20 20 21 21 22 23 23 23 23 24 27 28 29 32 32 32 33 33 34 35 36 37 38 38 39 40

3 Programmazione orientata agli oggetti 3.1 3.2 Definizione di classe e oggetto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Creazione di classi personalizzate . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.2.1 3.2.2 3.2.3 3.3 3.4 costruzione di una classe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . variabili di istanza . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ereditariet` . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . a

Modifica di classi esistenti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Gestione degli errori - Eccezioni . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

4 Uso di librerie particolari 4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9 Gemme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Lavorare con indirizzi IP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . YAML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Inviare mail via SMTP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Accesso a database MySQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Accesso a database SQLite . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Accesso a database KirbyBase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ActiveRecord . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . File XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.9.1 4.9.2 xmlsimple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . rexml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

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

II

Programmazione web di base

46
47

5 CGI 5.1 5.2 5.3 5.4 Introduzione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . CGI in ruby . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . CGI + ERB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . mod ruby . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

47 47 48 50 ii

III

Ruby on Rails

51
53 53 53 53 53 54 55 55 56 56 57 57 58 58 59 59 59 60 60 61

A Esercizi e soluzioni A.1 Semplici operazioni con stringhe . . . . . . . . . . . . . . . . . . . . . . . . . . . . A.1.1 Esercizio 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

A.2 Metodi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A.2.1 Esercizio 1 A.2.2 Esercizio 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

A.3 Operazioni con Array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A.3.1 Esercizio 1 A.3.2 Esercizio 2 A.3.3 Esercizio 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

A.4 Operazioni con Hash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A.4.1 Esercizio 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

A.5 Operazioni con File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A.5.1 Esercizio 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

A.6 Operazioni con Oggetti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A.6.1 Esercizio 1 A.6.2 Esercizio 2 A.6.3 Esercizio 3 A.6.4 Esercizio 4 A.6.5 Esercizio 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

iii

Parte I

Le Basi

1

Capitolo 1

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

1.1

Interprete interattivo

` E possibile lanciare l’interprete interattivo con ~# irb ci compari` quindi un prompt simile al seguente: a irb(main):001:0> _

1.2

Interprete non interattivo

Per lanciare l’interprete non interattivo ` necessario scrivere all’interno di un file di testo il codice e ruby che vogliamo venga eseguito. Possiamo poi lanciare l’interprete con ~# ruby nomedelfile.rb oppure, pi` semplicemente, inserire in testa al file contenente il codice ruby la riga u #!/usr/bin/env ruby come qualsiasi altro script in ambiente UNIX.

2

1.3

La prima prova

Facciamo subito la prima prova: creiamo il file prova1.rb con un editor di testo, e al suo interno scriviamo puts "Ciao mondo!" poi eseguiamolo con ~# ruby prova1.rb allo stesso modo apriamo irb, e al prompt digitiamo puts "Ciao mondo!" il nostro interprete sar` quindi in questa situazione: a irb(main):001:0> puts "Ciao mondo!" Ciao mondo! => nil irb(main):002:0> _ Analizziamo ora questo semplicissimo programma: puts ` una funzione del linguaggio che ci consente di mandare qualche oggetto allo standard e output. Ruby ` un linguaggio orientato agli oggetti ; per il momento consideriamo gli oggetti come delle e “cose” che vengono manipolate da un programma. In questo caso l’oggetto in questione, da stampare a schermo, ` una stringa di caratteri, delimitata e da doppi apici ( ). Equivalente sarebbe stato delimitarla da apici singoli (’).

3

Capitolo 2

Il linguaggio Ruby
2.1 Metodi

Abbiamo visto prima che in Ruby esistono delle funzioni, comunemente detti metodi che compiono una qualche azione. Ad esempio puts serve per stampare a schermo un oggetto. Esistono pi` modi per richiamare un metodo da un programma: con o senza parentesi tonde ( ). u metodo parametro1, parametro2 metodo(parametri1, parametro2) parametro1 e parametro1 sono degli oggetti su cui il metodo specifico andr` ad operare. Il numero a di oggetti “passabili” a un metodo ` deciso dal progettista del metodo stesso. e Nel contesto di prima, ad esempio, puts "Ciao mondo!" e puts("Ciao mondo!") sono del tutto equivalenti. Il metodo puts pu` lavorare, come gi` visto, su degli oggetti stringa o a di caratteri, ma anche, ad esempio, su oggetti del tipo numero intero (ma anche altri): puts 12 puts(12) Ricordiamoci che in Ruby, tutto ` un oggetto, anche i numeri. e ` Su ogni oggetto ` possibile invocare dei metodi, che lavoreranno direttamente su quell’oggetto. E e possibile anche invocare dei metodi su dei metodi ; questo perch` un metodo ritorna al programma e sempre un oggetto, quindi in realt` ` come invocare un metodo su un oggetto. vediamo un esempio ae (lavorando da irb): 3.next 4

in questo caso invochiamo il metodo next sull’oggetto 3. Il metodo ritorner` come valore di uscita a 4. 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 pi` oggetti u possono avere dei metodi che si chiamano nello stesso modo. Ad esempio "a".next non ` lo stesso metodo visto prima (infatti, la classe di partenza ` diversa), ma si chiama nello e e stesso 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 ` il . e (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 si intende 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. Ad esempio, nel caso di puts, non ha senso che venga ritornato un qualcosa, quindi ritorner` nil. a 5

2.2

Assegnazione di variabili

Un operatore particolare ` l’operatore di assegnazione, =, che consente di salvare il valore (oggetto) e 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 conterr` ora l’oggetto 7, ritornato dal metodo a + con parametro 5 invocato sull’oggetto 2. Su un’area di memoria (variabile) ` possibile invocare tutti i metodi propri dell’oggetto che vi ` e e memorizzato: risultato2=risultato.next la variabile risultato2 conterr` ora l’oggetto numerico 8. a

2.3

Tipi di dato numerici

Visto che ne stavamo parlando, vediamo ora come sono esprimibili i tipi di dato numerici. Fondamentalmente ` possibile esprimere un numero in due notazioni: e • Numero intero (Classe: Fixnum) • Numero a virgola mobile (Classe: Float) vediamo qualche esempio per comprendere meglio la “divisione”: 3 -5 4.33564 -4.5 1.34E98 -1.43E12 1.2E-5 -3.6E-3 (classe: (classe: (classe: (classe: (classe: (classe: (classe: (classe: Fixnum) Fixnum) Float) Float) Float) Float) Float) Float)

ad esempio, gli oggetti della classe Float non hanno il metodo next, se hai studiato matematica capirai il perch` :) e

2.4

I commenti

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

6

# commento su una riga =begin commento su piu‘ righe =end pu` essere utile ad esempio, in questo caso o # 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 ripetuti pi` volte all’interno del programma. u Si consiglia di usare nomi simbolici e descrittivi per le costanti, per facilitare la leggibilit` del a programma. 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 un oggetto basta cercare su google ruby+Classe ad esempio ruby+String il primo risultato sar` qualcosa del tipo a 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

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 gi` visto, gets invece ` una funzione che recupera una riga dallo standard input a e e la ritorna in una variabile di tipo stringa. Perch` poi invochiamo il metodo chomp! su quell’oggetto? e Perch` gets ritorna l’input inserito COMPRENSIVO di ritorno a capo (carattere speciale \n ), e 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 ne ritornano 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 stringa definita 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 conterr` quindi “ciao bao“. a

8

2.8

Conversione tra diversi tipi di dato

Su alcuni oggetti -tipi di dato ` possibile invocare dei metodi per la conversione in altri oggetti -tipi e di 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="ccc".to_f k="ccc".to_s k="c123d4".to_i k="123".to_i k=123.to_s k=123.to_f k=1.23.to_i #=> #=> #=> #=> #=> #=> #=> #=> k k k k k k k k vale vale vale vale vale vale vale vale 0 0.0 "ccc" 0 (la stringa non e‘ convertibile) 123 "123" 123.0 1

2.9

Dati booleani

Oltre a quelli gi` visti, ovvero numeri e stringhe di caratteri, esiste un altro tipo di dato “fondaa mentale”: il tipo booleano. I dati booleani rispecchiano l’algebra di Boole, per cui il valore pu` o essere solamente true o false (vero o falso, 1 o 0); e vengono usati soprattutto negli enunciati decisionali (che vedremo in seguito). I principali metodi che ritornano un oggetto booelano, e che si trovano in gran parte delle classi esistenti, 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

ovviamente, confronto1 sar` false, confronto2 true e confronto3 false. a Nell’algebra booleana sono presenti gli operatori e, o e “negazione“; dunque anche i tipi di dato booleano dispongono di questi operatori: • and - e • or - o • ! - “negazione“ vediamo qualche esempio: e1= e2= e3= e4= e5= e6= 3<4 and 4<5 !(3<4) 3<4 or 5>7 "a"<"d" and !(3>6) !("h"<"c" and "h"<"z") ((2>=5) or !("a"!="r")) and e4 #=> #=> #=> #=> #=> #=> true false true true true false

2.10

Enunciati decisionali - IF/UNLESS

L’enunciato if, o il suo negato unless viene adoperato nel caso si debbano eseguire delle istruzioni solo 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 eseguire nel caso la condizione sia vera - deve terminare con la parola chiave end. Se per`, come in questo caso, dobbiamo eseguire una sola istruzione, possiamo compattare tutto o in un’unica riga, evitando perci` il blocco di codice: o 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) usando la parola chiave unless. Vediamolo insieme: if !(2 < 3) puts "non minore" end ` del tutto equivalente a e

10

unless (2 < 3) puts "non minore" end Come visto prima per l’if, se abbiamo una sola istruzione da eseguire, anche l’unless pu` essere o compattato su una sola riga: puts "non minore" unless (2 < 3) Se alla condizione si vuole realizzare un’alternativa, si pu` utilizzare la clausula else, che sta per o altrimenti : 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 ` anche possibile aggiungere degli “step” intermedi; ipotizziamo di voler confrontare e due 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 ` un insieme di oggetti, a livello di codice delimitati da [ e ] e ad esempio, per “creare” un oggetto di tipo array vuoto: array=[]

11

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 ` sufficiente usare <<: e array << 6 array << 5 ora array sar` [1, 2, 3, 4, 6, 5]. a In realt` << ` un “sinonimo” del metodo push invocato su un array, a e quindi array << 8 avr` lo stesso effetto di a array.push 8 Se volessimo ordinare gli elementi al suo interno array.sort! array sar` ora [1, 2, 3, 4, 5, 6, 8] a ` E possibile accedere ad uno specifico elemento di un array sempre con [], ricordandoci che ogni oggetto ha un “indice” che lo identifica, e che gli indici partono da 0 e arrivano a numero di elementi -1 in questo caso: primo_elemento=array[0] a primo elemento conterr` quindi 1 in maniera simile, nel caso volessimo sovrascriverne il valore array[0]=-1 Un’altro oggetto particolare, simile agli array, ` l’oggetto Range, (intervallo) che si definisce in e questo 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 ` possibile una cosa simile anche con caratteri: e array=("a".."d").to_a array sar` quindi [’a’, ’b’, ’c’, ’d’] a Un array pu` contenere al suo interno qualsiasi tipo di oggetto (quindi anche un array) o 12

2.12
2.12.1

Cicli di istruzioni
while

Il ciclo while serve a ripetere delle istruzioni (blocco di codice) finch` una condizione ` vera e e 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 pi` efficace per ottenere il risultato di un ciclo for: u tutti gli oggetti di tipo array o range possiedono il metodo each. Il metodo each ` un metodo di tipo particolare, esso infatti non richiede parametri in ingresso, e 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, ` il metodo times dei numeri interi: e 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

o meglio ancora array.each {|n| puts n } ma il significato ` sempre lo stesso. e Personalmente trovo sia pi` leggibile delimitare il blocco con do e end. u

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 fase di concatenazione [’a’, ’b’, ’c’].join(", ") [’a’, ’b’, ’c’].join("--") #=> "a, b, c" #=> "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; la seconda ordina in-place ar1=[3, 5, 1] ar2=ar1.sort ar1.sort!

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

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

ar1=[1, 2, 3] ar1.include? 2 ar1.include? 8

#=> true #=> 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 pu` considerare come l’inverso di join applicato ad un array, infatti divide la o stringa in elementi di un array "a--b--c".split("--") #=> ["a", "b", "c"]

• [] - ` una funzione particolare che permette di accedere a determinati caratteri della stringa. e Se invocata con un numero ritorna il byte relativo, mentre se invocata con un “range” ritorna la serie di caratteri relativa: a = "hello there" a[1] #=> a[1].chr #=> a[1,3] #=> a[2,3] #=> a[1..3] #=> a[-3,2] #=> a[-4..-2] #=>

101 "e" "ell" "llo" "ell" "er" "her"

(byte in posizione 1) (byte in posizione 1 convertito in carattere) (3 caratteri partendo dalla posizione 1 compresa) (3 caratteri partendo dalla posizione 2 compresa) (caratteri dalla posizione 1 alla posizione 3) (2 caratteri dalla posizione 3 partendo da destra) (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" "ciao"<=>"ciao" "ciao"<=>"bao" #=> -1 #=> 0 #=> 1

• + - l’abbiamo gi` visto, ` l’operatore di concatenazione a e "ci"+"ao" #=> "ciao"

• * - ripete la stringa per n volte saluto="ciao " * 3 #=> "ciao ciao ciao "

15

• capitalize e capitalize! - fanno diventare maiuscola la prima lettera, la prima crea un nuovo oggetto, la seconda modifica l’oggetto esistente a = "ciao" b=a.capitalize a.capitalize!

# (b vale "Ciao", a vale ancora "ciao") # (a vale "Ciao")

• chomp e chomp! - rimuove carattere di fine linea, la prima crea un nuovo oggetto, la secondo modifica 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 crea un nuovo oggetto, la secondo modifica l’oggetto esistente a = "CIAO" b=a.downcase a.downcase!

# (b vale "ciao", a vale ancora "CIAO") # (a vale "ciao")

• empty? - ritorna true se la stringa ` vuota e "".empty? #=> true "c".empty? #=> false • include? - ritorna true se la stringa include una sequenza di caratteri "ciao".include? "ao" "ciao".include? "bo" #=> true #=> 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 nuovo oggetto, la secondo modifica l’oggetto esistente "stefano".reverse #=> "onafets"

• upcase e upcase! - trasformano tutti i caratteri minuscoli in maiuscoli, la prima crea un nuovo oggetto, la secondo modifica l’oggetto esistente a = "ciao" b=a.upcase a.upcase!

# (b vale "CIAO", a vale ancora "ciao") # (a vale "CIAO")

altri metodi consultabili su http://www.ruby-doc.org/core/classes/String.html 16

2.15

Definizione di metodi

Abbiamo visto che le strutture iterative sono molto utili per evitare di dover scrivere codice ripetitivo, un altro modo per evitare di scrivere codice ripetitivo ` definire dei propri metodi (o e funzioni). partiamo per esempi: ipotizziamo il seguente programma: nome="Mirko" puts "Ciao, #{nome}!" nome="Stefano" puts "Ciao, #{nome}!" vediamo chiaramente che ` presente una parte ripetitiva. e Possiamo semplificarci la vita creando il metodo saluta, che accetta come parametro in entrata un oggetto da stampare con puts: def saluta(nome) puts "Ciao, #{nome}!" end saluta(’Stefano’) saluta ’Mirko’ Partiamo con l’analisi: un metodo ` un blocco di codice, che comincia con e 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 ` usata la variabile nome, che ` appunto e e quella definita in ingresso. Ora, come possiamo ben vedere, quello che abbiamo scritto ` un metodo che non restituisce alcun e valore in uscita. Come fare quindi, se vogliamo far uscire qualcosa da quel metodo? Vediamo questo metodo un po’ pi` complesso che, dato un array di numeri interi torna in uscita u la somma di tutti i valori presenti: def somma_valori(array) somma=0 array.each do |elemento| somma=somma+elemento end return somma 17

end somma=somma_valori([1, 2, 3, 4]) puts somma notiamo subito prima dell’end l’istruzione return somma, questa ` l’istruzione che serve per e ritornare un valore al programma chiamante. A volte l’istruzione return pu` anche essere omessa, in quanto, se tale, ruby ritorner` per default o a il valore dell’ultima assegnazione o chiamata a funzione. Ad esempio, molto banalmente def banale a=1 end puts banale stamper` a video 1 a 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 dire a ruby che il valore da tenere nel buffer da ritornare ` il valore di somma, altrimenti avrebbe e ritornato l’array di partenza, perch` l’ultima istruzione sarebbe stata array.each (che lavora e quindi sull’array di partenza) [in questo caso il blocco viene visto come istruzione unica!!!] Ma allora, se ` possibile farlo implicitamente, perch` usare l’istruzione return? e e Perch` il return forza l’uscita dal metodo. Ad esempio: e def prova123(numero) if numero < 10 return "si" end "no" end puts prova123(12) puts prova123(9) => ’no’ => ’si’

18

omettendo il return invece avrebbe scritto ’no’ anche per il 9, perch` l’esecuzione del metodo e sarebbe continuata, ottenendo cos` come ultimo valore ’no’ in ogni caso. ı A un metodo ` possibile passare pi` di un parametro in ingresso, ad esempio: e u 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 pi` di un parametro in ingresso pu` ritornare un solo u o oggetto. Tuttavia, nel caso sentissimo la necessit` di tornare pi` di un oggetto in uscita possiamo a u applicare 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
2.16.1

Semplici operazioni con file di testo
lettura di un file

In fase di lettura tutto il file viene caricato come stringa unica, sta poi all’utente recuperare le varie righe. Il separatore di riga ` sempre ritorno a capo, carattere speciale “\n“, quindi basta uno split, o, e pi` semplicemente, each line. u 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

2.16.2

scrittura di un file

La scrittura di un file di testo ` leggermente pi` complicata, in quanto richiede un blocco di codice: e u 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.html http://ruby-doc.org/core/classes/Dir.html

20

2.17

Altri oggetti utili

Vediamo ora altri oggetti che possono essere utili: come introduzione ` necessario sapere che per avere una nuova istanza di un oggetto bisogna e chiamare la classe stessa con il metodo new. Finora non ` stato visto perch` ruby introduce delle semplificazioni nei tipi di dato nativi, e e ad esempio stringa="" array=[] ` equivalente a e 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 pu` usare mktime: o puts Time.mktime(2000, 1, 1) puts Time.mktime(1986, 11, 21, 18, 30) #=> Sat Jan 01 00:00:00 CET 2000 #=> Fri Nov 21 18:30:00 CET 1986

ovviamente un oggetto Time ha tutta una serie di metodi di utilit`; vediamone brevemente alcuni a con un esempio: adesso=Time.new giorno=adesso.day mese=adesso.month anno=adesso.year #=> #=> #=> #=> Tue Sep 09 11:54:45 CEST 2008 9 9 2008

Come ogni classe, l’elenco completo dei metodi ` disponibile a e http://ruby-doc.org/core/classes/Time.html

21

2.17.2

La classe ’Hash’

` E disponibile anche qui, come array, string, ... un costrutture semplificato, hash=Hash.new ` infatti equivalente a e hash={} Un hash ` molto simile ad un’array, con la differenza che gli indici possono essere degli oggetti. e 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 ` molto simile ad un array: e http://ruby-doc.org/core/classes/Hash.html

22

Capitolo 3

Programmazione orientata agli oggetti
3.1 Definizione di classe e oggetto

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

3.2
3.2.1

Creazione di classi personalizzate
costruzione di una classe

Vediamo subito come ` implementata in Ruby la programmazione orientata agli oggetti. Scriviamo e un semplice esempio supponendo di dover programmare un videogame di guerra dove tra le altre cose sono presenti dei carri armati e dei camion per il trasporto dei soldati. Rappresentiamo questi oggetti in Ruby: class CarroArmato def initialize puts "Sono un nuovo carro armato" end end

23

class Camion def initialize puts "Sono un nuovo camion" end end

ca = CarroArmato.new c = Camion.new

#=> Sono un nuovo carro armato #=> Sono un nuovo camion

abbiamo quindi capito che la definizione di una classe inizia con la parola chiave class e termina con end; initialize ` il metodo che viene invocato in fase di creazione di un oggetto (viene e chiamato 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 voler definire alcuni dati caratteristici dell’oggetto (carroarmato carburante e colpi; camion 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 metodi della 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

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 gi` visto) a 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 volessimo accederci in scrittura ` necessario definire un metodo; e rivediamo sempre la classe Camion, con la possibilit` di modificare il numero di posti a disposizione: a 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 ` nient’altro che un semplicissimo metodo con un parametro e in ingresso. Riproviamo quindi ad eseguire: 25

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 scrittura 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 automaticamente 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 ` proprio la “parola” attr accessor che permette di definire automaticamente questi due metodi e per ogni variabile specificata. Da notare che per ridurre ancora di pi` il codice scritto, invece di u 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, e un’altra viariabile chiamata sola scrittura fosse accessibile in sola scrittura? Anche in questo caso 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

corrisponde esattamente a scrivere attr_reader :variabile attr_writer :variabile (ovviamente il primo caso ` molto pi` pratico) e u 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 visibilit` delle a stesse: 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

ereditariet` a

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

27

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 le propriet`: a 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 pu` semplificare di molto o la vita... Ipotizziamo di avere 100 classi, e tutte queste classi hanno un metodo uguale. Dovendo modificare la logica di quel metodo, avremo bisogno di andare a modificare ognuna delle 100 classi precedentemente definite. Utilizzando l’ereditariet` invece, la modifica necessaria si riduce ad una a sola classe.

3.3

Modifica di classi esistenti

A volte, una cosa utile pu` essere modificare una classe esistente. o 28

Supponiamo di avere un array di numeri, di cui calcolare la somma, oppure la media. Certo con un each si pu` fare tutto, ma se il numero di array aumenta bisognerebbe scrivere un o each per ogni array... ci farebbe molto comodo se per ogni oggetto array esistessero i metodi sum e avg... in ruby per` o di default non esistono. Possiamo per` crearli noi andando a modificare al volo la classe Array! Basta semplicemente: o 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 media=[1, 2, 3, 4, 5].avg #=> 15 #=> 3

3.4

Gestione degli errori - Eccezioni

Un programma in esecuzione pu` passare attraverso vari problemi inaspettati. Un file che vorrebbe o leggere potrebbe non esistere; il disco potrebbe essere pieno quando desidera salvare dei dati; l’utente potrebbe fornirgli dei dati inaccettabili. Un programma robusto gestir` tali situazioni sensibilmente e in maniera elegante. a In ruby, come in molti linguaggi moderni, possiamo gestire le eccezioni per blocchi di codice in modo compartimentato, ottenendo un risultato sorprendentemente efficiente ma senza caricare in modo eccessivo il programmatore o chiunque tenti di leggere il codice in seguito. Il blocco di codice segnalato con begin viene eseguito finch` non c’` un’eccezione, che causa il trasferimento e e del controllo ad un blocco di gestione dell’eccezione, che viene segnalato da rescue. Se non c’` un e eccezione, il codice rescue non viene usato. Il metodo seguente ad esempio restituisce la prima linea di un file di testo, o nil se c’` un eccezione: e def prima_linea(filename) begin file = open("un_file") info = file.gets file.close 29

info rescue nil end end ` E necessario sapere che anche un’eccezione di per se ` un oggetto come un altro (discendente di e Exception), ed essendo tale ` perci` possibile distinguere il tipo di errore sollevato, e agire di e o conseguenza. Potremo quindi voler scrivere begin ...codice... rescue TipoDiEccezione1 ...azione1... rescue TipoDiEccezione2 ...azione2... end ed ` anche possibile recuperare e stampare a video in maniera pi` gradevole il tipo di errore: e u 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 ` questa: e • Exception – NoMemoryError – ScriptError ∗ LoadError ∗ NotImplementedError ∗ SyntaxError – SignalException ∗ Interrupt 30

– StandardError ∗ ArgumentError ∗ IOError · EOFError ∗ IndexError ∗ LocalJumpError ∗ NameError · NoMethodError ∗ RangeError · FloatDomainError ∗ RegexpError ∗ RuntimeError ∗ SecurityError ∗ SystemCallError ∗ SystemStackError ∗ ThreadError ∗ TypeError ∗ ZeroDivisionError – SystemExit

31

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 pi` usate) sono gi` incluse nell’installazione standard di ruby, mentre per altre u a ` necessaria l’installazione manuale. Per questo ci viene in aiuto il comando gem. e 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

network.include?(IPAddr.new(’192.168.0.23’)) network.include?(IPAddr.new(’192.168.1.1’)) network.include?(IPAddr.new(’192.168.10.1’)) network.include?(IPAddr.new(’172.16.100.2’))

#=> #=> #=> #=>

true true false false

4.3

YAML

YAML1 ` un formato che permette di salvare come stringhe/file di testo degli oggetti ruby, insome 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) puts(read_array == test_array ) #=> true #=> true

4.4

Inviare mail via SMTP

Utilizzando la libreria net/smtp ` molto facile inviare una e-mail: e require ’net/smtp’ user_from = "stefano@gnustile.local" user_to = "utente@dominio.com" email_body = "From: stefano@gnustile.local\n" email_body = email_body + "Subject: Ciao\n\nEmail da Ruby.\n\n"
1 http://en.wikipedia.org/wiki/YAML

33

# 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’, ’tizio@tizio.com’), (’caio’, ’caio@caio.net’), (’sempronio’, ’sempro@sempry.it’) )" ) 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("sempronio@sempry.it") con.query("UPDATE prova SET email = ’#{email}’ WHERE name = ’sempronio’") numero_righe_inserite=con.affected_rows # => 1

34

# 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 ` molto pi` facile da manipolare che non MySQL: e u require ’sqlite3’ db = SQLite3::Database.new("book.db") db.execute("SELECT * FROM libro") do |riga| puts "#{riga.titolo} - #{riga.autore}" end

35

# 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 ` un DBMS molto leggero e interamente scritto in Ruby. I dati vengono salvati su e file di testo e possono essere eventualmente modificati a mano, ` possibile usarlo sia in modalit` e a embedded sia in modalit` client/server. Pu` essere visto come una via di mezzo tra i file di testo a o semplice 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 per` volessimo utilizzare un’altra o directory 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 ogni colonna. I tipi disponibili sono: String, Integer, Float, Boolean, Time, Date, DateTime, Memo, Blob, e YAML. Per ogni campo ` possibile specificare anche alcune propriet` come il valore di e a default, l’obbligatoriet` del campo, l’indicizzazione e riferimenti ad altri record in altre tabella. a 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

book.insert("Reduce","Giovanni Lindo Ferretti",2006) #=> 1 book.insert("Il bosco degli urogalli","Mario Rigoni Stern",1962) #=> 2 (il numero ritornato ` il valore della chiave primaria) e in alternativa ` possibile usare dei blocchi di codice: e 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 senza nessun parametro:

ris = book.select #=> [#<struct #<Class:0xb7a98f14> recno=1, titolo="Reduce", autore="Giovanni Lindo Ferretti", ann ris ` un oggetto di tipo KBResultSet. e 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 Per meglio definire la ricerca ` possibile usare dei blocchi di codice: e 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 esiste gi`, e vogliamo soltanto un riferimento che punta ad essa: a 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

4.9

File XML

Vedremo ora come utilizzare due diverse librerie per trattare file XML, capiremo come leggerli e come costruirli. Le librerie si chiamano xmlsimple e rexml. La prima permette di convertire un file xml in un hash 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 sar` un semplice oggetto di tipo Hash. a 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 sar` una stringa contenente il nostro codice xml, che in questo caso sar`: a a <opt> <root dir1="Prova 123" dir2="Prova 124"> <k ciao="bao1" /> <k ciao="bao2" />
2 DOM:

Document Object Model

38

</root> </opt>

4.9.2

rexml

Questa libreria offre un trattamento pi` avanzato rispetto a xmlsimple. u 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

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 ` gestito dalla libreria standard socket che ` strutturata e e nel seguente modo: alla base c’` BasicSocket classe astratta, sottoclasse di IO, che contiene e alcuni 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 ` la classe e che implementa le socket che usano il protocollo di trasporto IP e ha due sottoclassi: TCPSocket e UDPSocket che rispettivamente trattano connessioni da, e verso, socket TCP e UDP. La classe Socket 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 ` possibile e usare i metodi di IO sui socket cos` come accade ad esempio per i file. ı

40

socket UDP
Iniziamo con un semplice esempio client-server, in cui il server invier` al client che si connetter` a a ora 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 invia il 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 mettiamo in 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

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 propriet` del protocollo, server e client TCP sono pi` semplici di quelli a u UDP. Un piccolo server Web I server web si basano sul protocollo TCP; vediamo ora come scrivere in pochissime riche di codice un web-server minimale che ad ogni richiesta risponde con data e ora corrente; ovviamente la risposta dovr` essere conforme alle specifiche del protocollo HTTP: a 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

Un server Web un po’ pi` complesso u 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

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 pi` complesso u Perch` complicarsi la vita a scrivere ancora pi` codice? I web server di prima erano a scopo e u puramente didattico, ma quando vogliamo qualcosa di pi` perch` non appoggiarsi a prodotti u e 44

stabili e testati? Vediamo ora come usare webrick, un web server piuttosto complesso, nei nostri programmi ruby. In ogni caso questo listato ` solo a titolo di esempio, affronteremo la programmazione web nel e dettaglio 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.html http://microjet.ath.cx/WebWiki/WEBrick.html Volendo potremo anche usare Apache e mod ruby, ma lo vedremo meglio pi` avanti... u

45

Parte II

Programmazione web di base

46

Capitolo 5

CGI
5.1 Introduzione

**** i metodi http, e il passaggio di parametri **** http://openskill.info/infobox.php?ID=357 http://it.wikipedia.org/wiki/HTTP

5.2

CGI in ruby

L’approccio tradizionale alla programmazione Web ` sicuramente costituito dai CGI. Un programe ma CGI sostanzialmente ` un programma che viene eseguito dal webserver, e l’output viene inviato e al 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 del nostro web server (e ovviamente deve essere reso eseguibile): #!/usr/bin/env ruby # un programma CGI deve sempre dire quale ` il formato e # 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

puts puts puts puts puts

"</head>" "<body>" "<h2>ciao mondo</h2>" "</body>" "</html>"

Andiamo quindi a http://localhost/cgi-bin/ciao.rb per vedere il risultato. L’input di programmi cgi non ` per` come il classico standard-input... e o i dati in ingresso vengono dati formattati dal browser in modo particolare; per questo per leggerli ci 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 ` un parametro in ingresso con il metodo HTTP GET, e ma anche se fosse stato inviato con POST la sua lettura sarebbe stata uguale.

5.3

CGI + ERB

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

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 ` un commento %> e </ul> </body> </html> Ecco, cos` ` decisamente molto pi` leggibile :-) ıe u Come possiamo vedere, in un template erb il codice ruby ` delimitato da <% e %>. Un po’ come e PHP e ASP, insomma. 49

5.4

mod ruby

Si pu` dire che ormai, in applicazioni web di una certa complessit`, CGI ` veramente poco o a e performate, perch` ad ogni richiesta il web server deve eseguire tutti il programma CGI. e Per questo ci vengono in aiuto alcuni componenti direttamente integrati nei web server, nel caso del 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 decisamente migliori.

50

Parte III

Ruby on Rails

51

**** DA FARE ****

52

Appendice A

Esercizi e soluzioni
A.1
A.1.1

Semplici operazioni con stringhe
Esercizio 1

Chiedere all’utente una stringa, verificare se ` o meno palindroma. e #!/usr/bin/env ruby puts "Inserisci una stringa, verificher` se ` palindroma..." o e # prendo una stringa dallo standard input, elimino il caporiga e # la faccio diventare maiuscola (o minuscola) # Il passo di farla diventare maiuscola/minuscola ` fondamentale, e # altrimenti il programma non saprebbe riconoscere che la parola # ’Anna’ ` palindroma. (’Anna’ ` diverso da ’annA’) e e 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 ` palindroma" e else puts "La stringa NON ` palindroma" e end

A.2
A.2.1

Metodi
Esercizio 1

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

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} ` divisibile per #{b}" e else puts "#{a} NON ` divisibile per #{b}" e end end

A.2.2

Esercizio 2

Scrivere il metodo is prime(x) che restituisce true se x ` primo, false altrimenti. Testare poi il e metodo 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

puts "Inserisci un numero, verificher` se ` primo." o e n=gets.chomp.to_i if is_prime n puts "#{n} ` un numero primo" e else puts "#{n} NON ` un numero primo (oppure non posso verificare se ` primo o meno)" e e end

A.3
A.3.1

Operazioni con Array
Esercizio 1

Scrivere un programma che chiede all’utente quanti voti ha collezionato; chiedere i vari voti e poi trovare: 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

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 ` presente, -1 in caso e contrario. #!/usr/bin/env ruby

def cerca_in_array(array, numero) # r ` il contatore della posizione in cui sono e 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 ` in posizione #{posizione}" e end

A.3.3

Esercizio 3

Con il metodo del crivello di eratostene trovare tutti i numeri primi da 1 a 100. Consiglio: vedere il crivello come un array in cui l’indice di un elemento corrisponde al numero, il valore in posizione k vale false se il numero ` stato eliminato. e #!/usr/bin/env ruby

56

# 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
A.4.1

Operazioni con Hash
Esercizio 1

Dato un hash cercare la chiave relativa ad un suo elemento senza usare i metodi di ricerca interni ad Hash. Usare un metodo che restituisce la chiave della prima ricorrenza se l’elemento ` presente, e nil in caso contrario. #!/usr/bin/env ruby def find_in_hash(hash, el) # in questo caso tra || uso due variabili. Questo perch` ` definito cos` e e ı # 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

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
A.5.1

Operazioni con File
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 sar` un array) a 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 ` leggibile" e end 58

A.6
A.6.1

Operazioni con Oggetti
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 in fase di inizializzazione dell’oggetto. class Libro attr_reader :isbn, :autore, :titolo attr_reader :editore, :anno_pubblicazione, :prezzo # scritte due righe solo per semplicit` di lettura da parte dell’utente. a # 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 costruisca un conto bancario, versi in esso 1000 euro, prelevi da esso 500 euro, prelevi altri 400 euro e stampi a video il saldo rimanente. (La progettazione della classe ` libera) e class NegativeBalanceError < ArgumentError end class BankAccount attr_reader :balance def initialize @balance = 0 end def deposit (amount) @balance = @balance + amount end

59

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, per conoscerne 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

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

You're Reading a Free Preview

Download
scribd
/*********** DO NOT ALTER ANYTHING BELOW THIS LINE ! ************/ var s_code=s.t();if(s_code)document.write(s_code)//-->