Un assaggio di Ruby e Rails

# .oOoOo Un assaggio di Ruby & Rails v0.0.2 .oOoOo

=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
#


corso javascript