Un assaggio di Ruby e Rails
=begin
° Introduzione
Ci sono tanti linguaggi di programmazione, il C per il "basso livello",
il Php per il web, il Visual Basic per le interfacce veloci, Java per le
applicazioni multipiattaforma, Bash per lo scripting di sistema, il Perl
per gli exploit..
Tutti simili e ognuno con le proprie peculiarità. Il C rimarrà ancora per
molto lo standard per il basso livello, ma ad alto livello ci sono state
innovazioni che non tutti i linguaggi di programmazione hanno considerato.
Questo documento non ha la pretesa di essere esaustivo sull'argomento
Ruby / Ruby on Rails, ma vuole essere solo uno "stuzzichino" per stimolare
la curiosità e l'interesse di neofiti e non verso questo linguaggio.
Ruby non discrimina
Da qualsiasi linguaggio arrivi a ruby, sarai (quasi) sempre in grado a scrivere
qualcosa di funzionante; col tempo potrai imparare le tantissime scorciatoie.
---------------------------------------------------------------------------
Ruby
Ruby è un linguaggio ooo, opensource, dinamico, multipiattaforma.
..e soggettivamente bello, conciso, espressivo e naturale.
Non c'è altro da aggiungere, il codice dice tutto
=end
# Ruby/C-style
i=0
while (i<5)
printf("%s %d\n","ciao", i)
i+=1
end
# Ruby/geek-style
(0..4).each{|n| p "ciao #{n}"}
# Un po' sintassi e convenzioni
# commento su una riga
=begin
commento su più righe
=end
# punto e virgola solo quando serve
def stampa('3 istruzioni su una riga'); print qualcosa; end
stampa('Finalmente!')
stampa('Basta abusare dei punto e virgola!')
# stampa delle variabili senza uscire dalla stringa
'stringa statica ' + RUBY_PLATFORM
"stringa interpretata #{RUBY_PLATFORM}"
variabile_locale
@variabile_di_istanza
@@variabile_della_classe
$variabile_globale
Costante
ClasseMoltoLunga
Classe.metodo
Modulo::metodo
# hash
dizionario_con_blocco= {:simbolo => 'valore', :simbolo2 => 'valore2'}
dizionario_con_blocco.each_key {|elemento| fai_qualcosa_con elemento}
dizionario_con_blocco[:simbolo] == 'valore'
:test.to_s "test"
# espressioni regolari già nel linguaggio
('Trova la posizione d1 unnumero nella regexp' =~ /[0-9]/) 20
# operatori specifici per i range
((1..10) === 10) == true
((1...10) = 10) false
# dinamico nel modo giusto
"ciao"+4 # errore
"ciao"+4.to_s # => ciao4
# classi
class Uccello
def vola
puts 'sto volando'
end
def dormi
"zzz.." # return non è indispensabile
end
end
# Ereditarietà
# sovrascrivo i metodi della classe di partenza
class Pinguino < Uccello
def vola
puts 'non so volare'
end
# tutti metodi che non esistono chiameranno questo!
def method_missing(*args)
puts 'non so ancora farlo'
end
end
Uccello.new.vola # => sto volando
tux = Pinguino.new
tux.vola # => non so volare
tux.nuota # => non so ancora farlo
puts tux.dormi # => zzz..
titti = Uccello.new
def titti.parla(frase)
puts frase.gsub(/r/,'l') # espressione regolare
end
titti.parla('Mi è sembrato di vedere un gatto')
# => Mi è semblato di vedele un gatto
class Gatto
# implementa per voi i metodi nome e nome= per leggere/scrivere
# la variabile
attr_writer :nome
@@quanti=0
# Overloading e parametri predefiniti
def initialize(nome='boh')
@nome = nome
@@quanti+=1
end
def parla(frase)
puts frase
end
# questo metodo è della classe e non dell'istanza
def self.quanti?
puts @@quanti
end
end
# anche le parentesi volendo si possono omettere
silvestro = Gatto.new 'Silvestro'
randagio = Gatto.new # => <Gatto:0xb7ca31d8 @nome="boh">
randagio.nome = 'micio'
randagio.nome # => micio
Gatto.quanti? # => 2
# Duck Typing
# nessuna interfaccia, basta che le classi/istanze implementino il metodo
def cantiamo_insieme(*args)
args.each{ |cantante| puts "sono un #{cantante.class} e canto: ";
cantante.parla('oh oh che bello cantareee..') }
end
# Uccello (parlante) e Gatto possono cantare insieme!
cantiamo_insieme(titti, silvestro)
# Moduli
module RedBullMetteLeAli
def vola
puts 'tutti possono volare basta volerlo!'
puts "anche #{@nome}" if @nome
end
end
# Le classi sono dinamiche e posso essere riprese
# includendo più moduli si può creare una ereditarietà multipla
class Gatto
include RedBullMetteLeAli
end
silvestro.vola
# => tutti possono volare basta volerlo!
# => anche Silvestro
# Ridefinizione delle classi base
class String
def censura
self.gsub(/aeiou/,'*')
end
# e degli operatori di base!
def *(stringa)
# operatore di concatenazione astratto
self << " x " << stringa << " ahahh"
end
end
puts "ciao" * "bau" # => ciao x bau ahahh
# Tutto è un'oggetto anche i numeri
3.times do
puts "ciao che figo!".gsub(/a|e|i|o|u/,'*')
end
# Separatore numerico _ e conversione automatica dei numeri
i = 500_000_000
while i < 10_000_000_000
puts "#{i} #{i.class}"; i = i*2
end
# output
# 500000000 Fixnum
# 1000000000 Fixnum
# 2000000000 Bignum
# 4000000000 Bignum
# 8000000000 Bignum
# Ruby come calcolatrice
puts Math::cos(0).to_i # 1
include Math
puts (cos(0) + (PI + 7.5) / 2)
# Ruby come scripting
Dir.open('.').each {|file| fai_qualcosa_con_quel file}
# idem
Dir.open('.').each do |file|
fai_qualcosa_con_quel file
end
# Ruby e l'interfaccia grafica Gtk
require 'gtk2'
Gtk.init # per inizializzare le librerie
w = Gtk::Window.new
b = Gtk::Button.new 'cliccami'
# assegno un'azione all'evento <cliccato> del pulsante b
b.signal_connect('clicked'){ puts 'ciao'; w.title='ciao'}
# impacchetto il pulsante b nella finestra w
w.add(b)
w.show_all # mostro la finestra w e tutti gli oggetti che contiene
Gtk.main # Loop, entra in esecuzione l'applicazione grafica
=begin
Ruby nel web2.0 con Rails
Ruby on Rails è un framework MVC (Model-View-Controller) che permette
di accedere al db come se si trattasse di oggetti. Permette quindi di
avere classi persistenti, uno sviluppo del db incrementale e tante altre cose.
ATTENZIONE !!
Rails è "magico", ci sono tante convenzioni, quasi tutte sovrascrivibili, per esempio
le tabelle sono chiamate al plurale, mentre i modelli e i controller al singolare.
Iniziamo con un semplice progetto
da cmd o bash:
=end
rails rtest # crea la struttura di base nella cartella rtest
cd rtest # Sarà sempre la cartella di riferimento
app
controllers # manipolano i dati e li richiamano dal database
models # descrivono i tipi di dato validi per il modello
views # sono i template che mostrano le informazioni
config
database.yml # specifica l'adattatore e il nome del db utilizzato
routes.rb # qui vendono mappati i controller nella URL
db
migrate # dentro la dir ci sono tutte le migrazioni incrementali
schema.rb # la struttura completa del db all'ultima versione
# Configurazione dell'ambiente
# 1. configurare i paramentri del database in config/database.yml
development:
adapter: sqlite3 # o qualsiasi altro db supportato mysql/firebird/..
database: db/rtest_dev.sqlite # dove volete creare il db o il suo nome
# Creaiamo il modello 'cartoon', inizialmente è solo un file vuoto, ma ci permetterà di
# interfacciarci con la tabella del db
script/generate model cartoon
# Viene anche creato 'db/migrate/001_create_cartoons.rb', possiamo aprirlo e implementare
# up e down, cioé i metodi che verranno eseguiti quando passeremo ad una versione successiva
# o precedente del db
# -- db/migrate/001_create_cartoons.rb --
class CreateCartoons < ActiveRecord::Migration
def self.up
create_table (:cartoons) do |t|
t.column :name, :string
t.column :nationality_id, :integer
end
end
def self.down
drop_table (:cartoons)
end
end
# -- EOF --
# Si può utilizzare Ruby on Rails anche senza browser in modalità interattiva,
# così da avere molte più informazioni di debug
# dal terminale digitate
script/console
x = Cartoon.new # Crea una istanza della classe Cartoon
x.name = 'pluto'
x.save # l'istanza viene salvata sul db
Cartoon.new(:name => 'paperino').save # tutto in una sola riga
Cartoon.find(:first) # restituisce pluto
Cartoon.find :all # restituisce array con pluto e paperino
exit
# Se il record esiste, 'save', eseguirà CREATE altrimenti UPDATE
script/generate model nationality
# analocamente aggiungete
[..]
t.column :nation, :string
[..]
rake db:migrate # rake --tasks per vedere tutto quello che fare
# Porta il db all'ultima versione del database, cioè esegue incrementalmente tutte le 'up'
# Se si è già all'ultima non c'é nessun output
# rake db:migrate VERSION=0 fà rollback ed esegue i metodi 'down' fino alla versione 0
# -- file app/models/nationality.rb
class Cartoon < ActiveRecord::Base
belongs_to :nationality
end
# -- file app/models/nationality.rb
class Nationality < ActiveRecord::Base
has_many :cartoons
end
lupin = Cartoon.new(:name => 'Lupin')
lupin.nationality = Nationality.new(:nation => 'Giappone')
lupin.save
lupin3 = Cartoon.find :first
lupin3.nationality.nation # Giappone
lupin3.nationality_id # 4 gestito in automatico
# Finora tutto quello che è stato realizzato era indipendente dal web
# adesso bisogna interfacciare i modelli creati con i controllori (controller)
# e viste (view) opportune.
# Un metodo molto comodo per essere subito operativi sono gli scaffold; possono essere
# utilizzati a runtime oppure si possono utilizzare come codice di partenza per fare
# input/output con il db.
# ATTENZIONE gli scaffold sono veloci da utilizzare, ma non sono la risposta a tutto,
# ci sono degli scaffold di terze parti in stile web 2.0 che supportano l'ordinamento delle
# colonne e le chiavi esterne, ma se vogliamo fare qualcosa di più di un semplice
# "modulo d'inserimento" bisogna programmare e sporcarci le mani :-)
# -- file app/controllers/nationality_controller.rb
class NationalityController < ApplicationController
scaffold :nationality # generato a runtime metodi edit/add/list che funzionano subito
end
# Genera il codice dello scaffold una volta, quindi se si aggiungerà una colonna bisognerà
# modificarlo; lo si può utilizzare come base di partenza per aggiungere il supporto alla
# chiave esterna (nel nostro caso).
script/generate scaffold cartoon
# Gestiamo la chiave esterna
# -- file app/controllers/cartoons_controller.rb
#[...]
def show
@cartoon = Cartoon.find(params[:id])
# Serve per poter visualizzare la nazionalità associata nella vista
@nationality = Nationality.find(@cartoon.nationality_id) # aggiunto
end
#[...]
# --
# Adesso bisogna che la vista sappia dove visualizzare questo nuovo oggetto.
# La vista, in questo caso, è 'show.rhtml' perché è associata al metodo show del controllore.
# I file .rhtml, sono simili ai file .php/.asp, ma dovrebbero solo contenere
# html e variabili da stampare racchiuse in <%= @var %>, se tuttavia dobbiamo aggiungere della
# logica, possiamo sempre includere il codice in <% for elem in @elementi %>
# ATTENZIONE al posto di </>%= dovete mettere <%=
# -- file app/views/cartoons/show.rhtml
#[...]
<p>Nazione: # aggiunto
</>%= @nationality.nation %> # aggiunto
</p> # aggiunto
#[...]
# --
# Riguardo la parte d'inserimento (add) e di modifica (edit), dobbiamo aggiungere il combobox o lista
# a scomparsa per la selezione dei paesi presenti. Visto che il form per le suddette operazioni risulterebbe
# molto simile, Rails permette di condividere i campi a livello di vista,
# tramite <%= render :partial => 'form' %>
# -- file app/views/cartoons/_form.rhtml
#[...]
<p><label for="cartoon_nationality">Nazione</label></br>
</>%= select("cartoon", "nationality_id", Nationality.find_all.collect {|p| [ p.nation, p.id ] }) %></p>
#[...]
# Link utili:
# - http://www.ruby-lang.org Ruby
# - http://www.rubyonrails.org Video/manuali/api
# - http://ruby-gnome2.sourceforge.jp/it/ Ruby + Gnome
#
# - http://www.rubyquiz.com Spunti e tecniche per usare Ruby come si deve
# - http://www.railsweenie.com Forum
# - http://www.railscasts.org Tanti video introduttivi su rails da scaricare
#
# In italiano:
# - http://ruby-it.org/ Comunità italiana di Ruby
# - http://www.extendi.it/ruby-on-rails Blog su Rails
# - http://www.therubymine.com Diversi articoli su Ruby e Rails
#