irb
irb1.8
libdbi-ruby1.8
liberb-ruby
libgems-ruby1.8
libopenssl-ruby1.8
libpgsql-ruby1.8
libreadline-ruby1.8
libredcloth-ruby1.8
rails
rake
rdoc
rdoc1.8
rubygems
===== Hello world =====
mkdir rails; cd rails
rails spese
cd spese
./script/server
Il risultato può essere controllato col browser all'indirizzo **''%%http://localhost:3000/%%''**.
Per il collegamento al database editare il file **''config/database.yml''**; si devono indicare tre database distinti: **development**, **test** e **production**.
./script/generate controller hello index
Il risultato si vede all'indirizzo **''%%http://localhost:3000/hello%%''**. La logica del controller è nel file ''**app/controllers/hello_controller.rb**''; è lui che costruisce i dati per la **view**. La **view** associata è costituita dal file **''app/views/hello/index.rhtml''**.
Se si definisce un **metodo pubblico** nel controller, questo diventa una **action**, ad esempio dopo il metodo //index// creiamo il metodo //world//:
class HelloController < ApplicationController
def index
end
def world
@greeting = "hello world!"
end
end
Visitando l'URL **''%%http://localhost:3000/hello/world%%''** un messaggio d'errore informa che manca il template, cioè la view. Basta creare il file **''app/views/hello/world.rhtml''** con questo contenuto:
<%= @greeting %>
===== Tabelle di database, modelli e scaffold =====
Se si ha a che fare con un database, Ruby on Rails mette a disposizione delle impalcature (scaffold) preconfezionate per fare le operazioni di base (list, insert, update, delete, ecc.). Si tratta ovviamente di template (view) grezzi, ma costituiscono il punto di partenza per la programmazione perché la separazione tra logica del programma e presentazione è già predisposta.
Nell'architettura **MVC** (Modello-Vista-Controller) di Ruby on Rails un **modello** corrisponde ad una tabella del database.
In questo esempio vogliamo costruire un'impalcatura attorno alla **tabella operations** (movimenti su un conto bancario). La struttura del database si scrive nel file **''db/create.sql''**.
CREATE TABLE operations (
id SERIAL PRIMARY KEY,
operation_type_id INTEGER NOT NULL,
date_time TIMESTAMP WITHOUT TIME ZONE NOT NULL,
description CHARACTER VARYING NOT NULL,
debit NUMERIC NOT NULL,
credit NUMERIC NOT NULL
);
**Attenzione alle convenzioni!** L'impalcatura **Operation** (singolare, con lettera maiuscola) si aspetta una tabella **operations** (plurale, minuscolo) con una chiave primaria **id**. Una chiave esterna ha nome del tipo **field_id**. Questa è una caratteristica di Ruby on Rails: **//convention over configuration//**.
La tabella viene inizialmente creata nel database **conto_development**. Creiamo l'impalcatura:
./script/generate scaffold Operation date_time:datetime, description:string, operation_type_id:integer, debit:float, credit:float
In relazione all'oggetto //Operation// (tabella nel database) vengono creati una serie di elementi:
* **''models/operation.rb''**, nel modello si definiscono i controlli di validità e le relazioni dell'oggetto Expense
* **''views/operations/*.html.erb''** sono le viste principali relative alle quattro operazioni di base: **index**, **show**, **edit** e **new**
* **''controllers/operations_controller.rb''** il controller è il codice che gestisce le azioni
^ index | Mostra tutti i record: **''%%http://localhost:3000/operations/%%''** |
^ new | Per l'inserimento di un nuovo record |
^ show | Mostra i dettagli del singolo record |
^ edit | Mostra i dettagli del record e consente la modifica |
^ update | Accetta le modifiche fatte da //edit// |
^ destroy | Permette di eliminare un record |
Notare che ogni action è collegata alle altre in modo intuitivo e naturale: dal //list// si può accedere al //new//, //edit// o //destroy//; ecc.
Il modello corrisponde quindi a una tabella del database, ma per Ruby on Rails è una classe definita nel file **''app/models/operation.rb''**. Tutto quello che dal programma va al database passa per il modello, quindi se si vogliono aggiungere controlli di validità sui valori inseriti conviene metterli nel modello stesso:
class Operation < ActiveRecord::Base
belongs_to :operation_type
validates_presence_of :operation_type_id, :date_time, :description, :credit, :debit
validates_numericality_of :credit, :debit
end
===== Scaffold dinamico =====
**ATTENZIONE:** Nella **versione 2** di Ruby on Rails lo scaffolding dinamico è stato **rimosso**.
Invece di far generare uno scaffold per poi modificarlo si può chiederne uno dinamico, generato al volo. Anzitutto si crea il modello e un controller vuoto:
./script/generate model operation
./script/generate controller Operations
poi dentro il controller si dichiara che si vuole lo scaffold dinamico:
class OperationsController < ApplicationController
scaffold :operation
end
Basta questo per avere a disposizione i metodi list, new, show, edit, update e destry. Perché lo scaffold dinamico funzioni non deve esistere né la view (es. ''app/views/operations/list.rhtml'') né la definizione del metodo nel controller:
class OperationsController < ApplicationController
scaffold :operation
#def list
#end
end
===== Supporto PostgreSQL =====
Il supporto a PostgreSQL in Ruby si ottiene semplicemente (con Debian) installando il pacchetto **libpgsql-ruby1.8** (si tratta della libreria compilata, non quella //pure Ruby//).
Queste invece sono le istruzioni per installare le estensioni RubyGem per PostgreSQL, qualora non si potessero installare come pacchetto.
Per accedere a un database PostgreSQL da Ruby esistono due estensioni: **[[http://rubyforge.org/projects/ruby-dbi|postgres-pr]]** e **[[http://ruby.scripting.ca/postgres/|postgres]]**, la prima è una implementazione //pure ruby//, la seconda invece va compilata rispetto a Postgres (richiede pacchetti **-dev**), ma dovrebbe garantire prestazioni superiori. Il database abstraction layer **ruby-dbi** può usare indifferentemente entrambe. Le estensioni (le cosiddette //RubyGems//) si installano con **gem**.
Eseguento **''gem install''** come **''root''** l'elenco delle RubyGems disponibili viene scaricato da **''%%http://gems.rubyforge.org%%''** e salvato in **''/var/lib/gems/1.8/source_cache''**. La directory predefinita per l'installazione delle RubyGems è **''/var/lib/gems/1.8/cache/''**.
Si potrebbe operare come utente non privilegiato, in questo caso **''gem install''** salva l'elenco dei pacchetti disponibili in **''~/.gem/source_cache''**. La directory per installare i pacchetti si può specificare da riga di comando; ma poi come si fa a far trovare queste librerie a Ruby?
gem install postgres-pr --install-dir /home/niccolo/lib/gems/1.8
L'installazione di **postgres-pr** avviene senza problemi:
gem install postgres-pr
Invece per installare la RubyGem **postgres** si devono installare anche i seguenti pacchetti Debian di sviluppo:
ruby1.8-dev
postgresql-server-dev-8.1
e bisogna indicare a ''gem'' dove trovare gli include di Postgres per compilare il driver:
POSTGRES_INCLUDE=/usr/include/postgresql gem install postgres
===== Data Type money e PostgreSQL =====
Il tipo **money** di Postgres è deprecato, si consiglia di sostituirlo con un tipo **numeric**. Ruby on Rails addirittura non lo supporta: cercando di visualizzare un campo di tale tipo, viene visualizzato uno zero.
Non esiste un metodo semplice per convertire un money in numeric, la strada più semplice pare essere quella di fare il dump, editare il file e poi il restore.
===== Tabelle correlate =====
Due tabelle collegate da una **relazione uno a molti**: ad esempio **expenses** e **people**, dove una persona può avere molte spese. La tabella people ha la chiave primaria **id**, mentre la tabella expenses ha il campo **person_id**. Notare la gestione dei plurali - anche irregolari - di Ruby on Rails!
^ people ^
| **id** |
| name |
^ expenses ^
| **id** |
| person_id |
| amount |
Si creano i due modelli Rails:
./script/generate model person
./script/generate model expense
Il collegamento si ha semplicemente aggiungendo le calusole **has_many** e **belongs_to** ai rispettivi modelli:
class Person < ActiveRecord::Base
has_many :expenses
class Expense < ActiveRecord::Base
belongs_to :person
Fatto questo nelle viste sarà possibile fare riferimento agli attributi della tabella correlata con un'istruzione del tipo:
<%=h expense.person.name %>
Infine bisogna che l'eventuale form di inserimento e modifica presenti un menu drop-down per il riempimento del campo. Purtroppo pare che questo non si possa ottenere con gli scaffold automatici per le azioni **new** e **edit**. Per fortuna la form deve essere definita solo in: **''%%app/views/expenses/_form.rhtml%%''**:
<%= select 'expense', 'person_id',
Person.find(:all, :order => 'name').collect {|p| [p.name, p.id]},
:prompt => "Select a Person",
:selected => session[:person_id] %>
<%= text_field 'expense', 'amount' %>
===== Integrazione con Apache2 =====
Invece di usare il web server WEBrick fornito con Ruby on Rails si vuole eseguire RoR tramite il modulo FastCgi di Apache. Il vantaggio è che ogni richiesta di pagina Ruby non deve avviare ex-novo l'interprete Ruby e l'ambiente Rails. Per Apache2 si consiglia il modulo **mod_fcgid** che è un'alternativa compatibile a **mod_fastcgi** (mod_fcgid is an actively maintained GPL project designed to replace mod_fastcgi).
Si installano i pacchetti Debian Lenny:
* libapache2-mod-fcgid
* libfcgi-ruby1.8
* libfcgi0c2
**NOTA** Forse **libfcgi-ruby1.8** non serve, perché è un doppione di **libapache2-mod-fcgid**?
Per utilizzare il modulo **mod_fcgid** si deve verificare che il file **''public/.htaccess''** dell'applicazione Ruby on Rails abbia le seguenti righe:
Routing Error
no route found to match "/rails/info/properties" with {:method=>:get}
Per risolvere si deve modificare il file **''config/boot.rb''** dell'applicazione, modificando la definizione di **''root_path''**:
#root_path = Pathname.new(root_path).cleanpath(true).to_s
root_path = Pathname.new(root_path).cleanpath(true).realpath().to_s
Questo imposta la root_path dell'applicazione al suo percorso assoluto e in forma canonica. Siccome l'applicativo gira sotto FastCGI si deve effettuare il reload di Apache dopo aver modificato la configurazione di Rails.
A livello di configurazione globale di Apache si potrebbe voler mettere solo quanto segue:
find tmp/ -type d -print0 | xargs -0 chmod 0770
find tmp/ -type f -print0 | xargs -0 chmod 0660
find tmp/ -type d -print0 | xargs -0 chmod 0770
find tmp/ -type f -print0 | xargs -0 chmod 0660
chown niccolo:www-data config/database.yml
chown niccolo:www-data /database.yml
chown niccolo:www-data /database.yml